Lucene介紹及簡單應用

wq1917發表於2020-04-05

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);

相關文章