要說(shuō)這高亮,還是比較好搞的,就是要引用luncene的highlight這個(gè)包,然后調(diào)用它里面的方法就可以了。主要代碼如下:






























上面這段代碼就已經(jīng)對(duì)關(guān)鍵詞進(jìn)行了高亮的處理,高亮處理后,關(guān)鍵詞的在網(wǎng)頁(yè)的顯示效果為
當(dāng)初令我困擾的地方并不是如何實(shí)現(xiàn)高亮,畢竟實(shí)現(xiàn)高亮的代碼google一下有很多,真正令我困擾的是如果將處理后的結(jié)果顯示到網(wǎng)頁(yè)上?因?yàn)槲乙祷匾粋€(gè)List給前端頁(yè)面,
這個(gè)List中存儲(chǔ)的是Lucene的Document,但是在做高亮處理時(shí),是將Docuemnt的內(nèi)容取出放到了String類型的變量里,最初的時(shí)候,由于腦子一時(shí)沒(méi)轉(zhuǎn)過(guò)來(lái)所以一直不知道該如何在頁(yè)面上顯示,經(jīng)過(guò)一個(gè)周末的休息,周一上班時(shí)腦子突然活絡(luò)——將處理好的內(nèi)容再重新封裝到Lucene的Document中,不就可以像沒(méi)處理前一樣,添加到List里然后返回給前端頁(yè)面了嗎,我怎么早沒(méi)想到呢。封裝代碼如下:











?
?
?
?
?
Lucene關(guān)鍵字高亮顯示
在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的自然的天籟,沒(méi)有誰(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)難,沒(mé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的自然的天籟,沒(méi)有誰(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)難,沒(méi)有誰(shuí)會(huì)能夠安逸著恬然
,<font color='red'>因?yàn)?lt;/font>
模糊讓人撕心裂肺地想?yún)群啊?
★高亮顯示第 3 條檢索結(jié)果如下所示:
我喜歡上了一個(gè)人孤獨(dú)地行游,在夢(mèng)與海洋的交接地帶熾烈燃燒著。
<font color='red'>因?yàn)?lt;/font>
,一條孤獨(dú)的魚喜歡上了火焰的顏色,真是荒唐地不合邏輯。
上面的檢索結(jié)果在HTML網(wǎng)頁(yè)中,就會(huì)高亮顯示關(guān)鍵字“因?yàn)椤保@示為紅色。
http://blog.sina.com.cn/s/blog_5de48f8b0100dple.html
??? Lucene不是一個(gè)現(xiàn)成的程序,類似文件搜索程序或web網(wǎng)絡(luò)爬行器或是一個(gè)網(wǎng)站的搜索引擎。Lucene是一個(gè)軟件庫(kù),一個(gè)開發(fā)工具包,而不是一個(gè)具 有完整特征的搜索應(yīng)用程序。它本身只關(guān)注文本的索引和搜索。Lucene使你可以為你的應(yīng)用程序添加索引和搜索能力。目前已經(jīng)有很多應(yīng)用程序的搜索功能是 基于 Lucene 的,比如 Eclipse 的幫助系統(tǒng)的搜索功能。 ??? Lucene 采用的是一種稱為反向索引(inverted index)的機(jī)制。反向索引就是說(shuō)我們維護(hù)了一個(gè)詞/短語(yǔ)表,對(duì)于這個(gè)表中的每個(gè)詞/短語(yǔ),都有一個(gè)鏈表描述了有哪些文檔包含了這個(gè)詞/短語(yǔ)。這樣在用戶輸入查詢條件的時(shí)候,就能非常快的得到搜索結(jié)果。
??? 文檔建立好索引后,就可以在這些索引上面進(jìn)行搜索了。搜索引擎首先會(huì)對(duì)搜索的關(guān)鍵詞進(jìn)行解析,然后再在建立好的索引上面進(jìn)行查找,最終返回和用戶輸入的關(guān)鍵詞相關(guān)聯(lián)的文檔。
1. 準(zhǔn)備環(huán)境:添加jar包
2. 構(gòu)造IndexWriter。IndexWriter是Lucene用來(lái)創(chuàng)建索引的一個(gè)核心的類。使用構(gòu)造方法IndexWriter(Directory d, Analyzer a, MaxFieldLength mfl); 如果索引不存在,就會(huì)被創(chuàng)建。 * 相關(guān)參數(shù)說(shuō)明 <1> Directory,代表了 Lucene 的索引的存儲(chǔ)的位置。這是一個(gè)抽象類,常用的有兩個(gè)實(shí)現(xiàn),第一個(gè)是 FSDirectory,它表示一個(gè)存儲(chǔ)在文件系統(tǒng)中的索引位置。第二個(gè)是 RAMDirectory,它表示一個(gè)存儲(chǔ)在內(nèi)存當(dāng)中的索引位置。 <2> Analyzer,在一個(gè)文檔被索引之前,首先需要對(duì)文檔內(nèi)容進(jìn)行分詞處理,這部分工作就是由 Analyzer 來(lái)做的。Analyzer 類是一個(gè)抽象類,它有多個(gè)實(shí)現(xiàn)。針對(duì)不同的語(yǔ)言和應(yīng)用需要選擇適合的 Analyzer。Analyzer 把分詞后的內(nèi)容交給IndexWriter 來(lái)建立索引。 <3> MaxFieldLength,用于限制Field的大小。這個(gè)變量可以讓用戶有計(jì)劃地對(duì)大文檔Field進(jìn)行截取。假如取值為10000,就只索引每個(gè) Field的前10000個(gè)Term(關(guān)鍵字)。也就是說(shuō)每個(gè)Field中只有前10000個(gè)Term(關(guān)鍵字)建立索引,除此之外的部分都不會(huì)被 Lucene索引,當(dāng)然也不能被搜索到。 3. 創(chuàng)建索引,使用方法IndexWriter.addDocument(Document doc)。 * 相關(guān)參數(shù)說(shuō)明 <1> Document,是用來(lái)描述Lucene文檔結(jié)構(gòu)的。任何需要進(jìn)行索引的數(shù)據(jù)都必須轉(zhuǎn)化成Document對(duì)象。Document是索引和搜索的最基本單元,是一組Field的集合。 <2> Field,組成Document的元素,用來(lái)描述一個(gè)文檔的某個(gè)屬性的,比如一封電子郵件的標(biāo)題和內(nèi)容可以用兩個(gè)Field對(duì)象分別描述。Field是 由name和value組成的,value只接受字符串(非字符串類型要先轉(zhuǎn)換成字符串才行)。在構(gòu)造Field時(shí)要指定Store和Index。
??? Field.Store,指定Field是否或怎樣存儲(chǔ)。
??? Field.Index,指定Field是否或怎么被索引。
注意:當(dāng)完成了索引操作后,一定要調(diào)用IndexWriter.close()方法。 4,刪除索引: IndexWriter.deleteDocuments(Term term); 會(huì)刪除索引文件里含有指定Term的所有Document。Term,是搜索的基本單位。代表某個(gè)Field中出現(xiàn)的某個(gè)關(guān)鍵字。? 5,更新索引: IndexWriter.updateDocument(Term term, Document doc); 實(shí)際上是先刪除再創(chuàng)建索引,就是說(shuō)如果有多條符合條件的Document,更新后只有一條。 6,搜索: 使用類IndexSearcher。查詢方法為: IndexSearcher.search(Query, Filter, int);
相關(guān)參數(shù)說(shuō)明:
ScoreDoc.doc返回文檔的內(nèi)部編號(hào)。
Query可以用QueryParser解析查詢字符串生成。使用構(gòu)造方法為QueryParser(String defaultFieldName, Analyzer a),第一個(gè)參數(shù)為默認(rèn)查詢的Field,第二個(gè)參數(shù)為使用的分詞器(這里用的分詞器要和建立索引時(shí)用的分詞器一致,否則可能會(huì)搜索不到結(jié)果)。使用parse(String)方法解析查詢內(nèi)容。 相關(guān)代碼:
List<Document> docs = new ArrayList<Document>();
???Filter filter = null;
???TopDocs topDocs = indexSearcher.search(query, filter, nDocs);
???for (int i = 0; i < topDocs.totalHits; i++) {
????docs.add(doc);
???return new SearchResult(topDocs.totalHits, docs);
7,測(cè)試LuceneIndexDao的增刪改查方法,把LuceneIndexDao做為練習(xí),要求通過(guò)LuceneIndexDaoTest中的單元測(cè)試,相關(guān)的測(cè)試代碼由于比較多,暫不一一列出。 8. 下面是對(duì)一些重要的類及術(shù)語(yǔ)進(jìn)行解釋:
<1> Directory,有兩個(gè):
? a) 文檔的得分與用戶輸入的關(guān)鍵字有關(guān)系,而且是實(shí)時(shí)運(yùn)算的結(jié)果。得分會(huì)受關(guān)鍵字在文檔中出現(xiàn)的位置與次數(shù)等的影響。 ? b) 可以利用Boost影響Lucene查詢結(jié)果的排序,通過(guò)設(shè)置Document的Boost來(lái)影響文檔的權(quán)重,以達(dá)到控制查詢結(jié)果順序的目的:Document.setBoost(float)。默認(rèn)值為1f,值越大,得分越高。
? c) 也可以在查詢時(shí)給Filed指定boost。當(dāng)同樣的term屬于不同的field時(shí),如果field的boost不一樣,其所屬的文件的得分也不一樣。
??? 對(duì)于英文的分詞,是按照標(biāo)點(diǎn)、空白等拆分單詞,比較簡(jiǎn)單;而中文的分詞就比較復(fù)雜了,如”中華人民共和國(guó)“可以分為”中華”、“人民”、“共和國(guó)”,但不應(yīng)有“華人”這個(gè)詞(不符合語(yǔ)義)。
??? 中文分詞幾種常用的方式:
通過(guò)方法getBestFragment(Analyzer a, String fieldName,String text)實(shí)現(xiàn)高亮。(如果進(jìn)行高亮的field中沒(méi)有出現(xiàn)關(guān)鍵字,返回null)。
? 1) TermQuery,按Term(關(guān)鍵字)查詢(term的值應(yīng)是最終的關(guān)鍵字,英文應(yīng)全部小寫)。
? 2) RangeQuery,指定范圍查詢。
? 3) PrefixQuery,前綴查詢。
? 4) WildcardQuery,通配符查詢,可以使用"?"代表一個(gè)字符,"*"代表0個(gè)或多個(gè)字符。(通配符不能出現(xiàn)在第一個(gè)位置上)
? 5) MultiFieldQueryParser,在多個(gè)Field中查詢。
?? syntax: + - AND NOT OR (必須為大寫)。 |
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
