Lucene介紹及簡單應用

1 簡介

1.1 什麼是Lucene

Lucene是一個高性能、可伸縮的信息搜索(IR)庫。可以爲應用程序添加索引和搜索能力。Lucene是用java實現的成熟的、免費的開源項目,是Apache Jakarta大家庭的一員,基於在Apache軟件許可 [ASF, License]。同樣,Lucene是當前與近幾年內非常流行的免費的Java信息搜索(IR)庫。

       是一個支持全文檢索的開源工具包。

1.2 Lucene的發展

Lucene最初是由Doug Cutting 開發的。在 SourceForge 的網站上提供下載。在 2001 9 月做爲高質量的開源Java產品加入到Apache軟件基金會的Jakarta家族中。

1.3 Lucene下載安裝

當前版本:lucene2.4.1

下載地址:http://apache.justdn.org/lucene/java/

下載:lucene-2.4.1.zip   jar

lucene-2.4.1-src.zip 原代碼

      

       直接使用jar包:

              lucene-2.4.1.zip解壓縮,將lucene-core-2.4.1.jar 導入工程。

       使用原代碼:

              lucene-2.4.1-src.zip解壓縮,將%LUCENE%/src/java/org/apache/lucene導入工程。

1.4 一個簡單的例子

public class text {

   

    public static List<TestItem> list = new ArrayList<TestItem>();

    public text() {

       TestItem testItem[] = new TestItem[5];

        testItem[0] = new TestItem("01","java編程",11,200.00,new Date(2008,01,06));

        testItem[1] = new TestItem("02","java思想",11,200.00,new Date(2008,02,06));

        testItem[2] = new TestItem("03","設計模式",11,200.00,new Date(2008,03,06));

        testItem[3] = new TestItem("04","編程與模式編程與模式編程與模式編程與模式",11,200.00,new Date(2008,04,06));

        testItem[4] = new TestItem("05","設計思想",11,200.00,new Date(2008,05,06));

        for (int i = 0; i < testItem.length; i++) {

            list.add(testItem[i]);

       }     

    }

    public static void createIndex(Directory dir, Analyzer analyzer) {  

        try {  

    //參數依次:索引目錄、分詞工具、是否清空目錄、字段值的最大長度(UNLImitedInterger.MaxValue  

            IndexWriter writer = new IndexWriter(dir, analyzer, true,  

                    IndexWriter.MaxFieldLength.UNLIMITED);  

            double start =  new Date().getTime();

            for (int i = 0; i < list.size(); i++) {

              TestItem testItem = (TestItem)list.get(i);

              Document doc = new Document();  

             

                doc.add(new Field("id",testItem.id , Field.Store.YES,  

                        Field.Index.ANALYZED));  

                doc.add(new Field("name", testItem.name, Field.Store.YES,  

                        Field.Index.ANALYZED));

                doc.add(new Field("number", Integer.toString(testItem.number), Field.Store.YES,  

                        Field.Index.ANALYZED));

                doc.add(new Field("worth", Double.toString(testItem.worth), Field.Store.YES,  

                        Field.Index.ANALYZED));

                doc.add(new Field("date", testItem.date.toString(), Field.Store.YES,  

                        Field.Index.ANALYZED));

                writer.addDocument(doc);

           }

            writer.close();  

            double end =  new Date().getTime();

            System.out.println((end-start)+" milliseconds");

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

    }  

    public static void search(Directory dir, Analyzer analyzer) {  

        try {  

        double start =  new Date().getTime();

            Searcher searcher = new IndexSearcher(dir);  

            Query query = new QueryParser("name", analyzer).parse("模式");

            //此處在2.0基礎上有改動,此處必須傳入一個返回條數,這裏用searcher.maxDoc()表示返回所有條數。  

            ScoreDoc[] docs = searcher.search(query, searcher.maxDoc()).scoreDocs;  

            double end =  new Date().getTime();

            System.out.println("IndexIng  "+docs.length +" files took "+(end-start)+" milliseconds");

            Document doc;  

            for (int i = 0; i < docs.length; i++) {  

                doc = searcher.doc(docs[i].doc);  

                System.out.println(doc.get("name"));  

            } 

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

    }  

    public static void main(String[] args) {  

        try {  

        text t = new text();

            Analyzer analyzer = new StandardAnalyzer();   

            FSDirectory dir = FSDirectory.getDirectory(new File("D://Lucene_index"));  

            createIndex(dir, analyzer);  

            search(dir, analyzer);  

            dir.close();  

        } catch (Exception e) {  

            e.printStackTrace();  

        }  

    }  

}

1.5 索引和搜索

1.5.1 索引

爲了快速搜索大量的文本,必須首先索引那個文本然後把它轉化爲一個可以讓你快速搜索的格式,除去緩慢的順序地掃描過程。這個轉化過程稱爲索引,它的輸出稱爲一條索引。可以把索引理解爲一個可以讓你快速隨機訪問存於其內部的詞的數據結構。它隱含的概念類似於 一本 。

1.5.2 搜索

搜索是在一個索引中查找單詞來找出它們所出現的文檔的過程。一個搜索的質量用精確度和召回率來描述。召回率衡量搜索系統搜索到相關文檔的能力,精確度衡量系統過濾不相關文檔的能力支持單個和多個詞彙的查詢,短語查詢,通配符,結果分級和排序。

 

1.6 索引的關鍵字

ü         IndexWriter

ü         Directory

ü         Analyzer

ü         Document

ü         Field

1.6.1 IndexWriter

IndexWriter是在索引過程中的中心組件。這個類創建一個新的索引並且添加文檔到一個已有的索引中你可以把IndexWriter想象成讓你可以對索引進行寫操作的對象,但是不能讓你讀取或搜索。

1.6.2 Directory

Directory類代表一個 Lucene 索引的位置。

FSDirectorylucene索引存儲在磁盤上。

RAMDirectorylucene索引存儲在內存中。

 

1.6.3 Analyzer

在文本索引之前先通過 AnalyzerAnalyzer IndexWriter 的構造函數中指定,對文本內容提取關鍵詞併除去其它的內容。如果要索引的內容不是普通的文本,首先要轉化成文本Analyzer是個抽象類,但是Lucene中有幾個它的實現

1.6.4 Document

一個Document代表字段的集合。你可以把它想象爲以後可獲取的虛擬文檔一塊數據,如一個網頁一個郵件消息或一個文本文件。一個文檔的字段代表這個文檔或與這個文檔相關的元數據。文檔數據的最初來源Lucene無關。元數據如作者、標題、主題、修改日期等等,分別做爲文檔的字段索引和存儲。

1.6.5 Field

在索引中的每個 Document 含有一個或多個字段,具體化爲 Field 類。每個字段相應於數據的一個 片段,將在搜索時查詢或從索引中重新獲取。

 

2 創建索引

 

2.1 創建索引步驟

1、 轉化爲文本

爲用Lucene索引數據,必須首先將它轉化爲純文本單詞流,Lucene能消化的格式。將HTMLPDFDOC等文本形式轉換爲txt文件,索引和搜索.txt文件,分析它的內容並使它來生成Field的實例。

 

2、 分析

一旦準備好了要索引的數據並創建了由Field組成的Lucene Document,就可以調用IndexWriteraddDocument(Document)方法,把數據送入Lucene索引。Lucene首先分析這些數據以使它更適合索引。它將文本數據分割成塊或單詞,並執行一些可選擇的操作。例如,單詞可以在索引之前轉化爲小寫,以保證搜索是大小寫無關的。典型的,它也有可能排除轉入中所有經常出現但無意義的詞,例如英語終止詞(a, an, the, in, on等等)。類似的,通常分析輸入單詞以獲取它的本質。

 

3、 寫索引

在輸入被分析完後,就可以添加到索引中了。Lucene將輸入存儲在一個反向索引的數據結構中。這個數據結構在允許快速關鍵字查詢的同時有效地利用了磁盤空間。這個結構反向是因爲它使用從輸入中提取的單詞做爲查詢鍵值而不是用處理的文檔做爲中樞入口。換句話說,代替嘗試回答這個問題這個文檔中含有哪些單詞?,這個結構爲提供快速回答哪篇文檔含有單詞X做了優化。

 

 

2.2 新增documentfield

IndexWriter writer = new IndexWriter(dir, analyzer, true,  

                    IndexWriter.MaxFieldLength.UNLIMITED);   //dir索引存放位置

TestItem testItem = (TestItem)list.get(i);

Document doc = new Document();              

doc.add(new Field("id",testItem.id , Field.Store.YES, Field.Index.ANALYZED));  

writer.addDocument(doc);

writer.close();  

 

2.3 索引其他操作

1、 在索引中清除Document

IndexReader reader = IndexRead.open(dir);

Reader.delete(1); // 根據document的序號刪除,序號是變化的。

Reader.delete(new Term(“city”,value)); //根據term刪除

Reader.close();

2、 恢復清除Document

刪除時,在沒有關閉indexReader前,要刪除的索引做刪除標識,並沒有對索引進行修改。在關閉IndexReader前執行undeleteAll();撤銷對索引的刪除標識。

 

3、 修改Document

先刪除後新增的過程。

1. 打開IndexReader

2. 刪除所有你要刪除的Document

3. 關閉IndexReader

4. 打開IndexWriter

5. 添加你要添加的所有Document

6. 關閉IndexWriter

 

3 搜索

對創建的索引進行搜索,初始化搜索、設置搜索條件、搜索、結果處理。

 

Searcher searcher new IndexSearcherdir);//dir表示索引文件的存放地址。

 

 

Searcher searcher = new IndexSearcher(dir);   //dir表示索引文件的存放地址。

Query query = new QueryParser("name", analyzer).parse("模式");

ScoreDoc[] docs = searcher.search(rQuery, searcher.maxDoc()).scoreDocs;  

Document doc;  

for (int i = 0; i < docs.length; i++) {  

    doc = searcher.doc(docs[i].doc);  

    System.out.println(doc.get("name")+ doc.get("date"));  

}

3.1 按詞條搜索

    TermQuery是最簡單也是最常用的QueryTermQuery可以理解爲“詞條搜索”,在搜索引擎中最基本的搜索就是在索引中搜索某一詞條,TermQuery就是用來完成這項工作的。

    Term term = new Term("name","模式");

Query q = new TermQuery(term);

 

3.2 或、與搜索

       BooleanQuery也是實際開發過程中經常使用的一種Query。它其實是一個組合的Query,在使用時可以把各種Query對象添加進去並標明它們之間的邏輯關係。在本節中所討論的所有查詢類型都可以使用BoolenQuery綜合起來。BooleanQuery本身來講是一般布爾子句的容器,它提供了專門的API方法往其中添加子句,並註明它們之間的關係。

 

   Query query = new QueryParser("name", analyzer).parse("模式");

   Query query1 = new QueryParser("name", analyzer).parse("編程");

   BooleanQuery query2 = new BooleanQuery();

   query2.add(query1,Occur.MUST); 

   query2.add(query,Occur.SHOULD);

 

Occur.MUST :必須包含

Occur.NOTMUST :必須不包含

Occur.SHOULD :可以包含

 

 

3.3 在一定範圍內搜索

       有時用戶需要一種在一個範圍內查找某個文檔,比如查找某一時間段內的所有文檔,此時,Lucene提供了RangeQuery的類來滿足這種需求。

       RangeQuery表示在某範圍內的搜索條件,實現從一個開始詞條到一個結束詞條的搜索功能,在查詢時“開始詞條”和“結束詞條”可以被包含在內也可以不被包含在內。

Term term1 = new Term("id","01");

Term term2 = new Term("id","04");

RangeQuery rQuery = new RangeQuery(term1,term2,false);//不包含邊界

RangeQuery rQuery = new RangeQuery(term1,term2,true); //包含邊界

 

3.4 使用前綴搜索

       PrefixQuery就是使用前綴來進行查詢的,通常情況下,首先定義一個詞條Term。該詞條包含要查找的字段名以及關鍵字的前綴,然後通過該詞條構造一個PrefixQuery對象,就可以進行前綴查找了。

Term term = new Term("name","模式");

PrefixQuery pQuery = new PrefixQuery(term);

3.5 多關鍵字搜索

       除了普通的TermQuery外,Lucene還提供了一種Phrase查詢的功能,用戶在搜索引擎中進行搜索時,常常查找的並非是一個簡單的詞,很可能是幾個不同的關鍵字、這些關鍵字之間要麼是緊密相聯成爲一個精確的短語,要麼是可能自愛這幾個關鍵字之間還插有其他無關的關鍵字。此時,用戶希望將它們找出來。從評分的角度看,這些關鍵字之間擁有與查找內容無關短語所在的文檔的分值一般會比較底一些。

       PhraseQuery正是Lucene所提供的滿足上述需求的一種Query對象、它的addfangf可以讓用戶往其內添加關鍵字,在添加完畢後,用戶還可以通過setSlop()方法來設定一個稱之爲“坡度”的變量來確定關鍵字之間十分允許、允許多少個無關詞彙的存在。

Term term1 = new Term("id","01");

Term term2 = new Term("id","04");

PhraseQuery phQuery = new PhraseQuery();

phQuery.add(term1);

phQuery.add(term2);

3.6 使用通配符搜索

WildcardQuery

Term term1 = new Term("name","設計*");

    WildcardQuery wildcardQuery = new WildcardQuery(term1);

3.7 相近詞語搜索

Term term1 = new Term("name","設計");

    FuzzyQuery fuzzyQuery = new FuzzyQuery(term1);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章