lucene(补充QueryParser,Filter,高亮显示,近实时搜索)

时间:2023-01-15 18:27:21

自定义QueryParser

1、对于某些Parser,在查询时,会使得性能降低,所以考虑将这些查询取消,

      诸如:  FuzzyQuery,wildCardQuery

2、在具体的草讯中,很有可能有一种需求:需要获取的是一组数字的范围查询,所以必须扩展原有的QueryParser才能进行查询。

限制性最低的QueryParser

lucene(补充QueryParser,Filter,高亮显示,近实时搜索)

扩展基于数字和日期的查询

lucene(补充QueryParser,Filter,高亮显示,近实时搜索)

自定义过滤器

自定义一个接口

public interface FilterAccessor {
public String[] values();
public String getField();
public boolean set();
}
自定义过滤器

public class MyIDFilter extends Filter {
private FilterAccessor accessor;

public MyIDFilter(FilterAccessor accessor) {
this.accessor = accessor;
}
覆盖getDocIdSet方法

@Override
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();
}
}
设置DocIdSet的意义

lucene(补充QueryParser,Filter,高亮显示,近实时搜索)

高亮显示

注:需要的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来查询相应的信息