自定义QueryParser
1、对于某些Parser,在查询时,会使得性能降低,所以考虑将这些查询取消,
诸如: FuzzyQuery,wildCardQuery
2、在具体的草讯中,很有可能有一种需求:需要获取的是一组数字的范围查询,所以必须扩展原有的QueryParser才能进行查询。限制性最低的QueryParser
扩展基于数字和日期的查询
自定义过滤器
自定义一个接口
public interface FilterAccessor {自定义过滤器
public String[] values();
public String getField();
public boolean set();
}
public class MyIDFilter extends Filter {覆盖getDocIdSet方法
private FilterAccessor accessor;
public MyIDFilter(FilterAccessor accessor) {
this.accessor = accessor;
}
@Override设置DocIdSet的意义
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
// 创建一个bit 默认所有的元素都是0
/**
*【 类似数组,在查询时会来这个docIdSet中查找,如果为1表示没有过滤,如果为0表示不显示】
*/
OpenBitSet obs = new OpenBitSetDISI(reader.maxDoc());
if (accessor.set()) {
set(reader, obs);
} else {
clear(reader, obs);
}
return obs;
}
private void set(IndexReader reader, OpenBitSet obs) {
try {
// 获取id所在的doc的位置,并且将其设置为0
int[] docs = new int[1];
int[] freqs = new int[1];
for (String d : accessor.values()) {
TermDocs tds = reader
.termDocs(new Term(accessor.getField(), d));
// 会将查询出来的对象的位置存储在doc中,出现的频率存储在freqs中,返回获取的条数
int count = tds.read(docs, freqs);
if (count == 1) {
obs.set(0);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
高亮显示
注:需要的jar包
Tike是2008年才产生的一个apache的项目,主要是用于打开各种不同的文档信息
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-app</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-highlighter</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>com.janeluo</groupId>
<artifactId>ikanalyzer</artifactId>
<version>2012_u6</version>
</dependency>
1、搜索的关键字高亮(我们正在学习<spanstyle=’xxx’>lucene</span>)
2、拆分内容
/**
* 自定义高亮显示
*
* @param name
*/
@SuppressWarnings("resource")
public void searcherByHighLighter(String name) {
try {
Analyzer a = new IKAnalyzer();
FSDirectory driectory = FSDirectory.open(new File("D:/lucene/files2"));
IndexSearcher searcher = new IndexSearcher(IndexReader.open(driectory));
// 使用多个查询的匹配方式去查询
MultiFieldQueryParser parser = new MultiFieldQueryParser(
Version.LUCENE_35, new String[] { "title", "content" },a);
Query query = parser.parse(name);
TopDocs tds = searcher.search(query, 100);
for (ScoreDoc sc : tds.scoreDocs) {
Document doc = searcher.doc(sc.doc);
String title = doc.get("title");
title = lighterStr(a, query, title, "title");
System.out.println(title);
String content = new Tika().parseToString(new File(doc.get("path")));
title = lighterStr(a, query, content, "content");
System.out.println(content);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 高亮词语组装
*
* @param a
* @param query
* @param txt
* @param filename
* @return
*/
private String lighterStr(Analyzer a, Query query, String txt,
String filename) {
String str = null;
try {
QueryScorer scorer = new QueryScorer(query);
Formatter fragmenter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
Highlighter lighter = new Highlighter(fragmenter, scorer);
str = lighter.getBestFragment(a, filename, txt);
} catch (IOException e) {
e.printStackTrace();
} catch (InvalidTokenOffsetsException e) {
e.printStackTrace();
}
return str;
}
Fragmenter拆分器
按指定字符长度将文档进行分割(默认字符长度是100)
// 自定义高亮显示
Fragmenter fragmenter = new SimpleSpanFragmenter(scorer);
QueryScorer(切片评分)
对每一个切片(frag)进行评分(例如一篇文章中有很搜索的内容,那么现实的时候需要显示哪一个呢?就需要QueryScore来进行评分,选出最适合显示的一段内容)
QueryScorer scorer = new QueryScorer(query);
Encoder(编码器)
编码器,比如返回的高亮片段里面包含了特殊字符,比如< > & "等等,如果你需要进行转义,则需要指定一个编码器
Highlighter highlighter = new Highlighter(formatter, scorer);
highlighter.setTextFragmenter(fragmenter);
Encoder encoder = new SimpleHTMLEncoder();
highlighter.setEncoder(encoder);
Formatter(高亮标签)
Formatter formatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
近实时搜索
lucene通过NRTManager这个类来实现近实时搜索,所谓近实时搜索即在索引发生改变时,通过线程跟踪,在相对很短的时间反映给给用户程序的调用。
NRTManager通过管理IndexWriter对象,并将IndexWriter的一些方法(增删改)
例如:addDocument,deleteDocument等方法暴露给客户调用,它的操作全部在内存里面,所以如果你不调用IndexWriter的commit方法,通过以上的操作,用户硬盘里面的索引库是不会变化的,所以你每次更新完索引库请记得commit掉,这样才能将变化的索引一起写到硬盘中,实现索引更新后的同步用户每次获取最新索引(IndexSearcher)。
可以通过两种方式:
1、通过调用NRTManagerReopenThread对象,该线程负责实时跟踪索引内存的变化,每次变化就调用maybeReopen方法,保持最新代索引,打开一个新的IndexSearcher对象,而用户所要的IndexSearcher对象是NRTManager通过调用getSearcherManager方法获得SearcherManager对象,然后通过SearcherManager对象获取IndexSearcher对象返回个客户使用,用户使用完之后调用SearcherManager的release释放IndexSearcher对象,最后记得关闭NRTManagerReopenThread。
2、直接调用NRTManager的maybeReopen方法来获取最新的IndexSearcher对象来获取最新索引。
IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig(Version.LUCENE_35, new StandardAnalyzer(Version.LUCENE_35)));
NRTManager nrtMgr = new NRTManager(writer, new SearcherWarmer() {
@Override
public void warm(IndexSearcher s) throws IOException {
System.out.println("reopen");
}
});
// 启动NrtManager的Reopen线程
//NRTManagerReopenThread会每隔25秒去检测一下索引是否更新并判断是否需要重新打开writer
NRTManagerReopenThread reopen = new NRTManagerReopenThread(nrtMgr,5.0, 0.025);
// 设置后台启动线程
reopen.setDaemon(true);
// 取名字
reopen.setName("NrtManager Reopen Thread");
// 启动线程
reopen.start();
SearcherManagermgr = nrtMgr.getSearcherManager(true);// 允许所有的线程的更新
NRTManager
NRTManager nrtMgr = new NRTManager(writer, new SearcherWarmer() {
@Override
public void warm(IndexSearcher s) throws IOException {
System.out.println("reopen");
}
});
删除元素
nrtMgr.deleteDocuments(new Term("id","2"));
更新元素
Document doc = new Document();
doc.add(new Field("id", "11", Field.Store.YES,
Field.Index.NOT_ANALYZED_NO_NORMS));
doc.add(new Field("email", "test" + emails[0] + "@test.com",
Field.Store.YES, Field.Index.NOT_ANALYZED));
doc.add(new Field("content", contents[0], Field.Store.NO,
Field.Index.ANALYZED));
doc.add(new Field("name", names[0], Field.Store.YES,
Field.Index.NOT_ANALYZED_NO_NORMS));
// 存储数字
doc.add(new NumericField("attach", Field.Store.YES, true)
.setIntValue(attachs[0]));
// 存储日期
doc.add(new NumericField("date", Field.Store.YES, true)
.setLongValue(dates[0].getTime()));
nrtMgr.updateDocument(new Term("id", "1"), doc);
SearcherManager
SearcherManagermgr = nrtMgr.getSearcherManager(true);// 允许所有的线程的更新扩展:
IndexSearcher searcher=mgr.acquire();//获得一个searcher
mgr.maybeReopen();//判断是否需要重新打开一个searcher
finally {
try {
mgr.release(searcher);
} catch (IOException e) {
e.printStackTrace();
}
}
Luke是一个查询索引的工具,使用时必须注意:版本要与lucene完全一致,否则打不开索引信息。
Java –jar luke-xx-xx.jar打开索引文件
选择索引存储的目录,就可以使用luke查询和使用luke的索引信息,并且可以在search中根据QueryParser来查询相应的信息