慢慢开始读Lucene源代码,首先就从高亮显示开始吧,因为最近才看过这个,而且好像是新版本后来加上的。

我的方案:从实例逐一解决源代码。

需要分析的实例代码:

package org.apache.lucene.search.highlight; 

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

import junit.framework.TestCase; 

import org.apache.lucene.analysis.Analyzer; 
import org.apache.lucene.analysis.TokenStream; 
import org.apache.lucene.analysis.standard.StandardAnalyzer; 
import org.apache.lucene.document.Document; 
import org.apache.lucene.document.Field; 
import org.apache.lucene.index.IndexReader; 
import org.apache.lucene.index.IndexWriter; 
import org.apache.lucene.queryParser.QueryParser; 
import org.apache.lucene.search.Hits; 
import org.apache.lucene.search.IndexSearcher; 
import org.apache.lucene.search.Query; 
import org.apache.lucene.search.Searcher; 
import org.apache.lucene.store.RAMDirectory; 

public class HighlighterTest extends TestCase  
{ 
    private IndexReader reader; 
    private static final String FIELD_NAME = "contents"; 
    private Query query; 
    RAMDirectory ramDir; 
    public Searcher searcher = null; 
    public Hits hits = null; 
    int numHighlights = 0; 
    Analyzer analyzer=new StandardAnalyzer(); 
    QueryScorer queryScorer=null; 

    String texts[] = 
        { 
            "Hello this is a piece of text that is very long and contains too much preamble and the meat is really here which says kennedy has been shot", 
            "This piece of text refers to Kennedy at the beginning then has a longer piece of text that is very long in the middle and finally ends with another reference to Kennedy", 
            "JFK has been shot", 
            "John Kennedy has been shot", 
            "This text has a typo in referring to Keneddy" }; 

    public HighlighterTest(String arg0) 
    { 
        super(arg0); 
    } 

    public void testSimpleHighlighter() throws Exception 
    { 
        doSearching("Kennedy"); 
        queryScorer=new QueryScorer(query);                                     //one 
        Highlighter highlighter =new Highlighter(queryScorer);             //two 
        highlighter.setTextFragmenter(new SimpleFragmenter(100));    //three 
        int maxNumFragmentsRequired =20; 
        for (int i = 0; i < hits.length(); i++) 
        { 
            String text = hits.doc(i).get(FIELD_NAME); 
            TokenStream tokenStream=analyzer.tokenStream(FIELD_NAME,new StringReader(text)); //four 

            String result = 
                highlighter.getBestFragments(tokenStream,text,maxNumFragmentsRequired, "...");       //five 
            System.out.println("\t" + result); 
        } 
    } 

    public void doSearching(String queryString) throws Exception 
    { 
        QueryParser parser=new QueryParser(FIELD_NAME, new StandardAnalyzer()); 
        query = parser.parse(queryString); 
        doSearching(query); 
    } 
    public void doSearching(Query unReWrittenQuery) throws Exception 
    { 
        searcher = new IndexSearcher(ramDir); 

        /* 
         * 在将Query实例传递到QueryScorer之前,可以调用 
         * Query.rewrite(IndexReader)方法来重写Query对象 
         * (否则,你必须确保用户输入的查询文本就是Lucene直接可以处理最基本的项)。 
         */ 
        query=unReWrittenQuery.rewrite(reader); 
         
        System.out.println("Searching for: " + query.toString(FIELD_NAME)); 
        hits = searcher.search(query); 
    }    

    /* 
     * @see TestCase#setUp() 
     */ 
    protected void setUp() throws Exception 
    { 
        ramDir = new RAMDirectory(); 
        IndexWriter writer = new IndexWriter(ramDir, new StandardAnalyzer(), true); 
        for (int i = 0; i < texts.length; i++) 
        { 
            addDoc(writer, texts); 
        } 

        writer.optimize(); 
        writer.close(); 
        reader = IndexReader.open(ramDir); 
        numHighlights = 0; 
    } 

    private void addDoc(IndexWriter writer, String text) throws IOException 
    { 
        Document d = new Document(); 
        Field f = new Field(FIELD_NAME, text,Field.Store.YES, Field.Index.TOKENIZED); 
        d.add(f); 
        writer.addDocument(d); 

    } 

    /* 
     * @see TestCase#tearDown() 
     */ 
    protected void tearDown() throws Exception 
    { 
        super.tearDown(); 
    } 

} 

 

上文是运用Junit来测试单元的,主要用到的类有:

QueryScorer;

HighLight;同时衍生出很多不同的类和接口。

整体内容如下:

Highlighter包括了三个主要部分:段划分器(Fragmenter)、计分器(Scorer)和格式化器(Formatter)。这几个部分对应于Java的同名接口,并且每部分都有一个内置的实现以便我们使用。最简单的Highlighter将返回在 匹配项周围的最佳段落,并使用HTML的<B>将这些项标记出来:

String text = “The quick brown fox jumps over the lazy dog”;
TermQuery query = new TermQuery(new Term(“field”, “fox”));
Scorer scorer = new QueryScorer(query);
Highlighter highlighter = new Highlighter(scorer);
TokenStream tokenStream =
new SimpleAnalyzer().tokenStream(“field”,
new StringReader(text));
System.out.println(highlighter.getBestFragment(tokenStream,text));
前述代码将产生如下输出

The quick brown <B>fox</B> jumps over the lazy dog

Highlighter 不仅需要你提供记分器和需要高亮显示的文本,还需要一个TokenStream实例。这个TokenStream实例是由分析器生成的。为了成功地对项进 行高亮显示,Query中的这些项需要匹配TokenStream产生的Token实例。我们提供的文本则被用于生成TokenStream,而这个 TokenStream又被用作高亮显示的原始文本。每个由TokenStream生成的Token实例都包含语汇单元的位置信息,这些信息用来指示原始 文本中高亮部分的起始和结束位置。

Highlighter利用Fragmenter将原始文本分割成多个片段。内置的SimpleFragmenter将原始文本分割成相同大小的片段,片段默认的大小为100个字符。这个大小是可控制的。

QueryScorer 是内置的计分器。计分器的工作首先是将片段排序。QueryScorer使用的项是从用户输入的查询中得到的;它会从原始输入的单词、词组和布尔查询中提 取项,并且基于相应的加权因子(boost factor)给它们加权。为了便于QueryScoere使用,还必须对查询的原始形式进行重写。比如,带通配符查询、模糊查询、前缀查询以及范围查询 等,都被重写为BoolenaQuery中所使用的项。在将Query实例传递到QueryScorer之前,可以调用Query.rewrite (IndexReader)方法来重写Query对象(否则,你必须确保用户输入的查询文本就是Lucene直接可以处理最基本的项)。

最后,格式化器(Formatter)用于装饰项文本。如果不指定其他的格式化器,Lucene会默认使用内置的格式化器 SimpleHTMLFormatter,这个格式化器将会用HTML的黑体开始标签(begin bold tags <B>)和黑体结束标签(end bold tags </B>)来标识出高亮显示的项文本。Highlighter默认地使用SimpleHTMLFormatter和 SimpleFragmenter这两个格式化器。每一个由Formatter高亮显示的项都将会带有一个语汇单元评分。当使用QueryScorer 时,这个评分将作为查询该项的加权因子。这个语汇单元评分能够被用来决定该项的重要性。要利用这个特性就必须实现自定义的格式化器。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐