在Lucene的org.apache.lucene.search.highlight包中提供了關(guān)于高亮顯示檢索關(guān)鍵字的工具。使用百度、Google搜索的時(shí)候,檢索結(jié)果顯示的時(shí)候,在摘要中實(shí)現(xiàn)與關(guān)鍵字相同的詞條進(jìn)行高亮顯示,百度和Google指定紅色高亮顯示。
有了Lucene提供的高亮顯示的工具,可以很方便地實(shí)現(xiàn)高亮顯示的功能。
高亮顯示,就是根據(jù)用戶輸入的檢索關(guān)鍵字,檢索找到該關(guān)鍵字對(duì)應(yīng)的檢索結(jié)果文件,提取對(duì)應(yīng)于該文件的摘要文本,然后根據(jù)設(shè)置的高亮格式,將格式寫入到摘要文本中對(duì)應(yīng)的與關(guān)鍵字相同或相似的詞條上,在網(wǎng)頁(yè)上顯示出來(lái),該摘要中的與關(guān)鍵字有關(guān)的文本就會(huì)以高亮的格式顯示出來(lái)。
Lucene中org.apache.lucene.search.highlight.SimpleHTMLFormatter類可以構(gòu)造一個(gè)高亮格式,這是最簡(jiǎn)單的構(gòu)造方式,例如:
SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
構(gòu)造方法聲明為public SimpleHTMLFormatter(String preTag, String postTag),因?yàn)檫@種高亮格式是依賴于網(wǎng)頁(yè)文件的,HTML文件中是以標(biāo)記(tag)來(lái)標(biāo)識(shí)的,即存在一個(gè)preTag和一個(gè)postTag。
上面構(gòu)造的高亮格式是摘要中出現(xiàn)的關(guān)鍵字使用紅色來(lái)顯示,區(qū)分其它文本。
通過(guò)構(gòu)造好的高亮格式對(duì)象,來(lái)構(gòu)造一個(gè)org.apache.lucene.search.highlight.Highlighter實(shí)例,然后根據(jù)對(duì)檢索結(jié)果得到的Field的文本內(nèi)容(這里是指摘要文本)進(jìn)行切分,找到與檢索關(guān)鍵字相同或相似的詞條,將高亮格式加入到摘要文本中,返回一個(gè)新的、帶有格式的摘要文本,在網(wǎng)頁(yè)上就可以呈現(xiàn)高亮顯示。
下面實(shí)現(xiàn)一個(gè)簡(jiǎn)單的例子,展示實(shí)現(xiàn)高亮顯示的處理過(guò)程。
測(cè)試類如下所示:
package org.shirdrn.lucene.learn.highlight;
import java.io.IOException;
import java.io.StringReader;
import net.teamhot.lucene.ThesaurusAnalyzer;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.queryParser.ParseException;
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.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
public class MyHighLighter {
private String indexPath = "F:\\index";
private Analyzer analyzer;
private IndexSearcher searcher;
public MyHighLighter(){
analyzer = new ThesaurusAnalyzer();
}
public void createIndex() throws IOException { // 該方法建立索引
IndexWriter writer = new IndexWriter(indexPath,analyzer,true);
Document docA = new Document();
String fileTextA = "因?yàn)榛馃瓶偸侨紵г谔?yáng)沖下地平線的時(shí)刻,然后便是寧?kù)o的自然的天籟,沒有誰(shuí)會(huì)在這樣的時(shí)光的鏡片里傷感自語(yǔ),因?yàn)闋N爛給人以安靜的舒適感。";
Field fieldA = new Field("contents", fileTextA, Field.Store.YES,Field.Index.TOKENIZED);
docA.add(fieldA);
Document docB = new Document();
String fileTextB = "因?yàn)閹в幸詡蹫榇鷥r(jià)的美麗風(fēng)景總是讓人不由地惴惴不安,緊接著襲面而來(lái)的抑或是病痛抑或是災(zāi)難,沒有誰(shuí)會(huì)能夠安逸著恬然,因?yàn)槟:屓怂盒牧逊蔚叵雲(yún)群啊?;
Field fieldB = new Field("contents", fileTextB, Field.Store.YES,Field.Index.TOKENIZED);
docB.add(fieldB);
Document docC = new Document();
String fileTextC = "我喜歡上了一個(gè)人孤獨(dú)地行游,在夢(mèng)與海洋的交接地帶熾烈燃燒著。"+
"因?yàn)椋粭l孤獨(dú)的魚喜歡上了火焰的顏色,真是荒唐地不合邏輯。";
Field fieldC = new Field("contents", fileTextC, Field.Store.YES,Field.Index.TOKENIZED);
docC.add(fieldC);
writer.addDocument(docA);
writer.addDocument(docB);
writer.addDocument(docC);
writer.optimize();
writer.close();
}
public void search(String fieldName,String keyword) throws CorruptIndexException, IOException, ParseException{ // 檢索的方法,并實(shí)現(xiàn)高亮顯示
searcher = new IndexSearcher(indexPath);
QueryParser queryParse = new QueryParser(fieldName, analyzer); // 構(gòu)造QueryParser,解析用戶輸入的檢索關(guān)鍵字
Query query = queryParse.parse(keyword);
Hits hits = searcher.search(query);
for(int i=0;i<hits.length();i++){
Document doc = hits.doc(i);
String text = doc.get(fieldName);
SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<font color='red'>", "</font>");
Highlighter highlighter = new Highlighter(simpleHTMLFormatter,new QueryScorer(query));
highlighter.setTextFragmenter(new SimpleFragmenter(text.length()));
if (text != null) {
TokenStream tokenStream = analyzer.tokenStream(fieldName,new StringReader(text));
String highLightText = highlighter.getBestFragment(tokenStream, text);
System.out.println("★高亮顯示第 "+(i+1) +" 條檢索結(jié)果如下所示:");
System.out.println(highLightText);
}
}
searcher.close();
}
public static void main(String[] args) { // 測(cè)試主函數(shù)
MyHighLighter mhl = new MyHighLighter();
try {
mhl.createIndex();
mhl.search("contents", "因?yàn)?);
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
}
}
}
程序說(shuō)明:
1、createIndex()方法:使用ThesaurusAnalyzer分析器為指定的文本建立索引。每個(gè)Document中都有一個(gè)name為contents的Field。在實(shí)際應(yīng)用中,可以再構(gòu)造一一個(gè)name為path的Field,指定檢索到的文件的路徑(本地路徑或者網(wǎng)絡(luò)上的鏈接地址)
2、根據(jù)已經(jīng)建好的索引庫(kù)進(jìn)行檢索。這首先需要解析用戶輸入的檢索關(guān)鍵字,使用QueryParser,必須與后臺(tái)使用的分析器相同,否則不能保證解析得到的查詢(由詞條構(gòu)造)Query檢索到合理的結(jié)果集。
3、根據(jù)解析出來(lái)的Query進(jìn)行檢索,檢索結(jié)果集保存在Hits中。遍歷,提取每個(gè)滿足條件的Document的內(nèi)容,程序中直接把它的內(nèi)容當(dāng)作摘要內(nèi)容,實(shí)現(xiàn)高亮顯示。在實(shí)際應(yīng)用中,應(yīng)該對(duì)應(yīng)著一個(gè)提取摘要(或者檢索數(shù)據(jù)庫(kù)得到檢索關(guān)鍵字對(duì)應(yīng)的結(jié)果集文件的摘要內(nèi)容)的過(guò)程。有了摘要以后,就可以為摘要內(nèi)容增加高亮格式。
4、如果提取結(jié)果集文件的前N個(gè)字符串作為摘要,只需要在 highlighter.setTextFragmenter(new SimpleFragmenter(text.length())); 中設(shè)置顯示摘要的字?jǐn)?shù),這里顯示全部的文本作為摘要。
運(yùn)行程序,結(jié)果如下所示:
詞庫(kù)尚未被初始化,開始初始化詞庫(kù).
初始化詞庫(kù)結(jié)束。用時(shí):3906毫秒;
共添加195574個(gè)詞語(yǔ)。
★高亮顯示第 1 條檢索結(jié)果如下所示:
<font color='red'>因?yàn)?lt;/font>火燒云總是燃燒著消失在太陽(yáng)沖下地平線的時(shí)刻,然后便是寧?kù)o的自然的天籟,沒有誰(shuí)會(huì)在這樣的時(shí)光的鏡片里傷感自語(yǔ),<font color='red'>因?yàn)?lt;/font>燦爛給人以安靜的舒適感。
★高亮顯示第 2 條檢索結(jié)果如下所示:
<font color='red'>因?yàn)?lt;/font>帶有以傷痕為代價(jià)的美麗風(fēng)景總是讓人不由地惴惴不安,緊接著襲面而來(lái)的抑或是病痛抑或是災(zāi)難,沒有誰(shuí)會(huì)能夠安逸著恬然,<font color='red'>因?yàn)?lt;/font>模糊讓人撕心裂肺地想?yún)群啊?br />
★高亮顯示第 3 條檢索結(jié)果如下所示:
我喜歡上了一個(gè)人孤獨(dú)地行游,在夢(mèng)與海洋的交接地帶熾烈燃燒著。<font color='red'>因?yàn)?lt;/font>,一條孤獨(dú)的魚喜歡上了火焰的顏色,真是荒唐地不合邏輯。
上面的檢索結(jié)果在HTML網(wǎng)頁(yè)中,就會(huì)高亮顯示關(guān)鍵字“因?yàn)?#8221;,顯示為紅色。