Lucene4:创建FastVectorHighlighter高亮查询
1. 要求
实现FastVectorHighlighter的高亮查询。Highlighter是流行和广泛使用的Lucene应用,但索引大文件时,Highlighter是相当费时的。可替代Highlighter的FastVectorHighlighter首次出现在Lucene的2.9版本,并提供更快的性能。
注意:任意需要高亮的field,都必须使用TermVector.WITH_POSITIONS_OFFSETS参数进行索引,并且需要存储。在Lucene 4.0中,可以使用FieldType来解决这个问题。参考下面的代码。
如果是在Solr中使用,在schema.xml中相关<field>中加入:stored="true" termVectors="true" termPositions="true" termOffsets="true"即可。
另注意:我的经验是小型文本最好不用FastVectorHighlighter,因为它不一定达到你的要求。比如说针对微博信息的索引,要求在查询结果页面中全文展现的,用FastVectorHighlighter就不是一个好的选择。
2. 实现代码
package com.clzhang.sample.lucene; import java.io.*; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.document.Document; import org.apache.lucene.document.Field; import org.apache.lucene.document.FieldType; import org.apache.lucene.document.TextField; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriterConfig; import org.apache.lucene.index.IndexWriterConfig.OpenMode; import org.apache.lucene.store.Directory; import org.apache.lucene.store.FSDirectory; import org.apache.lucene.queryparser.classic.QueryParser; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.Query; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.vectorhighlight.BaseFragmentsBuilder; import org.apache.lucene.search.vectorhighlight.FastVectorHighlighter; import org.apache.lucene.search.vectorhighlight.FieldQuery; import org.apache.lucene.search.vectorhighlight.FragListBuilder; import org.apache.lucene.search.vectorhighlight.FragmentsBuilder; import org.apache.lucene.search.vectorhighlight.ScoreOrderFragmentsBuilder; import org.apache.lucene.search.vectorhighlight.SimpleFragListBuilder; import org.apache.lucene.util.Version; //import org.wltea.analyzer.lucene.IKAnalyzer; import com.chenlb.mmseg4j.Dictionary; import com.chenlb.mmseg4j.analysis.SimpleAnalyzer; import org.junit.Test; /** * 环境:Lucene 4.1版本/IKAnalyzer 2012 FF版本/mmseg4j 1.9版本 * 功能: * 1.Fast高亮查询演示 * 注意: * 1.在创建索引时需要指定TermVectors/TermVectorPositions/TermVectorOffsets等参数为true; * 同样,值需要存储(setStored(true))。 * 2.如果是Solr中使用,在schema.xml中配置<field>形如: * <field name="webTitle" type="text_mm4j" indexed="true" stored="true" termVectors="true" termPositions="true" termOffsets="true"/> * @author Administrator * */ public class FastHighlightDemo { // mmseg4j字典路径 private static final String MMSEG4J_DICT_PATH = "C:\\solr\\news\\conf"; private static Dictionary dictionary = Dictionary.getInstance(MMSEG4J_DICT_PATH); // Lucene索引存放路径 private static final String LUCENE_INDEX_DIR = "C:\\solr\\news\\data\\index"; // @Test public void doFastIndex() throws Exception { // 如果没有索引可用 ,需要先建立索引的内容 String[] title = new String[] { "上海公务员抛售豪宅,记者张三报道", "广州打空姐区政委", "载8名船员中国渔船疑违规", "浙江5人疑坐冤狱17年续", "王岐山:拉长耳朵瞪大眼睛" }; String[] content = new String[] { "这是近两个月以来,上海公务员抛售豪宅的缩影。", "昨天,记者相继连线广州市越秀区宣传部", "一艘中国渔船24日下午疑因在长崎县附近日本专属经济水域内违规作业", "1995年3月20日和8月12日,在浙江萧山发生两起抢劫出租车司机事件", "王岐山说,今天开会的会议室是党的十一届三中全会召开的地方" }; // 实例化IKAnalyzer分词器 // Analyzer analyzer = new IKAnalyzer(); // 实例化mmseg4j分词器 Analyzer analyzer = new SimpleAnalyzer(dictionary); // 建立索引对象 Directory directory = FSDirectory.open(new File(LUCENE_INDEX_DIR)); // 配置IndexWriterConfig IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_41, analyzer); iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); IndexWriter writer = new IndexWriter(directory, iwConfig); // 写入索引,先创建默认的FieldType(索引、存储......) FieldType fieldType = new FieldType(); fieldType.setIndexed(true); fieldType.setStored(true); fieldType.setTokenized(true); fieldType.setStoreTermVectors(true); fieldType.setStoreTermVectorPositions(true); fieldType.setStoreTermVectorOffsets(true); fieldType.freeze(); for (int i = 0; i < title.length; i++) { Document doc = new Document(); doc.add(new TextField("id", "" + i, Field.Store.YES)); // 加入权重 Field field = new Field("title", title[i], fieldType); field.setBoost(AnalyzerTool.getBoost(title[i])); doc.add(field); field = new Field("content", title[i], fieldType); field.setBoost(AnalyzerTool.getBoost(content[i])); doc.add(field); // 计算字段,对此字段进行查询即可;查询时可以忽略title与content。 doc.add(new TextField("text", title[i], Field.Store.NO)); doc.add(new TextField("text", content[i], Field.Store.NO)); writer.addDocument(doc); } writer.forceMerge(1); writer.close(); System.out.println("索引建立成功!"); System.out.println("--------------------------"); } @Test public void doFastHighlightQuery() throws Exception { // 实例化IKAnalyzer分词器 // Analyzer analyzer = new IKAnalyzer(); // 实例化mmseg4j分词器,可以设置为另两种分词器 Analyzer analyzer = new SimpleAnalyzer(dictionary); // 实例化搜索器 Directory directory = FSDirectory.open(new File(LUCENE_INDEX_DIR)); DirectoryReader reader = DirectoryReader.open(directory); IndexSearcher searcher = new IndexSearcher(reader); final String FIELD_NAME = "webTitle"; String keyword = "记者"; // 使用QueryParser查询分析器构造Query对象 QueryParser qp = new QueryParser(Version.LUCENE_41, FIELD_NAME, analyzer); Query query = qp.parse(keyword); // 搜索相似度最高的5条记录 TopDocs hits = searcher.search(query, 5); System.out.println("命中:" + hits.totalHits); // 高亮代码1 FastVectorHighlighter highlighter = getHighlighter(); FieldQuery fieldQuery = highlighter.getFieldQuery(query); // 输出结果 for (ScoreDoc scoreDoc : hits.scoreDocs) { // 高亮代码2 String snippet = highlighter.getBestFragment( fieldQuery, searcher.getIndexReader(), scoreDoc.doc, FIELD_NAME, 100); if (snippet != null) { System.out.println(scoreDoc.doc + " : " + snippet + "<br/>"); } } reader.close(); directory.close(); System.out.println("--------------------------"); } private static FastVectorHighlighter getHighlighter() { // 可以在这里定义高亮关键词的样式,比如: // 改BaseFragmentsBuilder.COLORED_PRE_TAGS为new String[]{"<EM>"} // 改BaseFragmentsBuilder.COLORED_POST_TAGS为new String[]{"</EM>"} FragListBuilder fragListBuilder = new SimpleFragListBuilder(); FragmentsBuilder fragmentBuilder = new ScoreOrderFragmentsBuilder( BaseFragmentsBuilder.COLORED_PRE_TAGS, BaseFragmentsBuilder.COLORED_POST_TAGS); return new FastVectorHighlighter(true, true, fragListBuilder, fragmentBuilder); } }
输出:
命中:125
11388 : 浙江杭州一男子涉嫌殴打<b style="background:yellow">记者</b>被警方抓获<br/>
18295 : 领导快看;<b style="background:yellow">记者</b>曝光!<br/>
35243 : [视频]节前聚焦烟花爆竹安全 居民楼内存花炮 <b style="background:yellow">记者</b>举报无人监管 20130203<br/>
25672 : 老夫看过<b style="background:yellow">记者</b>关于肖某勒索的调查视频,可以说,“胁从犯罪”的证据极为明显——问题就在于,曾经处理方哦,算是结了案,再次处理,法理上有疑问<br/>
7255 : <b style="background:yellow">记者</b>调查:重庆忠县一桩疑窦重生的受贿案(转载)<br/>
--------------------------
备注:
代码中引用的AnalyzerTool类请参考:Lucene4.1:获取中文分词结果,根据文本计算boost