從lucene開始學elasticsearch

最近從lucene開始學搜索引擎,看到這篇文章寫得不錯,保存下來,以待後面繼續看,

轉載自簡書

               https://www.jianshu.com/p/d710d4cb171e

1. 案例分析:什麼時全文檢索,如何實現全文檢索

  1.1 案例

  實現一個文件的搜索功能,通過關鍵字搜索文件,凡是文件名或文件內容包括關鍵字的文件都需要找出來。還可以根據中文詞語進行查詢,並且需要支持多個條件查詢。

 

 

本案例中的原始內容就是磁盤上的文件,如下圖:

2. 需求分析

  2.1. 數據庫搜索

數據庫中的搜索很容易實現,通常都是使用sql語句進行查詢,而且能很快的得到查詢結果。
爲什麼數據庫搜索很容易?
因爲數據庫中的數據存儲是有規律的,有行有列而且數據格式、數據長度都是固定的。

  2.2. 數據分類

我們生活中的數據總體分爲兩種:結構化數據和非結構化數據。
結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。
非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等磁盤上的文件

2.3. 非結構化數據查詢方法

(1)順序掃描法(Serial Scanning)
  所謂順序掃描,比如要找內容包含某一個字符串的文件,就是一個文檔一個文檔的看,對於每一個文檔,從頭看到尾,如果此文檔包含此字符串,則此文檔爲我們要找的文件,接着看下一個文件,直到掃描完所有的文件。如利用windows的搜索也可以搜索文件內容,只是相當的慢。

(2)全文檢索(Full-text Search)
  將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然後對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。這部分從非結構化數據中提取出的然後重新組織的信息,我們稱之索引。
例如:字典。字典的拼音表和部首檢字表就相當於字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,於是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然後按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。
這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。
雖然創建索引的過程也是非常耗時的,但是索引一旦創建就可以多次使用,全文檢索主要處理的是查詢,所以耗時間創建索引是值得的。

  2.4. 如何實現全文檢索

可以使用Lucene實現全文檢索。Lucene是apache下的一個開放源代碼的全文檢索引擎工具包。提供了完整的查詢引擎和索引引擎,部分文本分析引擎。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能。

  2.5. 全文檢索的應用場景

對於數據量大、數據結構不固定的數據可採用全文檢索方式搜索,比如百度、Google等搜索引擎、論壇站內搜索、電商網站站內搜索等。

3. Lucene實現全文檢索的流程

3.1.索引和搜索流程圖

  1. 綠色表示索引過程,對要搜索的原始內容進行索引構建一個索引庫,索引過程包括:
    確定原始內容即要搜索的內容 -> 採集文檔 -> 創建文檔 -> 分析文檔 -> 索引文檔

  2. 紅色表示搜索過程,從索引庫中搜索內容,搜索過程包括:
    用戶通過搜索界面 -> 創建查詢 -> 執行搜索,從索引庫搜索 -> 渲染搜索結果

3.2. 創建索引

創建索引步驟:
  1. 獲得原始文檔
  2. 創建文檔對象
  3. 分析文檔
  4. 創建索引
  對文檔索引的過程,將用戶要搜索的文檔內容進行索引,索引存儲在索引庫(index)中。
這裏我們要搜索的文檔是磁盤上的文本文件,根據案例描述:凡是文件名或文件內容包括關鍵字的文件都要找出來,這裏要對文件名和文件內容創建索引。

  3.2.1 獲得全部文檔
  原始文檔是指要索引和搜索的內容。原始內容包括互聯網上的網頁、數據庫中的數據、磁盤上的文件等。
本案例中的原始內容就是磁盤上的文件,如下圖:

 

  從互聯網上、數據庫、文件系統中等獲取需要搜索的原始信息,這個過程就是信息採集,信息採集的目的是爲了對原始內容進行索引。
  在Internet上採集信息的軟件通常稱爲爬蟲或蜘蛛,也稱爲網絡機器人,爬蟲訪問互聯網上的每一個網頁,將獲取到的網頁內容存儲起來。
  Lucene不提供信息採集的類庫,需要自己編寫一個爬蟲程序實現信息採集,也可以通過一些開源軟件實現信息採集,如下:

開源軟件
Nutch(http://lucene.apache.org/nutch), Nutch是apache的一個子項目,包括大規模爬蟲工具,能夠抓取和分辨web網站數據
jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據
heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一個由 java 開發的、開源的網絡爬蟲,用戶可以使用它來從網上抓取想要的資源。其最出色之處在於它良好的可擴展性,方便用戶實現自己的抓取邏輯

  本案例我們要獲取磁盤上文件的內容,可以通過文件流來讀取文本文件的內容,對於pdf、doc、xls等文件可通過第三方提供的解析工具讀取文件內容,比如Apache POI讀取doc和xls的文件內容。

  3.2.2 創建文檔對象
  獲取原始內容的目的是爲了索引,在索引前需要將原始內容創建成文檔(Document),文檔中包括一個一個的域(Field),域中存儲內容。
  這裏我們可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容),如下圖:

 

注意: 每個Document可以有多個Field,不同的Document可以有不同的Field,同一個Document可以有相同的Field(域名和域值都相同)
每個文檔都有一個唯一的編號,就是文檔id。

  3.2.3 分析文檔
  將原始內容創建爲包含域(Field)的文檔(document),需要再對域中的內容進行分析,分析的過程是經過對原始文檔提取單詞、將字母轉爲小寫、去除標點符號、去除停用詞等過程生成最終的語彙單元,可以將語彙單元理解爲一個一個的單詞。

  比如下邊的文檔經過分析如下:
  原文檔內容:

Lucene is a Java full-text search engine. Lucene is not a complete
application, but rather a code library and API that can easily be used
to add search capabilities to applications.

  分析後得到的語彙單元:
  lucene、java、full、search、engine .....

  每個單詞叫做一個Term,不同的域中拆分出來的相同的單詞是不同的term。term中包含兩部分一部分是文檔的域名,另一部分是單詞的內容。

例如:文件名中包含apache和文件內容中包含的apache是不同的term。

  3.2.4 創建索引
  對所有文檔分析得出的語彙單元進行索引,索引的目的是爲了搜索,最終要實現只搜索被索引的語彙單元從而找到Document(文檔)。

 

注意:創建索引是對語彙單元索引,通過詞語找文檔,這種索引的結構叫倒排索引結構
傳統方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大、搜索慢。
倒排索引結構是根據內容(詞語)找文檔,如下圖:

倒排索引結構

 

倒排索引結構也叫反向索引結構,包括索引和文檔兩部分,索引即詞彙表,它的規模較小,而文檔集合較大。

3.3 查詢索引

  查詢索引也是搜索的過程。搜索就是用戶輸入關鍵字,從索引(index)中進行搜索的過程。根據關鍵字搜索索引,根據索引找到對應的文檔,從而找到要搜索的內容(這裏指磁盤上的文件)。

  3.3.1. 用戶查詢接口
 全文檢索系統提供用戶搜索的界面供用戶提交搜索的關鍵字,搜索完成展示搜索結果。

  比如:

  Lucene不提供製作用戶搜索界面的功能,需要根據自己的需求開發搜索界面。

  3.3.2. 創建查詢
  用戶輸入查詢關鍵字執行搜索之前需要先構建一個查詢對象,查詢對象中可以指定查詢要搜索的Field文檔域、查詢關鍵字等,查詢對象會生成具體的查詢語法,
  例如:
  語法“fileName:lucene”表示要搜索Field域的內容爲“lucene”的文檔

  3.3.3. 執行查詢

  • 搜索索引過程:
    根據查詢語法在倒排索引詞典表中分別找出對應搜索詞的索引,從而找到索引所鏈接的文檔鏈表。
    比如搜索語法爲“fileName:lucene”表示搜索出fileName域中包含Lucene的文檔。
    搜索過程就是在索引上查找域爲fileName,並且關鍵字爲Lucene的term,並根據term找到文檔id列表。

  3.3.4. 渲染結果

  • 以一個友好的界面將查詢結果展示給用戶,用戶根據搜索結果找自己想要的信息,爲了幫助用戶很快找到自己的結果,提供了很多展示的效果,比如搜索結果中將關鍵字高亮顯示,百度提供的快照等。

 

4. 配置環境開發

4.1. Lucene下載

Lucene是開發全文檢索功能的工具包,從官方網站下載Lucene4.10.3,並解壓。
官方網站:http://lucene.apache.org/
版本:lucene4.10.3
Jdk要求:1.7以上
IDE:Eclipse

4.2. javaSE項目中使用的jar包

jar包 jar包位置

lucene-4.10.3\core Lucene包

lucene-4.10.3\analysis\common Lucene包

lucene-4.10.3\queryparser Lucene包
commons-io-2.4.jar IO流包

5. 功能一:創建索引庫

使用 IndexWriter 對象創建

5.1 實現步驟

1. 創建一個java工程,並導入jar包。
2. 創建一個indexwriter對象。
  2.1 指定索引庫的存放位置Directory對象
  2.2 指定一個分析器,對文檔內容進行分析。
3. 創建document對象。
4. 創建field對象,將field添加到document對象中。
5. 使用indexwriter對象將document對象寫入索引庫,此過程進行索引創建。並將索引和document對象寫入索引庫。
6. 關閉IndexWriter對象。

5.2. Field域的屬性

是否分析:是否對域的內容進行分詞處理。前提是我們要對域的內容進行查詢。
是否索引:將Field分析後的詞或整個Field值進行索引,只有索引方可搜索到。
比如:商品名稱、商品簡介分析後進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作爲查詢條件。
是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field纔可以從Document中獲取
比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要存儲。

是否存儲的標準:是否要將內容展示給用戶

Field類 數據類型 Analyzed是否分析 Indexed是否索引 Stored是否存儲 說明
StringField(FieldNameFieldValue,Store.YES) 字符串 N Y Y或N 這個Field用來構建一個字符串Field,但是不會進行分析,會將整個串存儲在索引中,比如(訂單號,姓名等)
是否存儲在文檔中用Store.YES或Store.NO決定
LongField(FieldNameFieldValue,Store.YES) Long型 Y Y Y或N 這個Field用來構建一個Long數字型Field,進行分析和索引,比如(價格)
是否存儲在文檔中用Store.YES或Store.NO決定
StoredField(FieldNameFieldValue) 重載方法,支持多種類型 N N Y 這個Field用來構建不同類型Field不分析,不索引,但要Field存儲在文檔中
TextField(FieldNameFieldValueStore.NO)或TextField(FieldName, reader) 字符串/流 Y Y Y或N 如果是一個Reader, lucene猜測內容比較多,會採用Unstored的策略

5.3 代碼實現:創建索引庫

@Test
    public void createIndex() throws Exception {
        // 1.指定索引庫存放的位置,可以是內存也可以是磁盤
        // Directory directory = new RAMDirectory();//保存到內存中一般不用
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));

        // 2.創建一個IndexWriter對象,需要一個分析器對象。
        //Analyzer analyzer = new StandardAnalyzer();// 分析器
        IKAnalyzer analyzer = new IKAnalyzer();

        // 參數1:lucene的版本號,參數2:分析器對象
        IndexWriterConfig conf = new IndexWriterConfig(Version.LATEST, analyzer);

        // 參數1:索引庫存放的路徑,參數2:配置信息,其中包含分析器對象
        IndexWriter indexWriter = new IndexWriter(directory, conf);

        // 3.獲得原始文檔,使用io流讀取文本文件
        File docPath = new File("E:\\File\\lucene&solr\\資料\\searchsource");
        for (File f : docPath.listFiles()) {
            // 取文件名
            String fileName = f.getName();
            // 取文件路徑
            String filePath = f.getPath();
            // 文件內容
            String fileContent = FileUtils.readFileToString(f);
            // 文件的大小
            long fileSize = FileUtils.sizeOf(f);
            // 4.創建文檔對象
            Document document = new Document();
            // 5.創建域
            // 參數1:域的名稱 。參數2:域的內容 。參數3:是否存儲
            TextField fileNameFiled = new TextField("name", fileName, Store.YES);
            StoredField filePathField = new StoredField("path", filePath);
            TextField fileContentField = new TextField("content", fileContent, Store.NO);
            LongField fileSizeField = new LongField("size", fileSize, Store.YES);
            // 6.向文檔中添加域
            document.add(fileNameFiled);
            document.add(filePathField);
            document.add(fileContentField);
            document.add(fileSizeField);
            // 7.把文檔對象寫入索引庫
            indexWriter.addDocument(document);
        }
        // 8.關閉IndexWriter對象
        indexWriter.close();
    }

6. 功能二:查詢索引

6.1 實現步驟

1. 創建一個Directory對象,也就是索引庫存放的位置。
2. 創建一個indexReader對象,需要指定Directory對象。
3. 創建一個indexsearcher對象,需要指定IndexReader對象
4. 創建一個TermQuery對象,指定查詢的域和查詢的關鍵詞。
5. 執行查詢。
6. 返回查詢結果。遍歷查詢結果並輸出。
7. 關閉IndexReader對象

6.2 IndexSearcher 搜索方法

方法 說明
indexSearcher.search(query, n) 根據Query搜索,返回評分最高的n條記錄
indexSearcher.search(queryfiltern) 根據Query搜索,添加過濾策略,返回評分最高的n條記錄
indexSearcher.search(query,nsort) 根據Query搜索,添加排序策略,返回評分最高的n條記錄
indexSearcher.search(booleanQueryfilter,nsort) 根據Query搜索,添加過濾策略,添加排序策略,返回評分最高的n條記錄

6.3 代碼實現

    /**
     * 查詢索引庫
     * @throws Exception
     */
    @Test
    public void searchIndex() throws Exception {
        // 1.指定索引庫存放的位置
        Directory directory = FSDirectory.open(new File("C:\\tem\\index"));
        // 2.使用IndexReader對象打開索引庫
        IndexReader indexReader = DirectoryReader.open(directory);
        // 3.創建一個IndexSearcher對象,構造方法需要一個IndexReader對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        // 4.創建一個查詢對象,需要指定查詢域及要查詢的關鍵子
        // term的參數1:要搜索的域 參數2:要搜索的關鍵字
        Query query = new TermQuery(new Term("name", "apache"));
        // 參數1:查詢條件 參數2:返回的結果數量
        // 5.取查詢結果
        TopDocs topDocs = indexSearcher.search(query, 10);
        // 取查詢結果總記錄數
        System.out.println("查詢結果總記錄數:" + topDocs.totalHits);
        // 6.遍歷查詢結果並打印
        for (ScoreDoc doc : topDocs.scoreDocs) {
            // 取文檔id
            int id = doc.doc;
            // 從索引庫中取文檔對象
            Document document = indexSearcher.doc(id);
            // 取屬性
            System.out.println(document.get("name"));
            System.out.println(document.get("size"));
            System.out.println(document.get("content"));
            System.out.println(document.get("path"));
        }
        // 7.關閉IndexReaer對象
        indexReader.close();
    }

6.4 TopDocs對象

Lucene搜索結果可通過TopDocs遍歷,TopDocs類提供了少量的屬性,如下:

方法或屬性 說明
totalHits 匹配搜索條件的總記錄數
scoreDocs 頂部匹配記錄

注意:
Search方法需要指定匹配記錄數量n:indexSearcher.search(query,n)
TopDocs.totalHits:是匹配索引庫中所有記錄的數量
TopDocs.scoreDocs:匹配相關度高的前邊記錄數組,scoreDocs的長度小於等於search方法指定的參數n

 

7. 功能三:支持中文分詞

7.1. 分析器(Analyzer)的執行過程

如下圖是語彙單元的生成過程:

從一個Reader字符流開始,創建一個基於Reader的Tokenizer分詞器,經過三個TokenFilter生成語彙單元Token。
要看分析器的分析效果,只需要看Tokenstream中的內容就可以了。每個分析器都有一個方法tokenStream,返回一個tokenStream對象。

7.2 分析器的分詞效果

//查看標準分析器的分詞效果
    public void testTokenStream() throws Exception {
        //創建一個標準分析器對象
        Analyzer analyzer = new StandardAnalyzer();
        //獲得tokenStream對象
        //第一個參數:域名,可以隨便給一個
        //第二個參數:要分析的文本內容
        TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model.");
        //添加一個引用,可以獲得每個關鍵詞
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        //添加一個偏移量的引用,記錄了關鍵詞的開始位置以及結束位置
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        //將指針調整到列表的頭部
        tokenStream.reset();
        //遍歷關鍵詞列表,通過incrementToken方法判斷列表是否結束
        while(tokenStream.incrementToken()) {
            //關鍵詞的起始位置
            System.out.println("start->" + offsetAttribute.startOffset());
            //取關鍵詞
            System.out.println(charTermAttribute);
            //結束位置
            System.out.println("end->" + offsetAttribute.endOffset());
        }
        tokenStream.close();
    }

7.3 中文分析器

  7.3.1 Lucene自帶中文分詞器

  • StandardAnalyzer:
    單字分詞:就是按照中文一個字一個字地進行分詞。如:“我愛中國”,
    效果:“我”、“愛”、“中”、“國”。
  • CJKAnalyzer
    二分法分詞:按兩個字進行切分。如:“我是中國人”,效果:“我是”、“是中”、“中國”“國人”。

上邊兩個分詞器無法滿足需求。

  • SmartChineseAnalyzer
    對中文支持較好,但擴展性差,擴展詞庫,禁用詞庫和同義詞庫等不好處理

  7.3.2 第三方周顧問分析器

  7.3.3 IKAnalyzer

解壓IKAnalyzer


使用方法:
第一步:把jar包添加到工程中
第二步:把配置文件IKAnalyzer.cfg.xml和擴展詞典ext.dic和停用詞詞典stopword.dic添加到classpath下
注意:ext.dic和stopword.dic文件的格式爲UTF-8,注意是無BOM的UTF-8編碼。

 

7.4. Analyzer使用時機

  7.4.1 搜索時使用Analyzer
  輸入關鍵字進行搜索,當需要讓該關鍵字與文檔域內容所包含的詞進行匹配時需要對文檔域內容進行分析,需要經過Analyzer分析器處理生成語彙單元(Token)。分析器分析的對象是文檔中的Field域。當Field的屬性tokenized(是否分詞)爲true時會對Field值進行分析,如下圖:

 

對於一些Field可以不用分析:

  1. 不作爲查詢條件的內容,比如文件路徑
  2. 不是匹配內容中的詞而匹配Field的整體內容,比如訂單號、身份證號等。

  7.4.2 搜索時使用Analyzer
  對搜索關鍵字進行分析和索引分析一樣,使用Analyzer對搜索關鍵字進行分析、分詞處理,使用分析後每個詞語進行搜索。比如:搜索關鍵字:spring web ,經過分析器進行分詞,得出:spring web拿詞去索引詞典表查找 ,找到索引鏈接到Document,解析Document內容。
對於匹配整體Field域的查詢可以在搜索時不分析,比如根據訂單號、身份證號查詢等。
注意:搜索使用的分析器要和索引使用的分析器一致。

 

8. 功能四:索引庫的維護

8.1 索引庫的添加

  8.1.1. 步驟
  向索引庫中添加document對象。
  1. 先創建一個indexwriter對象
  2. 創建一個document對象
  3. 把document對象寫入索引庫
  4. 關閉indexwriter。

  8.1.2. 代碼實現

    //添加索引
    @Test
    public void addDocument() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //創建文檔
        Document document = new Document();
        //創建域
        TextField fileNameField = new TextField("name","測試文本.txt",Store.YES);
        StoredField filePathField = new StoredField("path", "c:\\tem\\測試文本.txt");
        //向document對象中添加域。
        //不同的document可以有不同的域,同一個document可以有相同的域。
        document.add(new TextField("filename", "新添加的文檔", Store.YES));
        document.add(new TextField("content", "新添加的文檔的內容", Store.NO));
        document.add(new TextField("content", "新添加的文檔的內容第二個content", Store.YES));
        document.add(new TextField("content1", "新添加的文檔的內容要能看到", Store.YES));
        //添加文檔到索引庫
        indexWriter.addDocument(document);
        indexWriter.close();
    }

8.2 索引庫刪除

  8.2.1. 刪除全部

    //刪除全部索引
    @Test
    public void deleteAllIndex() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //刪除全部索引
        indexWriter.deleteAll();
        //關閉indexwriter
        indexWriter.close();
    }

說明:將索引目錄的索引信息全部刪除,直接徹底刪除,無法恢復。

  8.2.2. 指定查詢條件刪除

    //根據查詢條件刪除索引
    @Test
    public void deleteIndexByQuery() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //創建一個查詢條件
        Query query = new TermQuery(new Term("filename", "apache"));
        //根據查詢條件刪除
        indexWriter.deleteDocuments(query);
        //關閉indexwriter
        indexWriter.close();
    }

8.3 索引庫的修改

原理就是先刪除後添加

    //修改索引庫
    @Test
    public void updateIndex() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //創建一個Document對象
        Document document = new Document();
        //向document對象中添加域。
        //不同的document可以有不同的域,同一個document可以有相同的域。
        document.add(new TextField("filename", "要更新的文檔", Store.YES));
        document.add(new TextField("content", "2013年11月18日 - Lucene 簡介 Lucene 是一個基於 Java 的全文信息檢索工具包,它不是一個完整的搜索應用程序,而是爲你的應用程序提供索引和搜索功能。", Store.YES));
        indexWriter.updateDocument(new Term("content", "java"), document);
        //關閉indexWriter
        indexWriter.close();
    }

 

9. Lucene索引庫查詢

Lucene的查詢:

  1. 使用Query的子類查詢
    • MatchAllDocsQuery
    • TermQuery
    • NumericRangeQuery
    • BooleanQuery
  2. 使用QueryParser
    • QueryParser
    • MulitFieldQueryParser

  對要搜索的信息創建Query查詢對象,Lucene會根據Query查詢對象生成最終的查詢語法,類似關係數據庫Sql語法一樣Lucene也有自己的查詢語法,比如:“name:lucene”表示查詢Fieldname“lucene”的文檔信息。

可通過兩種方法創建查詢對象:

  1. 使用Lucene提供Query子類
    Query是一個抽象類,lucene提供了很多查詢對象,比如TermQuery項精確查詢,NumericRangeQuery數字範圍查詢等。
    如下代碼:
Query query = new TermQuery(new Term("name", "lucene"));
  1. 使用QueryParse解析查詢表達式
    QueryParse會將用戶輸入的查詢表達式解析成Query對象實例。
    如下代碼:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");

9.1 使用query的子類查詢

  9.1.1 MatchAllDocsQuery 查詢索引目錄中的所有文檔

    /**
     * 使用MatchAllDocsQuery查詢索引目錄中的所有文檔 
     */
    @Test
    public void testMatchAllDocsQuery() throws Exception {
        //指定索引庫存放的位置
        Directory directory = FSDirectory.open(new File("C:\\tem\\index"));
        //創建一個IndexReader對象
        DirectoryReader indexReader = DirectoryReader.open(directory);
        //創建IndexSearcher對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //創建一個Query對象
        Query query = new MatchAllDocsQuery();
        System.out.println(query);//*:*
        //查詢索引庫獲取查詢結果
        TopDocs topDocs = indexSearcher.search(query, 100);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        System.out.println("查詢結果總記錄數:"+topDocs.totalHits);
        //遍歷查詢結果
        for (ScoreDoc scoreDoc : scoreDocs) {
            int id = scoreDoc.doc;//獲取每個文檔的id
            Document document = indexSearcher.doc(id);//同過id查詢文檔對象
            //獲取文檔對象的屬性
            System.out.println(document.get("name"));
            System.out.println(document.get("size"));
            System.out.println(document.get("path"));
            System.out.println(document.get("content"));
        }
        //關閉索引庫
        indexReader.close();
    }

  9.1.2 TermQuery 指定要查詢的域和要查詢的關鍵詞

//使用Termquery查詢
    @Test
    public void testTermQuery() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        //創建查詢對象
        Query query = new TermQuery(new Term("content", "lucene"));
        //執行查詢
        TopDocs topDocs = indexSearcher.search(query, 10);
        //共查詢到的document個數
        System.out.println("查詢結果總數量:" + topDocs.totalHits);
        //遍歷查詢結果
        for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
            Document document = indexSearcher.doc(scoreDoc.doc);
            System.out.println(document.get("filename"));
            //System.out.println(document.get("content"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
        }
        //關閉indexreader
        indexSearcher.getIndexReader().close();
    }

  9.1.3 NumericRangeQuery 可以根據數值範圍查詢

//創建一個數值範圍查詢對象
        //參數1:要查詢的域 參數2:最小值 參數3:最大值 參數4:是否包含最小值 參數5:是否包含最大值
        Query query = NumericRangeQuery.newLongRange("size", 1000l, 10000l, false, true);
        System.out.println(query);//size:{1000 TO 10000]
        //打印結果
                //printResult , getIndexSearcher 抽取通用的方法
        printResult(getIndexSearcher(), query);

  9.1.3 BooleanQuery 可可以組合查詢條件

    @Test
    public void testBooleandQuery() throws Exception {
        //創建一個BooleanQuery對象
        BooleanQuery booleanQuery = new BooleanQuery();
        //創建子查詢,文件大於1000小於10000
        Query query1 = NumericRangeQuery.newLongRange("size", 1000l, 10000l, false, true);
        //文件命中包含mybatis關鍵字
        TermQuery query2 = new TermQuery(new Term("name","mybatis"));
        //添加包BooleanQuery對象中
        booleanQuery.add(query1,Occur.MUST);
        booleanQuery.add(query2,Occur.MUST);
        //執行查詢
        printResult(getIndexSearcher(), booleanQuery);
    }

Occur.MUST:必須滿足此條件,相當於and
Occur.SHOULD:應該滿足,但是不滿足也可以,相當於or
Occur.MUST_NOT:必須不滿足。相當於not

9.2 使用queryparser查詢

  通過QueryParser也可以創建Query,QueryParser提供一個Parse方法,此方法可以直接根據查詢語法來查詢。Query對象執行的查詢語法可通過System.out.println(query);查詢。
需要使用到分析器。建議創建索引時使用的分析器和查詢索引時使用的分析器要一致。

  9.2.1 queryParser查詢
  加入queryParser依賴的jar包

    /**
     * QueryParser 查詢
     */
    @Test
    public void testQueryParser() throws Exception {
        //創建一個QueryPareser對象。 參數1:默認搜索域  參數2:分析器對象
        QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
        //調用pares方法可以獲得一個Query對象
        //參數:要查詢的內容,可以是一句話。先分詞在查詢
        //Query query = queryParser.parse("cotent:mybatis");
        Query query = queryParser.parse("mybatis is a apache project");
        printResult(getIndexSearcher(), query);
    }

查詢語法

  1. 基礎的查詢語法,關鍵詞查詢:
    域名+“:”+搜索的關鍵字
    例如:content:java
  2. 範圍查詢
    域名+“:”+[最小值 TO 最大值]
    例如:size:[1 TO 1000]
    範圍查詢在lucene中不支持數值類型,支持字符串類型。在solr中支持數值類型。
  3. 組合條件查詢
    • +條件1 +條件2:兩個條件之間是並且的關係and
      例如:+filename:apache +content:apache
    • +條件1 條件2:必須滿足第一個條件,應該滿足第二個條件
      例如:+filename:apache content:apache
    • 條件1 條件2:兩個條件滿足其一即可。
      例如:filename:apache content:apache
    • -條件1 條件2:必須不滿足條件1,要滿足條件2
      例如:-filename:apache content:apache
Occur.MUST 查詢條件必須滿足,相當於and +(加號
Occur.SHOULD 查詢條件可選,相當於or 空(不用符號)
Occur.MUST 查詢條件必須滿足,相當於and +(加號

第二種寫法:
條件1 AND 條件2
條件1 OR 條件2
條件1 NOT 條件2

  9.2.2 MulitFieldQueryParser查詢
  可以指定多個默認搜索域

    /**
     * MulitFieldQueryParser 可以指定多個默認搜索域 
     */
    @Test
    public void testMultiFileQuryParser() throws Exception {
        //指定默認搜索域
        String[] fields = {"name","content"};
        MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer());
        Query query = queryParser.parse("mytatis is a apache project");
        System.out.println(query);
        printResult(getIndexSearcher(), query);
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章