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

posted @ 2013-01-25 16:36  那些年的事儿  阅读(1325)  评论(0编辑  收藏  举报