Lucene基本使用和代碼實現

目錄

 

 

 

Lucene:全文檢索技術

一、Lucene的介紹

1.1背景

1.2優點

1.3Lucene的缺點

1.4全文檢索

二、Lucene的基本使用流程

2.1Lucene檢索過程

2.2獲取文檔

2.3分析文檔(分詞)

2.4創建索引

2.5查詢索引

三、Lucene具體實現

3.1下載

3.2實際開發要使用的jar包

3.3代碼實現

3.4使用Luke工具查看索引文件

3.5分析器

3.6索引庫的維護(增刪改)


 

 

Lucene:全文檢索技術

一、Lucene的介紹

1.1背景

Lucene是apache軟件基金會4 jakarta項目組的一個子項目,是一個開放源代碼的全文檢索引擎工具包,但它不是一個完整的全文檢索引擎,而是一個全文檢索引擎的架構,提供了完整的查詢引擎和索引引擎,部分文本分析引擎(英文與德文兩種西方語言)。Lucene的目的是爲軟件開發人員提供一個簡單易用的工具包,以方便的在目標系統中實現全文檢索的功能,或者是以此爲基礎建立起完整的全文檢索引擎。Lucene是一套用於全文檢索和搜尋的開源程式庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程式接口,能夠做全文索引和搜尋。在Java開發環境裏Lucene是一個成熟的免費開源工具。就其本身而言,Lucene是當前以及最近幾年最受歡迎的免費Java信息檢索程序庫。人們經常提到信息檢索程序庫,雖然與搜索引擎有關,但不應該將信息檢索程序庫與搜索引擎相混淆。

Lucene['lusen]的原作者是Doug Cutting,他是一位資深全文索引/檢索專家,曾經是V-Twin搜索引擎的主要開發者,後在Excite擔任高級系統架構設計師,當前從事於一些Internet底層架構的研究。早先發布在作者自己的博客上,他貢獻出Lucene的目標是爲各種中小型應用程式加入全文檢索功能。後來發佈在SourceForge,2001年年底成爲apache軟件基金會jakarta的一個子項目。

1.2優點

作爲一個開放源代碼項目,Lucene從問世之後,引發了開放源代碼社羣的巨大反響,程序員們不僅使用它構建具體的全文檢索應用,而且將之集成到各種系統軟件中去,以及構建Web應用,甚至某些商業軟件也採用了Lucene作爲其內部全文檢索子系統的核心。apache軟件基金會的網站使用了Lucene作爲全文檢索的引擎,IBM的開源軟件eclipse[9]的2.1版本中也採用了Lucene作爲幫助子系統的全文索引引擎,相應的IBM的商業軟件Web Sphere[10]中也採用了Lucene。Lucene以其開放源代碼的特性、優異的索引結構、良好的系統架構獲得了越來越多的應用。

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

(1)索引文件格式獨立於應用平臺。Lucene定義了一套以8位字節爲基礎的索引文件格式,使得兼容系統或者不同平臺的應用能夠共享建立的索引文件。

(2)在傳統全文檢索引擎的倒排索引的基礎上,實現了分塊索引,能夠針對新的文件建立小文件索引,提升索引速度。然後通過與原有索引的合併,達到優化的目的。

(3)優秀的面向對象的系統架構,使得對於Lucene擴展的學習難度降低,方便擴充新功能。

(4)設計了獨立於語言和文件格式的文本分析接口,索引器通過接受Token流完成索引文件的創立,用戶擴展新的語言和文件格式,只需要實現文本分析的接口。

(5)已經默認實現了一套強大的查詢引擎,用戶無需自己編寫代碼即可使系統可獲得強大的查詢能力,Lucene的查詢實現中默認實現了布爾操作、模糊查詢(Fuzzy Search[11])、分組查詢等等。

面對已經存在的商業全文檢索引擎,Lucene也具有相當的優勢。

首先,它的開發源代碼發行方式(遵守Apache Software License[12]),在此基礎上程序員不僅僅可以充分的利用Lucene所提供的強大功能,而且可以深入細緻的學習到全文檢索引擎製作技術和麪向對象編程的實踐,進而在此基礎上根據應用的實際情況編寫出更好的更適合當前應用的全文檢索引擎。在這一點上,商業軟件的靈活性遠遠不及Lucene。

其次,Lucene秉承了開放源代碼一貫的架構優良的優勢,設計了一個合理而極具擴充能力的面向對象架構,程序員可以在Lucene的基礎上擴充各種功能,比如擴充中文處理能力,從文本擴充到HTML、PDF[13]等等文本格式的處理,編寫這些擴展的功能不僅僅不復雜,而且由於Lucene恰當合理的對系統設備做了程序上的抽象,擴展的功能也能輕易的達到跨平臺的能力。

最後,轉移到apache軟件基金會後,藉助於apache軟件基金會的網絡平臺,程序員可以方便的和開發者、其它程序員交流,促成資源的共享,甚至直接獲得已經編寫完備的擴充功能。最後,雖然Lucene使用Java語言寫成,但是開放源代碼社區的程序員正在不懈的將之使用各種傳統語言實現(例如.net framework[14]),在遵守Lucene索引文件格式的基礎上,使得Lucene能夠運行在各種各樣的平臺上,系統管理員可以根據當前的平臺適合的語言來合理的選擇。

 

1.3Lucene的缺點

1、Lucene 的內建不支持羣集。 Lucene是作爲嵌入式的工具包的形式出現的,在覈心代碼上沒有提供對羣集的支持。實現對Lucene的羣集有三種方式:1、繼承實現一個 Directory;2、使用Solr 3、使用 Nutch+Hadoop;使用Solr你不得不用他的Index Server ,而使用Nutch你又不得不集成抓取的模塊;

2、區間範圍搜索速度非常緩慢; Lucene的區間範圍搜索,不是一開始就提供的是後來才加上的。對於在單個文檔中term出現比較多的情況,搜索速度會變得很慢。因此作者稱Lucene是一個高效的全文搜索引擎,其高效僅限於提供基本布爾查詢 boolean queries; 3、排序算法的實現不是可插拔的;

因爲貫穿Lucene的排序算法的tf/idf 的實現,儘管term是可以設置boost或者擴展Lucene的Query類,但是對於複雜的排序算法定製還是有很大的侷限性; 4、Lucene的結構設計不好; Lucene的OO設計的非常糟,儘管有包package和類class,但是Lucene的設計基本上沒有設計模式的身影。這是不是c或者c++程序員寫java程序的通病? A、Lucene中沒有使用接口Interface,比如Query 類( BooleanQuery, SpanQuery, TermQuery...) 大都是從超類中繼承下來的; B、Lucene的迭代實現不自然: 沒有hasNext() 方法, next() 返回一個布爾值 boolean然後刷新對象的上下文; 5、封閉設計的API使得擴展Lucene變得很困難; 參考第3點; 6、Lucene的搜索算法不適用於網格計算;

 

1.4全文檢索

1.4.1數據分類

我們生活中的數據總體分爲兩種:結構化數據和非結構化數據。

結構化數據:指具有固定格式或有限長度的數據,如數據庫,元數據等。

非結構化數據:指不定長或無固定格式的數據,如郵件,word文檔等磁盤上的文件。

1.4.2結構化數據搜索

常見的結構化數據也就是數據庫中的數據。在數據庫中搜索很容易實現,通常都是使用sql語句進行查詢,而且能很快的得到查詢結果。

爲什麼數據庫搜索很容易?

因爲數據庫中的數據存儲是有規律的,有行有列而且數據格式、數據長度都是固定的。

1.4.3非結構化數據搜索

(1)順序掃描法

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

(2)全文檢索

將非結構化數據中的一部分信息提取出來,重新組織,使其變得有一定結構,然後對此有一定結構的數據進行搜索,從而達到搜索相對較快的目的。這部分從非結構化數據中提取出的然後重新組織的信息,我們稱之索引

例如:字典。字典的拼音表和部首檢字表就相當於字典的索引,對每一個字的解釋是非結構化的,如果字典沒有音節表和部首檢字表,在茫茫辭海中找一個字只能順序掃描。然而字的某些信息可以提取出來進行結構化處理,比如讀音,就比較結構化,分聲母和韻母,分別只有幾種可以一一列舉,於是將讀音拿出來按一定的順序排列,每一項讀音都指向此字的詳細解釋的頁數。我們搜索時按結構化的拼音搜到讀音,然後按其指向的頁數,便可找到我們的非結構化數據——也即對字的解釋。

這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。

雖然創建索引的過程也是非常耗時的,但是索引一旦創建就可以多次使用,全文檢索主要處理的是查詢,所以耗時間創建索引是值得的。

 

1.4.4全文檢索應用

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

 

 

二、Lucene的基本使用流程

2.1Lucene檢索過程

 

2.2獲取文檔

獲取原始內容的目的是爲了索引,在索引前需要將原始內容創建成文檔(Document),文檔中包括一個一個的域(Field),域中存儲內容。

這裏我們可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱、file_path文件路徑、file_size文件大小、file_content文件內容),如下圖:

 

 注意:每個Document可以有多個Field,不同的Document可以有不同的Field,同一個Document可以有相同的Field(域名和域值都相同)

 

每個文檔都有一個唯一的編號,就是文檔id。

 

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。

 

2.4創建索引

注意:創建索引是對語彙單元索引,通過詞語找文檔,這種索引的結構叫倒排索引結構

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

傳統方法是根據文件找到該文件的內容,在文件內容中匹配搜索關鍵字,這種方法是順序掃描方法,數據量大、搜索慢。

倒排索引結構是根據內容(詞語)找文檔,如下圖:

 

 

2.5查詢索引

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

搜索索引過程:

  1. 根據查詢語法在倒排索引詞典表中分別找出對應搜索詞的索引,從而找到索引所鏈接的文檔鏈表。

  2. 比如搜索語法爲“fileName:lucene”表示搜索出fileName域中包含Lucene的文檔。

  3. 搜索過程就是在索引上查找域爲fileName,並且關鍵字爲Lucene的term,並根據term找到文檔id列表。

 

三、Lucene具體實現

3.1下載

可以去官網下載:Lucene:https://lucene.apache.org/

 

 

3.2實際開發要使用的jar包

lucene-analyzers-common-8.2.0.jar

lucene-core-8.2.0.jar

lucene-queryparser-8.2.0.jar

 

3.3代碼實現

準備好要搜索的原始文檔,本博主使用的是本機:

 

 

 



import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;


import java.io.File;

/**
 * 使用索引
 */
public class TestLucene1 {

    @Test
    /**
     * 創建索引
     * @throws Exception
     */
    public void createIndex() throws Exception{
        //1.創建一個目錄對象指定索引存放位置
        //把索引存放在內存
        //Directory directory=new RAMDirectory();
        //把索引存放在硬盤上
        Directory directory= FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath());
        //2.基於Directory對象創建一個IndexWriter對象
        IndexWriterConfig indexWriterConfig=new IndexWriterConfig(new IKAnalyzer());        //指定使用哪種分析器
        IndexWriter indexWriter=new IndexWriter(directory,indexWriterConfig);
        //3.讀取硬盤上的文件,對應每個文件創建一個文檔對象
        File fileDir=new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\Lucene\\02.參考資料\\searchsource");
        File[] files=fileDir.listFiles();
        for (File file:files
             ) {
            //讀取文件名
            String fileName=file.getName();
            //讀取路徑
            String filePath=file.getPath();
            //讀取文件內容
            String fileContent= FileUtils.readFileToString(file,"UTF-8");
            //讀取文件大小
            long fileSize=FileUtils.sizeOf(file);
            //創建Filed
            //參數:域的名稱、域的內容、是否存儲
            Field fieldName=new TextField("name",fileName,Field.Store.YES);
            //Field fieldPath=new TextField("path",filePath,Field.Store.YES);
            Field fieldPath=new StoredField("path",filePath);       //默認存儲
            Field fieldContent=new TextField("content",fileContent,Field.Store.YES);
            //Field fieldSize=new TextField("size",fileSize+"",Field.Store.YES);
            Field fieldSizeValue=new LongPoint("size",fileSize);    //只是作爲值使用
            Field fieldSizeStore=new StoredField("size",fileSize);  //存儲
            //創建文檔對象
            Document document=new Document();
            //向文檔對象中添加域
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSizeValue);
            document.add(fieldSizeStore);
            //5.把文檔對象寫入索引庫
            indexWriter.addDocument(document);
        }
        //6.關閉IndexWriter
        indexWriter.close();
    }

    /**
     * 查詢索引
     * @throws Exception
     */
    @Test
    public void searchIndex() throws Exception{
        //1.創建一個Directory對象,指定索引庫的位置
        Directory directory=FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath());
        //2.創建一個IndexReader對象
        IndexReader indexReader= DirectoryReader.open(directory);
        //3.創建一個IndexSearcher對象
        IndexSearcher indexSearcher=new IndexSearcher(indexReader);
        //4.創建一個Query對象
        Query query=new TermQuery(new Term("name","Lucene"));
        //5.執行查詢得到一個TopDocs對象
        //參數:查詢對象、返回最大記錄數
        TopDocs topDocs=indexSearcher.search(query,10);
        //6.取查詢結果總記錄數
        System.out.println("查詢結果總記錄數:"+topDocs.totalHits);
        //7.取文檔列表
        ScoreDoc[] scoreDocs=topDocs.scoreDocs;
        //8.打印文檔內容
        for (ScoreDoc doc:scoreDocs
             ) {
            //獲取文檔ID
            int docId=doc.doc;
            //根據ID獲取文檔對象
            Document document=indexSearcher.doc(docId);
            System.out.println("文件名:"+document.get("name"));
            System.out.println("文件路徑:"+document.get("path"));
            System.out.println("文件大小:"+document.get("size"));
            System.out.println("文件內容:"+document.get("content"));
            System.out.println("-------------------------");
        }
        indexReader.close();
    }
}

 

 

 

3.4使用Luke工具查看索引文件

 

 

 

3.5分析器

3.5.1標準分析器

  1. StandardAnalyzer:

    單字分詞:就是按照中文一個字一個字地進行分詞。如:“我愛中國”, 效果:“我”、“愛”、“中”、“國”。

  2. SmartChineseAnalyzer:

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

3.5.2中文分析器

IKAnalyzer

使用方法:

第一步:把jar包添加到工程中

第二步:把配置文件和擴展詞典和停用詞詞典添加到classpath下

 

注意:hotword.dic和ext_stopword.dic文件的格式爲UTF-8,注意是無BOM 的UTF-8 編碼。

也就是說禁止使用windows記事本編輯擴展詞典文件

 

 

代碼:

package com.xy;


/**
 * 中文分析器
 */

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class TestLucene2 {

    /**
     * 使用標準分析器
     * @throws Exception
     */
    @Test
    public void testTokenStream() throws Exception{
        //1.創建一個Analyzer對象,使用它的子類StandardAnalyzer對象
        Analyzer analyzer=new StandardAnalyzer();
        //2.使用分析器的tokenStream方法,獲得一個TokenStream對象
        TokenStream tokenStream=analyzer.tokenStream("","Apache是世界使用排名第一的Web服務器軟件。它可以運行在幾乎所有廣泛使用的計算機平臺上,由於其跨平臺和安全性被廣泛使用,是最流行的Web服務器端軟件之一。它快速、可靠並且可通過簡單的API擴充,將Perl/Python等解釋器編譯到服務器中。同時Apache音譯爲阿帕奇,是北美印第安人的一個部落,叫阿帕奇族,在美國的西南部。也是一個基金會的名稱、一種武裝直升機等等。");
        //3.向TokenStream對象中設置一個引用,相當於一個指針
        CharTermAttribute charTermAttribute=tokenStream.addAttribute(CharTermAttribute.class);
        //4.調用TokenStream對象的reset方法,如果不調用會拋出異常
        tokenStream.reset();
        //5.使用while循環遍歷TokenStream對象
        while (tokenStream.incrementToken()){
            System.out.println(charTermAttribute.toString());
        }
        //6.關閉TokenStream對象
        tokenStream.close();
    }

    /**
     * 使用中文分析器
     * @throws Exception
     */
    @Test
    public void testIkAnalyzer() throws Exception{
        //1.創建一個Analyzer對象,使用它的子類StandardAnalyzer對象
        Analyzer analyzer=new IKAnalyzer();
        //2.使用分析器的tokenStream方法,獲得一個TokenStream對象
        TokenStream tokenStream=analyzer.tokenStream("","Apache是世界使用排名第一的Web服務器軟件。它可以運行在幾乎所有廣泛使用的計算機平臺上,由於其跨平臺和安全性被廣泛使用,是最流行的Web服務器端軟件之一。它快速、可靠並且可通過簡單的API擴充,將Perl/Python等解釋器編譯到服務器中。同時Apache音譯爲阿帕奇,是北美印第安人的一個部落,叫阿帕奇族,在美國的西南部。也是一個基金會的名稱、一種武裝直升機等等。");
        //3.向TokenStream對象中設置一個引用,相當於一個指針
        CharTermAttribute charTermAttribute=tokenStream.addAttribute(CharTermAttribute.class);
        //4.調用TokenStream對象的reset方法,如果不調用會拋出異常
        tokenStream.reset();
        //5.使用while循環遍歷TokenStream對象
        while (tokenStream.incrementToken()){
            System.out.println(charTermAttribute.toString());
        }
        //6.關閉TokenStream對象
        tokenStream.close();
    }
}

3.6索引庫的維護(增刪改)

3.6.1Field域的屬性

是否分析:是否對域的內容進行分詞處理。前提是我們要對域的內容進行查詢。

是否索引:將Field分析後的詞或整個Field值進行索引,只有索引方可搜索到。

比如:商品名稱、商品簡介分析後進行索引,訂單號、身份證號不用分析但也要索引,這些將來都要作爲查詢條件。

是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field纔可以從Document中獲取

比如:商品名稱、訂單號,凡是將來要從Document中獲取的Field都要存儲。

 

 

3.6.2添加文檔代碼



import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * 維護索引
 */
public class TestLucene3 {

    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws IOException {
        indexReader= DirectoryReader.open(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()));
        indexSearcher=new IndexSearcher(indexReader);
    }
    /**
     * 聲明IndexWriter對象
     */
    @Before
    public void indexWriter() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
       indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
    }
    /**
     * 添加文檔
     */
    @Test
    public void addDocument() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
        IndexWriter indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
        //2.創建一個Document對象
        Document document=new Document();
        //3.向文檔中添加域
        document.add(new TextField("name","新添加的文件", Field.Store.YES));
        document.add(new TextField("content","新添加的文件內容", Field.Store.NO));
        document.add(new StoredField("path","D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\temp"));
        //4.把文檔寫入索引庫
        indexWriter.addDocument(document);
        //5.關閉索引庫
        indexWriter.close();
    }

    
}

3.6.3刪除索引庫

package com.xy;

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * 維護索引
 */
public class TestLucene3 {

    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws IOException {
        indexReader= DirectoryReader.open(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()));
        indexSearcher=new IndexSearcher(indexReader);
    }
    /**
     * 聲明IndexWriter對象
     */
    @Before
    public void indexWriter() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
       indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
    }


    /**
     * 刪除文檔
     */
    @Test
    public void deleteDocument() throws IOException {
        //刪除全部文檔
        indexWriter.deleteAll();
        //關閉索引庫
        indexWriter.close();
    }

    /**
     * 通過查詢刪除文檔
     * @throws IOException
     */
    @Test
    public void deleteByQuery() throws IOException {
        indexWriter.deleteDocuments(new Term("name","Lucene"));
    }
    @Test
    public void updateDocument() throws IOException {
        //創建一個新的文檔對象
        Document document=new Document();
        //向文檔中添加域
        document.add(new TextField("name","更新之後的文檔",Field.Store.YES));
        document.add(new TextField("name1","更新之後的文檔2",Field.Store.YES));
        document.add(new TextField("name2","更新之後的文檔3",Field.Store.YES));
        //更新操作
        indexWriter.updateDocument(new Term("name","Lucene"),document);
        //關閉索引庫
        indexWriter.close();
    }

    private  void printResults(Query query) throws IOException {
        //執行查詢
        TopDocs topDocs=indexSearcher.search(query,10);
        System.out.println("總記錄數:"+topDocs.totalHits);
        ScoreDoc[] scoreDocs=topDocs.scoreDocs;
        //打印文檔內容
        for (ScoreDoc doc:scoreDocs
        ) {
            //獲取文檔ID
            int docId=doc.doc;
            //根據ID獲取文檔對象
            Document document=indexSearcher.doc(docId);
            System.out.println("文件名:"+document.get("name"));
            System.out.println("文件路徑:"+document.get("path"));
            System.out.println("文件大小:"+document.get("size"));
            System.out.println("文件內容:"+document.get("content"));
            System.out.println("-------------------------");
        }
        indexReader.close();
    }

}

3.6.4索引庫的修改

package com.xy;

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * 維護索引
 */
public class TestLucene3 {

    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws IOException {
        indexReader= DirectoryReader.open(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()));
        indexSearcher=new IndexSearcher(indexReader);
    }
    /**
     * 聲明IndexWriter對象
     */
    @Before
    public void indexWriter() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
       indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
    }
  

    @Test
    public void updateDocument() throws IOException {
        //創建一個新的文檔對象
        Document document=new Document();
        //向文檔中添加域
        document.add(new TextField("name","更新之後的文檔",Field.Store.YES));
        document.add(new TextField("name1","更新之後的文檔2",Field.Store.YES));
        document.add(new TextField("name2","更新之後的文檔3",Field.Store.YES));
        //更新操作
        indexWriter.updateDocument(new Term("name","Lucene"),document);
        //關閉索引庫
        indexWriter.close();
    }

 
}

3.6.5索引庫的查詢

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

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

1)使用Lucene提供Query子類

2)使用QueryParse解析查詢表達式

 

1.TermQuery

TermQuery,通過項查詢,TermQuery不使用分析器所以建議匹配不分詞的Field域查詢,比如訂單號、分類ID號等。

指定要查詢的域和要查詢的關鍵詞。

 

2.數值範圍查詢

code:

package com.xy;

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * 維護索引
 */
public class TestLucene3 {

    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws IOException {
        indexReader= DirectoryReader.open(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()));
        indexSearcher=new IndexSearcher(indexReader);
    }
    /**
     * 聲明IndexWriter對象
     */
    @Before
    public void indexWriter() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
       indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
    }


    private  void printResults(Query query) throws IOException {
        //執行查詢
        TopDocs topDocs=indexSearcher.search(query,10);
        System.out.println("總記錄數:"+topDocs.totalHits);
        ScoreDoc[] scoreDocs=topDocs.scoreDocs;
        //打印文檔內容
        for (ScoreDoc doc:scoreDocs
        ) {
            //獲取文檔ID
            int docId=doc.doc;
            //根據ID獲取文檔對象
            Document document=indexSearcher.doc(docId);
            System.out.println("文件名:"+document.get("name"));
            System.out.println("文件路徑:"+document.get("path"));
            System.out.println("文件大小:"+document.get("size"));
            System.out.println("文件內容:"+document.get("content"));
            System.out.println("-------------------------");
        }
        indexReader.close();
    }

    /**
     * 測試查詢
     */
    @Test
    public void searchDocument() throws IOException {
        //創建一個Query對象
        Query query= LongPoint.newRangeQuery("size",0l,10000l);
        //查詢結果
        printResults(query);
    }

  
}

 

3.QueryParser查詢

通過QueryParser也可以創建Query,QueryParser提供一個Parse方法,此方法可以直接根據查詢語法來查詢。Query對象執行的查詢語法可通過System.out.println(query);查詢。

需要使用到分析器。建議創建索引時使用的分析器和查詢索引時使用的分析器要一致。

需要加入queryParser依賴的jar包。

lucene-queryparser-8.2.0.jar

code:

package com.xy;

import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;
import java.io.IOException;

/**
 * 維護索引
 */
public class TestLucene3 {

    private IndexWriter indexWriter;
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;
    @Before
    public void init() throws IOException {
        indexReader= DirectoryReader.open(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()));
        indexSearcher=new IndexSearcher(indexReader);
    }
    /**
     * 聲明IndexWriter對象
     */
    @Before
    public void indexWriter() throws IOException {
        //1.創建一個IndexWriter對象,指定分析所用的分析器
       indexWriter=new IndexWriter(FSDirectory.open(new File("D:\\Java\\2018JavaEE傳智播客IDEA版\\階段4 1.Lucene\\index").toPath()),
                new IndexWriterConfig(new IKAnalyzer()));
    }
 

    private  void printResults(Query query) throws IOException {
        //執行查詢
        TopDocs topDocs=indexSearcher.search(query,10);
        System.out.println("總記錄數:"+topDocs.totalHits);
        ScoreDoc[] scoreDocs=topDocs.scoreDocs;
        //打印文檔內容
        for (ScoreDoc doc:scoreDocs
        ) {
            //獲取文檔ID
            int docId=doc.doc;
            //根據ID獲取文檔對象
            Document document=indexSearcher.doc(docId);
            System.out.println("文件名:"+document.get("name"));
            System.out.println("文件路徑:"+document.get("path"));
            System.out.println("文件大小:"+document.get("size"));
            System.out.println("文件內容:"+document.get("content"));
            System.out.println("-------------------------");
        }
        indexReader.close();
    }



    @Test
    public void searchDocumentByQueryParse() throws ParseException, IOException {
        //創建一個QueryParse對象
        //參數:默認搜索域、分析器對象
        QueryParser queryParser=new QueryParser("name",new IKAnalyzer());
        //使用後一個QueryParse對象創建一個Query對象
        Query query=queryParser.parse("Spring框架");
        //執行查詢
        printResults(query);
    }
}

 

 

 

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