【注意:本文版權歸++yong所有,轉載請注明。 】
【++yong的部落格位址:http://blog.csdn.net/qjyong】
開源全文搜尋工具包Lucene2.9.1的使用。
1. 搭建Lucene的開發環境:在classpath中添加lucene-core-2.9.1.jar包
2. 全文搜尋的兩個工作: 建立索引檔案,搜尋索引.
3. Lucene的索引檔案邏輯結構
1) 索引(Index)由若幹塊(片段)(Segment)組成
★2) 塊由若幹文檔(Document)組成: 一個檔案映射成一個文檔。資料庫表中的一條記錄映射成一個文檔。
★3) 文檔由若幹域(Field)組成:檔案的屬性(檔案路徑,檔案的内容)映射成一個域。記錄的某個字段映射成一個域。
☆4) 域由若幹詞(關鍵字)(Term)組成:檔案的屬性的内容中某個字元串映射成一個詞。
4. Lucene包結構
1) analysis子產品:負責詞法分析及語言處理而形成Term(詞)。提供了一些内置的分析器:最常用的是StandardAnalyzer
2) index子產品:負責索引的讀寫。 對索引檔案的segment進行寫、合并、優化的IndexWriter類。對索引進行讀取和删除操作的IndexReader類。
3) store子產品:負責索引的存儲。提供索引的各種存儲類:FSDirectory,RAMDirectory等。
4) document子產品:索引檔案内部的基礎存儲結構封裝。如:Document類和Field類等。
5) search子產品:負責對索引的搜尋。提供了索引搜尋器IndexSearcher類和各種Query類,如TermQuery、BooleanQuery等。
6) queryParser子產品:負責查詢語句的文法分析。提供了解析查詢語句的QueryParser類
7) util子產品:包含一些公共工具類。
5. 建立索引
1) IndexWriter:索引寫出器
a) 構造方法:
IndexWriter(Directory d, Analyzer a, IndexWriter.MaxFieldLength mfl)
如果索引不存在,就會被建立。如果索引存在,就追加.
IndexWriter(Directory d, Analyzer a, boolean create, IndexWriter.MaxFieldLength mfl)
create為true時,原索引檔案不存在就建立,存在就覆寫。
create為false時,原索引檔案不存在就報錯,存在就追加。
b) 常用方法:
void addDocument(Document doc); //把指定文檔添加到索引寫出器中
void iw.close(); //關閉索引寫出器,此時才把索引寫到目标存儲地
2) Directory: 索引存放地。
a) 檔案系統:FSDirectory: FSDirectory.open(File file);
b) 記憶體RAMDirectory: new RAMDirectory();
3) Analyzer: 分詞器。
a) StandardAnalyzer: 标準分詞器。對英文采用空白, 标點符号進行分詞。對中文采用單字分詞。
b) SmartChineseAnalyzer: 智能中文分詞器。(LUCENE_HOME/contrib/analyzers/smartcn/lucene-smartcn-2.9.1.jar)
C) 第三方的中文分詞器:如PaodingAnalyzer、IKAnalyzer
4) IndexWriter.MaxFieldLength: 指定域值的最大長度。
a) UNLIMITED 無限制的。
b) LIMITED 有限制的。值為10000
5) Document: 索引的組成單元. 一組Field的集合.
a) 構造方法: Document();
b) 常用方法: void add(Field f); //添加指定域到這個文檔中
6) Field: 域,代表文檔的某個索引域.
a) 構造方法: Field(String name, String value, Field.Store.YES, Field.Index.ANALYZED)
name: 域的名稱, 隻能是字元串.
value: 域的值, 隻能是字元串.
Field.Store: 指定Field的值是否存儲或怎樣存儲. NO(不存儲), YES(存儲),COMPRESS(壓縮後存儲)
Field.Index: 指定Field是否被索引或怎麼被索引. NO(不索引), ANALYZED(分詞後索引), NOT_ANALYZED(不分詞直接索引)
7) 示例代碼:
//src要建立索引的檔案,destDir索引存放的目錄
public static void createIndex(File src, File destDir){
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT); //建立一個文法分析器
IndexWriter iwriter = null;
Directory directory = null;
try {
directory = FSDirectory.open(destDir); //把索引檔案存儲到磁盤目錄
//建立一個IndexWriter(存放索引檔案的目錄,分析器,Field的最大長度)
iwriter = new IndexWriter(directory, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
//iwriter.setUseCompoundFile(true);//使用複合檔案
Document doc = new Document(); //建立一個Document對象
//把檔案路徑作為"path"域:不分詞,索引,儲存
doc.add(new Field("path", src.getCanonicalPath(), Field.Store.YES, Field.Index.NOT_ANALYZED));
StringBuilder sb = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(src));
for(String str = null; (str = br.readLine())!=null;){
sb.append(str).append(System.getProperty("line.separator"));
}
//檔案内容作為"content"域:分詞,索引,儲存
doc.add(new Field("contents", sb.toString(), Field.Store.YES, Field.Index.ANALYZED));
iwriter.addDocument(doc); //把Document存放到IndexWriter中
iwriter.optimize(); //對索引進行優化
} catch (IOException e) {
e.printStackTrace();
} finally {
if (iwriter != null) {
try {
iwriter.close(); //關閉IndexWriter時,才把記憶體中的資料寫到檔案
} catch (IOException e) {
e.printStackTrace();
}
}
if (directory != null) {
try {
directory.close(); //關閉索引存放目錄
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6. 查詢索引
1) IndexSearcher: 索引查詢器
a) 構造器: IndexSearcher(Directory path, boolean readOnly)
b) 常用方法:
TopDocs search(Query query, Filter filter, int n); //執行查詢。n指的是最多傳回的Document的數量。
Document doc(int 檔案内部編号); //根據文檔的内部編号擷取到該Document
void close(); //關閉查詢器
2) Query: 查詢對象。把使用者輸入的查詢字元串封裝成Lucene能夠識别的Query對象。
3) Filter: 用來過慮搜尋結果的對象。
4) TopDocs: 代表查詢結果集資訊對象。它有兩個屬性:
a) totalHits: 查詢命中數。
b) scoreDocs: 查詢結果資訊。它包含符合條件的Document的内部編号(doc)及評分(score)。
5) 示例代碼:
//keyword要搜尋的關鍵字。indexDir索引存放的目錄
public static void searcher(String keyword, File indexDir){
IndexSearcher isearcher = null;
Directory directory = null;
try{
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_CURRENT);
directory = FSDirectory.open(indexDir);
//建立解析器
QueryParser parser = new QueryParser(Version.LUCENE_CURRENT, "contents", analyzer);
Query query = parser.parse(keyword);//擷取查詢對象
// Query query1 = new TermQuery(new Term("contents", keyword));
// Query query2 = new TermQuery(new Term("contents", keyword2));
// BooleanQuery query = new BooleanQuery();
// query.add(query1, Occur.SHOULD);
// query.add(query2, Occur.SHOULD);
// QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_CURRENT, new String[]{"path", "contents"}, analyzer);
// Query query = parser.parse(keyword);
isearcher = new IndexSearcher(directory, true); //建立索引搜尋器
TopDocs ts = isearcher.search(query, null, 100); //執行搜尋,擷取查詢結果集對象
int totalHits = ts.totalHits; //擷取命中數
System.out.println("命中數:" + totalHits);
ScoreDoc[] hits = ts.scoreDocs; //擷取命中的文檔資訊對象
for (int i = 0; i < hits.length; i++) {
Document hitDoc = isearcher.doc(hits[i].doc); //根據命中的文檔的内部編号擷取該文檔
System.out.println(hitDoc.getField("contents").stringValue()); //輸出該文檔指定域的值
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} finally {
if (isearcher != null) {
try {
isearcher.close(); //關閉搜尋器
} catch (IOException e) {
e.printStackTrace();
}
}
if (directory != null) {
try {
directory.close(); //關閉索引存放目錄
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
7. 删除索引
IndexWriter提供deleteDocuments(Term term); //會删除索引檔案裡含有指定Term的所有Document。
IndexReader也提供了deleteDocuments(Term term);
8. 更新索引
IndexWriter提供updateDocument(Term term, Document doc); //實際上是先删除再建立索引。
9. 常用查詢器
1) TermQuery : 按Term(關鍵字)查詢。構造方法:TermQuery(Term t)
Query query = new TermQuery(new Term("contents", keyword));
isearcher = new IndexSearcher(FSDirectory.open(indexDir), true);
TopDocs ts = isearcher.search(query, null, 100);
2) BooleanQuery: 布爾查詢。組合多個查詢器。
Query query1 = new TermQuery(new Term("contents", keyword));
Query query2 = new TermQuery(new Term("contents", keyword2));
BooleanQuery query = new BooleanQuery();
query.add(query1, Occur.SHOULD);
query.add(query2, Occur.SHOULD);
isearcher = new IndexSearcher(directory, true);
TopDocs ts = isearcher.search(query, null, 100);
3) MultiFieldQueryParser: 多Field中查詢。
QueryParser parser = new MultiFieldQueryParser(Version.LUCENE_CURRENT, new String[]{"path", "contents"}, analyzer);
Query query = parser.parse(keyword);
isearcher = new IndexSearcher(FSDirectory.open(indexDir), true);
TopDocs ts = isearcher.search(query, null, 100);
10. 高亮器Highlighter:在網頁中對搜尋結果予以高亮顯示。
1) 在classpath添加contrib/highlighter/lucene-highlighter-2.9.1.jar
2) 示例僞代碼
SimpleHTMLFormatter shf = new SimpleHTMLFormatter("<span style="color:red" mce_style="color:red">", "</span>"); //預設是<b>..</b>
// 構造高亮器:指定高亮的格式,指定查詢計分器
Highlighter highlighter = new Highlighter(shf, new QueryScorer(query));
//設定塊劃分器
highlighter.setTextFragmenter(new SimpleFragmenter(Integer.MAX_VALUE));
String content = highlighter.getBestFragment(Analyzer, "fieldName", "fieldValue");
11. 優化
1) 使用IndexWriter須注意
修改索引後,需flush()或close()方能生效
2) 使用IndexSearcher須注意
一旦打開,不會搜尋到以後添加的索引
線程安全,多個線程僅需一個執行個體
3) 最佳實踐
多個線程共享一個IndexSearcher, 隻有當索引修改後才重新打開IndexSearcher
多個線程共享一個IndexWriter并嚴格同步
異步修改索引提高性能(JMS)
為每個Document建立單獨的索引目錄
12. 在emall項目中整合Lucene對産品的ID,名稱和描述進行全文搜尋。
13. 使用Compass簡化Lucene操作。(未完待續)