lucene 高亮显示

在Lucene的org.apache.lucene.search.highlight包中提供了关于高亮显示检索关键字的工具。

高亮显示模块需要两个独立的输入:完整的原始文本以用来提供操作数据,以及来源于该文本的一个TokenStream。为了创建TokenStream,你必须对文本进行重新分析,此时需要使用与索引期间相同的分析器进行。

package test;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.queryparser.classic.ParseException;
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.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.search.highlight.SimpleSpanFragmenter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class Seacher {
    // 索引保存路径
    public static String indexDir = "d:/LuceneIndex";

    public static void main(String[] args) throws ParseException, InvalidTokenOffsetsException, IOException {
     // 新建 IndexSearcher
        Directory dir = FSDirectory.open(new File(indexDir));
        IndexReader reader = DirectoryReader.open(dir);
        IndexSearcher searcher = new IndexSearcher(reader);
        // 搜索条件
        IKAnalyzer analyzer = new IKAnalyzer();
        analyzer.setUseSmart(true);

        QueryParser parser = new QueryParser("title", analyzer);

        Query query = parser.parse("如何设计一个电商网站");
        // 搜索结果
        TopDocs hits = searcher.search(query, 10);
        System.out.println(hits.scoreDocs.length);

        // 高亮显示
      /** 
       * 创建QueryScorer
       * Fragmenter输出的是文本片段序列,
       * 而Highlighter必须从中挑选出最适合的一个或多个片段呈现给客户,
       * 为了做到这点,Highlighter会要求Java接口Scorer来对每个片段进行评分
       * QueryTermScorer 基于片段中对应Query的项数进行评分
       * QueryScorer只对促成文档匹配的实际项进行评分
       */
        QueryScorer qs = new QueryScorer(query);
        /** 自定义标注高亮文本标签 */
        SimpleHTMLFormatter sh = new SimpleHTMLFormatter("<span style='color:red;'>", "</span>");
      /** 
       * 创建Fragmenter 作用是将原始字符串拆分成独立的片段
       * SimpleSpanFragmenter 是尝试将让片段永远包含跨度匹配的文档
       * SimpleFragmenter 是负责将文本拆分封固定字符长度的片段,但它并处理子边界(默认100)
       * NullFragmenter 整个字符串作为单个片段返回,这适合于处理title域和前台文本较短的域
       */
        Fragmenter fragmenter = new SimpleSpanFragmenter(qs);
        Highlighter highlighter = new Highlighter(sh, qs);
        highlighter.setTextFragmenter(fragmenter);

        // 获取搜索结果
        for (ScoreDoc scoreDoc : hits.scoreDocs) {
            Document doc = searcher.doc(scoreDoc.doc);
            String title = doc.get("title");
            if (title != null) {
                TokenStream ts = analyzer.tokenStream("title", new StringReader(title));
                String val = highlighter.getBestFragment(ts, title);
                System.out.println(doc.get("id") + " : " + val);
            }
        }
    }
}