天天看點

Lucene2.9.1使用小結(同樣适用于Lucene 3.0 )

【注意:本文版權歸++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操作。(未完待續)