慢慢开始读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[i]);
- }
- 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 时,这个评分将作为查询该项的加权因子。这个语汇单元评分能够被用来决定该项的重要性。要利用这个特性就必须实现自定义的格式化器。
下一篇我要开始学习QueryScorer的源代码