Lucene—全文檢索

1. 案例分析:什么時全文檢索左胞,如何實現(xiàn)全文檢索

? 1.1 案例

? 實現(xiàn)一個文件的搜索功能杆怕,通過關(guān)鍵字搜索文件,凡是文件名或文件內(nèi)容包括關(guān)鍵字的文件都需要找出來韩脑。還可以根據(jù)中文詞語進行查詢劫谅,并且需要支持多個條件查詢见坑。

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

2. 需求分析

? 2.1. 數(shù)據(jù)庫搜索

數(shù)據(jù)庫中的搜索很容易實現(xiàn)捏检,通常都是使用sql語句進行查詢荞驴,而且能很快的得到查詢結(jié)果。
為什么數(shù)據(jù)庫搜索很容易贯城?
因為數(shù)據(jù)庫中的數(shù)據(jù)存儲是有規(guī)律的熊楼,有行有列而且數(shù)據(jù)格式、數(shù)據(jù)長度都是固定的冤狡。

? 2.2. 數(shù)據(jù)分類

我們生活中的數(shù)據(jù)總體分為兩種:結(jié)構(gòu)化數(shù)據(jù)和非結(jié)構(gòu)化數(shù)據(jù)孙蒙。
結(jié)構(gòu)化數(shù)據(jù):指具有固定格式或有限長度的數(shù)據(jù),如數(shù)據(jù)庫悲雳,元數(shù)據(jù)等挎峦。
非結(jié)構(gòu)化數(shù)據(jù):指不定長或無固定格式的數(shù)據(jù),如郵件合瓢,word文檔等磁盤上的文件

2.3. 非結(jié)構(gòu)化數(shù)據(jù)查詢方法

(1)順序掃描法(Serial Scanning)
? 所謂順序掃描坦胶,比如要找內(nèi)容包含某一個字符串的文件,就是一個文檔一個文檔的看晴楔,對于每一個文檔顿苇,從頭看到尾,如果此文檔包含此字符串税弃,則此文檔為我們要找的文件纪岁,接著看下一個文件,直到掃描完所有的文件则果。如利用windows的搜索也可以搜索文件內(nèi)容幔翰,只是相當?shù)穆?/p>

(2)全文檢索(Full-text Search)
? 將非結(jié)構(gòu)化數(shù)據(jù)中的一部分信息提取出來,重新組織西壮,使其變得有一定結(jié)構(gòu)遗增,然后對此有一定結(jié)構(gòu)的數(shù)據(jù)進行搜索,從而達到搜索相對較快的目的款青。這部分從非結(jié)構(gòu)化數(shù)據(jù)中提取出的然后重新組織的信息做修,我們稱之索引。
例如:字典抡草。字典的拼音表和部首檢字表就相當于字典的索引饰及,對每一個字的解釋是非結(jié)構(gòu)化的,如果字典沒有音節(jié)表和部首檢字表渠牲,在茫茫辭海中找一個字只能順序掃描旋炒。然而字的某些信息可以提取出來進行結(jié)構(gòu)化處理,比如讀音签杈,就比較結(jié)構(gòu)化瘫镇,分聲母和韻母,分別只有幾種可以一一列舉答姥,于是將讀音拿出來按一定的順序排列铣除,每一項讀音都指向此字的詳細解釋的頁數(shù)。我們搜索時按結(jié)構(gòu)化的拼音搜到讀音鹦付,然后按其指向的頁數(shù)尚粘,便可找到我們的非結(jié)構(gòu)化數(shù)據(jù)——也即對字的解釋。
這種先建立索引敲长,再對索引進行搜索的過程就叫全文檢索(Full-text Search)郎嫁。
雖然創(chuàng)建索引的過程也是非常耗時的秉继,但是索引一旦創(chuàng)建就可以多次使用,全文檢索主要處理的是查詢泽铛,所以耗時間創(chuàng)建索引是值得的尚辑。

? 2.4. 如何實現(xiàn)全文檢索

可以使用Lucene實現(xiàn)全文檢索。Lucene是apache下的一個開放源代碼的全文檢索引擎工具包盔腔。提供了完整的查詢引擎和索引引擎杠茬,部分文本分析引擎。Lucene的目的是為軟件開發(fā)人員提供一個簡單易用的工具包弛随,以方便的在目標系統(tǒng)中實現(xiàn)全文檢索的功能瓢喉。

? 2.5. 全文檢索的應(yīng)用場景

對于數(shù)據(jù)量大、數(shù)據(jù)結(jié)構(gòu)不固定的數(shù)據(jù)可采用全文檢索方式搜索舀透,比如百度栓票、Google等搜索引擎、論壇站內(nèi)搜索盐杂、電商網(wǎng)站站內(nèi)搜索等逗载。

3. Lucene實現(xiàn)全文檢索的流程

3.1.索引和搜索流程圖
  1. 綠色表示索引過程,對要搜索的原始內(nèi)容進行索引構(gòu)建一個索引庫链烈,索引過程包括:
    確定原始內(nèi)容即要搜索的內(nèi)容 -> 采集文檔 -> 創(chuàng)建文檔 -> 分析文檔 -> 索引文檔

  2. 紅色表示搜索過程厉斟,從索引庫中搜索內(nèi)容,搜索過程包括:
    用戶通過搜索界面 -> 創(chuàng)建查詢 -> 執(zhí)行搜索强衡,從索引庫搜索 -> 渲染搜索結(jié)果

3.2. 創(chuàng)建索引

創(chuàng)建索引步驟:
? 1. 獲得原始文檔
? 2. 創(chuàng)建文檔對象
? 3. 分析文檔
? 4. 創(chuàng)建索引
? 對文檔索引的過程擦秽,將用戶要搜索的文檔內(nèi)容進行索引,索引存儲在索引庫(index)中漩勤。
這里我們要搜索的文檔是磁盤上的文本文件感挥,根據(jù)案例描述:凡是文件名或文件內(nèi)容包括關(guān)鍵字的文件都要找出來,這里要對文件名和文件內(nèi)容創(chuàng)建索引越败。

? 3.2.1 獲得全部文檔
? 原始文檔是指要索引和搜索的內(nèi)容触幼。原始內(nèi)容包括互聯(lián)網(wǎng)上的網(wǎng)頁、數(shù)據(jù)庫中的數(shù)據(jù)究飞、磁盤上的文件等置谦。
本案例中的原始內(nèi)容就是磁盤上的文件,如下圖:

? 從互聯(lián)網(wǎng)上亿傅、數(shù)據(jù)庫媒峡、文件系統(tǒng)中等獲取需要搜索的原始信息,這個過程就是信息采集葵擎,信息采集的目的是為了對原始內(nèi)容進行索引谅阿。
? 在Internet上采集信息的軟件通常稱為爬蟲或蜘蛛,也稱為網(wǎng)絡(luò)機器人,爬蟲訪問互聯(lián)網(wǎng)上的每一個網(wǎng)頁签餐,將獲取到的網(wǎng)頁內(nèi)容存儲起來寓涨。
? Lucene不提供信息采集的類庫,需要自己編寫一個爬蟲程序?qū)崿F(xiàn)信息采集氯檐,也可以通過一些開源軟件實現(xiàn)信息采集缅茉,如下:

開源軟件
Nutch(http://lucene.apache.org/nutch), Nutch是apache的一個子項目,包括大規(guī)模爬蟲工具男摧,能夠抓取和分辨web網(wǎng)站數(shù)據(jù)
jsoup(http://jsoup.org/ ),jsoup 是一款Java 的HTML解析器译打,可直接解析某個URL地址耗拓、HTML文本內(nèi)容。它提供了一套非常省力的API奏司,可通過DOM乔询,CSS以及類似于jQuery的操作方法來取出和操作數(shù)據(jù)
heritrix(http://sourceforge.net/projects/archive-crawler/files/),Heritrix 是一個由 java 開發(fā)的韵洋、開源的網(wǎng)絡(luò)爬蟲竿刁,用戶可以使用它來從網(wǎng)上抓取想要的資源。其最出色之處在于它良好的可擴展性搪缨,方便用戶實現(xiàn)自己的抓取邏輯

? 本案例我們要獲取磁盤上文件的內(nèi)容食拜,可以通過文件流來讀取文本文件的內(nèi)容,對于pdf副编、doc负甸、xls等文件可通過第三方提供的解析工具讀取文件內(nèi)容,比如Apache POI讀取doc和xls的文件內(nèi)容痹届。

? 3.2.2 創(chuàng)建文檔對象
? 獲取原始內(nèi)容的目的是為了索引呻待,在索引前需要將原始內(nèi)容創(chuàng)建成文檔(Document),文檔中包括一個一個的域(Field)队腐,域中存儲內(nèi)容蚕捉。
? 這里我們可以將磁盤上的一個文件當成一個document,Document中包括一些Field(file_name文件名稱柴淘、file_path文件路徑迫淹、file_size文件大小、file_content文件內(nèi)容)悠就,如下圖:

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

? 3.2.3 分析文檔
? 將原始內(nèi)容創(chuàng)建為包含域(Field)的文檔(document),需要再對域中的內(nèi)容進行分析,分析的過程是經(jīng)過對原始文檔提取單詞瑞妇、將字母轉(zhuǎn)為小寫稿静、去除標點符號、去除停用詞等過程生成最終的語匯單元辕狰,可以將語匯單元理解為一個一個的單詞改备。

? 比如下邊的文檔經(jīng)過分析如下:
? 原文檔內(nèi)容:

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中包含兩部分一部分是文檔的域名,另一部分是單詞的內(nèi)容聚谁。

例如:文件名中包含apache和文件內(nèi)容中包含的apache是不同的term母剥。

? 3.2.4 創(chuàng)建索引
? 對所有文檔分析得出的語匯單元進行索引,索引的目的是為了搜索形导,最終要實現(xiàn)只搜索被索引的語匯單元從而找到Document(文檔)环疼。

注意:創(chuàng)建索引是對語匯單元索引,通過詞語找文檔朵耕,這種索引的結(jié)構(gòu)叫倒排索引結(jié)構(gòu)炫隶。
傳統(tǒng)方法是根據(jù)文件找到該文件的內(nèi)容,在文件內(nèi)容中匹配搜索關(guān)鍵字阎曹,這種方法是順序掃描方法等限,數(shù)據(jù)量大、搜索慢芬膝。
倒排索引結(jié)構(gòu)是根據(jù)內(nèi)容(詞語)找文檔望门,如下圖:

倒排索引結(jié)構(gòu)

倒排索引結(jié)構(gòu)也叫反向索引結(jié)構(gòu),包括索引和文檔兩部分锰霜,索引即詞匯表筹误,它的規(guī)模較小,而文檔集合較大癣缅。

3.3 查詢索引

? 查詢索引也是搜索的過程厨剪。搜索就是用戶輸入關(guān)鍵字,從索引(index)中進行搜索的過程友存。根據(jù)關(guān)鍵字搜索索引祷膳,根據(jù)索引找到對應(yīng)的文檔,從而找到要搜索的內(nèi)容(這里指磁盤上的文件)屡立。

? 3.3.1. 用戶查詢接口
?全文檢索系統(tǒng)提供用戶搜索的界面供用戶提交搜索的關(guān)鍵字直晨,搜索完成展示搜索結(jié)果。

? 比如:

? Lucene不提供制作用戶搜索界面的功能,需要根據(jù)自己的需求開發(fā)搜索界面勇皇。

? 3.3.2. 創(chuàng)建查詢
? 用戶輸入查詢關(guān)鍵字執(zhí)行搜索之前需要先構(gòu)建一個查詢對象罩句,查詢對象中可以指定查詢要搜索的Field文檔域、查詢關(guān)鍵字等敛摘,查詢對象會生成具體的查詢語法门烂,
? 例如:
? 語法“fileName:lucene”表示要搜索Field域的內(nèi)容為“l(fā)ucene”的文檔

? 3.3.3. 執(zhí)行查詢

  • 搜索索引過程:
    根據(jù)查詢語法在倒排索引詞典表中分別找出對應(yīng)搜索詞的索引,從而找到索引所鏈接的文檔鏈表兄淫。
    比如搜索語法為“fileName:lucene”表示搜索出fileName域中包含Lucene的文檔屯远。
    搜索過程就是在索引上查找域為fileName,并且關(guān)鍵字為Lucene的term捕虽,并根據(jù)term找到文檔id列表氓润。

? 3.3.4. 渲染結(jié)果

  • 以一個友好的界面將查詢結(jié)果展示給用戶,用戶根據(jù)搜索結(jié)果找自己想要的信息薯鳍,為了幫助用戶很快找到自己的結(jié)果,提供了很多展示的效果挨措,比如搜索結(jié)果中將關(guān)鍵字高亮顯示挖滤,百度提供的快照等。


4. 配置環(huán)境開發(fā)

4.1. Lucene下載

Lucene是開發(fā)全文檢索功能的工具包浅役,從官方網(wǎng)站下載Lucene4.10.3斩松,并解壓。
官方網(wǎng)站: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. 功能一:創(chuàng)建索引庫

使用 IndexWriter 對象創(chuàng)建

5.1 實現(xiàn)步驟

1. 創(chuàng)建一個java工程觉既,并導入jar包惧盹。
2. 創(chuàng)建一個indexwriter對象。
? 2.1 指定索引庫的存放位置Directory對象
? 2.2 指定一個分析器瞪讼,對文檔內(nèi)容進行分析钧椰。
3. 創(chuàng)建document對象。
4. 創(chuàng)建field對象符欠,將field添加到document對象中嫡霞。
5. 使用indexwriter對象將document對象寫入索引庫,此過程進行索引創(chuàng)建希柿。并將索引和document對象寫入索引庫诊沪。
6. 關(guān)閉IndexWriter對象。

5.2. Field域的屬性

是否分析:是否對域的內(nèi)容進行分詞處理曾撤。前提是我們要對域的內(nèi)容進行查詢端姚。
是否索引:將Field分析后的詞或整個Field值進行索引,只有索引方可搜索到挤悉。
比如:商品名稱渐裸、商品簡介分析后進行索引,訂單號、身份證號不用分析但也要索引橄仆,這些將來都要作為查詢條件剩膘。
是否存儲:將Field值存儲在文檔中,存儲在文檔中的Field才可以從Document中獲取
比如:商品名稱盆顾、訂單號怠褐,凡是將來要從Document中獲取的Field都要存儲。

是否存儲的標準:是否要將內(nèi)容展示給用戶

Field類 數(shù)據(jù)類型 Analyzed是否分析 Indexed是否索引 Stored是否存儲 說明
StringField(FieldName, FieldValue,Store.YES) 字符串 N Y Y或N 這個Field用來構(gòu)建一個字符串Field您宪,但是不會進行分析奈懒,會將整個串存儲在索引中,比如(訂單號,姓名等)
是否存儲在文檔中用Store.YES或Store.NO決定
LongField(FieldName, FieldValue,Store.YES) Long型 Y Y Y或N 這個Field用來構(gòu)建一個Long數(shù)字型Field宪巨,進行分析和索引磷杏,比如(價格)
是否存儲在文檔中用Store.YES或Store.NO決定
StoredField(FieldName, FieldValue) 重載方法,支持多種類型 N N Y 這個Field用來構(gòu)建不同類型Field不分析捏卓,不索引极祸,但要Field存儲在文檔中
TextField(FieldName, FieldValue, Store.NO)或TextField(FieldName, reader) 字符串/流 Y Y Y或N 如果是一個Reader, lucene猜測內(nèi)容比較多,會采用Unstored的策略
5.3 代碼實現(xiàn):創(chuàng)建索引庫
@Test
    public void createIndex() throws Exception {
        // 1.指定索引庫存放的位置,可以是內(nèi)存也可以是磁盤
        // Directory directory = new RAMDirectory();//保存到內(nèi)存中一般不用
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));

        // 2.創(chuàng)建一個IndexWriter對象怠晴,需要一個分析器對象遥金。
        //Analyzer analyzer = new StandardAnalyzer();// 分析器
        IKAnalyzer analyzer = new IKAnalyzer();

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

        // 參數(shù)1:索引庫存放的路徑蒜田,參數(shù)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();
            // 文件內(nèi)容
            String fileContent = FileUtils.readFileToString(f);
            // 文件的大小
            long fileSize = FileUtils.sizeOf(f);
            // 4.創(chuàng)建文檔對象
            Document document = new Document();
            // 5.創(chuàng)建域
            // 參數(shù)1:域的名稱 冲粤。參數(shù)2:域的內(nèi)容 美莫。參數(shù)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.關(guān)閉IndexWriter對象
        indexWriter.close();
    }

6. 功能二:查詢索引

6.1 實現(xiàn)步驟

1. 創(chuàng)建一個Directory對象,也就是索引庫存放的位置梯捕。
2. 創(chuàng)建一個indexReader對象厢呵,需要指定Directory對象。
3. 創(chuàng)建一個indexsearcher對象傀顾,需要指定IndexReader對象
4. 創(chuàng)建一個TermQuery對象述吸,指定查詢的域和查詢的關(guān)鍵詞。
5. 執(zhí)行查詢锣笨。
6. 返回查詢結(jié)果蝌矛。遍歷查詢結(jié)果并輸出。
7. 關(guān)閉IndexReader對象

6.2 IndexSearcher 搜索方法
方法 說明
indexSearcher.search(query, n) 根據(jù)Query搜索错英,返回評分最高的n條記錄
indexSearcher.search(query, filter, n) 根據(jù)Query搜索入撒,添加過濾策略,返回評分最高的n條記錄
indexSearcher.search(query,n, sort) 根據(jù)Query搜索椭岩,添加排序策略茅逮,返回評分最高的n條記錄
indexSearcher.search(booleanQuery, filter,n, sort) 根據(jù)Query搜索璃赡,添加過濾策略,添加排序策略献雅,返回評分最高的n條記錄
6.3 代碼實現(xiàn)
    /**
     * 查詢索引庫
     * @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.創(chuàng)建一個IndexSearcher對象碉考,構(gòu)造方法需要一個IndexReader對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        // 4.創(chuàng)建一個查詢對象,需要指定查詢域及要查詢的關(guān)鍵子
        // term的參數(shù)1:要搜索的域 參數(shù)2:要搜索的關(guān)鍵字
        Query query = new TermQuery(new Term("name", "apache"));
        // 參數(shù)1:查詢條件 參數(shù)2:返回的結(jié)果數(shù)量
        // 5.取查詢結(jié)果
        TopDocs topDocs = indexSearcher.search(query, 10);
        // 取查詢結(jié)果總記錄數(shù)
        System.out.println("查詢結(jié)果總記錄數(shù):" + topDocs.totalHits);
        // 6.遍歷查詢結(jié)果并打印
        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.關(guān)閉IndexReaer對象
        indexReader.close();
    }
6.4 TopDocs對象

Lucene搜索結(jié)果可通過TopDocs遍歷挺身,TopDocs類提供了少量的屬性侯谁,如下:

方法或?qū)傩?/th> 說明
totalHits 匹配搜索條件的總記錄數(shù)
scoreDocs 頂部匹配記錄

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


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

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

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

從一個Reader字符流開始章钾,創(chuàng)建一個基于Reader的Tokenizer分詞器墙贱,經(jīng)過三個TokenFilter生成語匯單元Token。
要看分析器的分析效果贱傀,只需要看Tokenstream中的內(nèi)容就可以了惨撇。每個分析器都有一個方法tokenStream,返回一個tokenStream對象府寒。

7.2 分析器的分詞效果
//查看標準分析器的分詞效果
    public void testTokenStream() throws Exception {
        //創(chuàng)建一個標準分析器對象
        Analyzer analyzer = new StandardAnalyzer();
        //獲得tokenStream對象
        //第一個參數(shù):域名魁衙,可以隨便給一個
        //第二個參數(shù):要分析的文本內(nèi)容
        TokenStream tokenStream = analyzer.tokenStream("test", "The Spring Framework provides a comprehensive programming and configuration model.");
        //添加一個引用,可以獲得每個關(guān)鍵詞
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        //添加一個偏移量的引用株搔,記錄了關(guān)鍵詞的開始位置以及結(jié)束位置
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        //將指針調(diào)整到列表的頭部
        tokenStream.reset();
        //遍歷關(guān)鍵詞列表剖淀,通過incrementToken方法判斷列表是否結(jié)束
        while(tokenStream.incrementToken()) {
            //關(guān)鍵詞的起始位置
            System.out.println("start->" + offsetAttribute.startOffset());
            //取關(guān)鍵詞
            System.out.println(charTermAttribute);
            //結(jié)束位置
            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
? 輸入關(guān)鍵字進行搜索,當需要讓該關(guān)鍵字與文檔域內(nèi)容所包含的詞進行匹配時需要對文檔域內(nèi)容進行分析门驾,需要經(jīng)過Analyzer分析器處理生成語匯單元(Token)射赛。分析器分析的對象是文檔中的Field域。當Field的屬性tokenized(是否分詞)為true時會對Field值進行分析奶是,如下圖:

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

  1. 不作為查詢條件的內(nèi)容楣责,比如文件路徑
  2. 不是匹配內(nèi)容中的詞而匹配Field的整體內(nèi)容,比如訂單號聂沙、身份證號等秆麸。

? 7.4.2 搜索時使用Analyzer
? 對搜索關(guān)鍵字進行分析和索引分析一樣,使用Analyzer對搜索關(guān)鍵字進行分析及汉、分詞處理沮趣,使用分析后每個詞語進行搜索。比如:搜索關(guān)鍵字:spring web 坷随,經(jīng)過分析器進行分詞房铭,得出:spring web拿詞去索引詞典表查找 ,找到索引鏈接到Document温眉,解析Document內(nèi)容缸匪。
對于匹配整體Field域的查詢可以在搜索時不分析,比如根據(jù)訂單號、身份證號查詢等。
注意:搜索使用的分析器要和索引使用的分析器一致闲礼。


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

8.1 索引庫的添加

? 8.1.1. 步驟
? 向索引庫中添加document對象。
? 1. 先創(chuàng)建一個indexwriter對象
? 2. 創(chuàng)建一個document對象
? 3. 把document對象寫入索引庫
? 4. 關(guān)閉indexwriter砂心。

? 8.1.2. 代碼實現(xiàn)

    //添加索引
    @Test
    public void addDocument() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創(chuàng)建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創(chuàng)建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創(chuàng)建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //創(chuàng)建文檔
        Document document = new Document();
        //創(chuàng)建域
        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", "新添加的文檔的內(nèi)容", Store.NO));
        document.add(new TextField("content", "新添加的文檔的內(nèi)容第二個content", Store.YES));
        document.add(new TextField("content1", "新添加的文檔的內(nèi)容要能看到", 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"));
        //創(chuàng)建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創(chuàng)建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創(chuàng)建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //刪除全部索引
        indexWriter.deleteAll();
        //關(guān)閉indexwriter
        indexWriter.close();
    }

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

? 8.2.2. 指定查詢條件刪除

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

原理就是先刪除后添加

    //修改索引庫
    @Test
    public void updateIndex() throws Exception {
        //指定索引庫位置
        Directory directory = FSDirectory.open(new File("c:\\tem\\index"));
        //創(chuàng)建分詞分析器
        IKAnalyzer analyzer = new IKAnalyzer();
        //創(chuàng)建IndexWriter的config
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        //創(chuàng)建IndexWriter
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //創(chuàng)建一個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 的全文信息檢索工具包,它不是一個完整的搜索應(yīng)用程序,而是為你的應(yīng)用程序提供索引和搜索功能褐桌。", Store.YES));
        indexWriter.updateDocument(new Term("content", "java"), document);
        //關(guān)閉indexWriter
        indexWriter.close();
    }


9. Lucene索引庫查詢

Lucene的查詢:

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

? 對要搜索的信息創(chuàng)建Query查詢對象,Lucene會根據(jù)Query查詢對象生成最終的查詢語法象迎,類似關(guān)系數(shù)據(jù)庫Sql語法一樣Lucene也有自己的查詢語法荧嵌,比如:“name:lucene”表示查詢Fieldname“l(fā)ucene”的文檔信息。

可通過兩種方法創(chuàng)建查詢對象:

  1. 使用Lucene提供Query子類
    Query是一個抽象類砾淌,lucene提供了很多查詢對象啦撮,比如TermQuery項精確查詢,NumericRangeQuery數(shù)字范圍查詢等汪厨。
    如下代碼:
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"));
        //創(chuàng)建一個IndexReader對象
        DirectoryReader indexReader = DirectoryReader.open(directory);
        //創(chuàng)建IndexSearcher對象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //創(chuàng)建一個Query對象
        Query query = new MatchAllDocsQuery();
        System.out.println(query);//*:*
        //查詢索引庫獲取查詢結(jié)果
        TopDocs topDocs = indexSearcher.search(query, 100);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        System.out.println("查詢結(jié)果總記錄數(shù):"+topDocs.totalHits);
        //遍歷查詢結(jié)果
        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"));
        }
        //關(guān)閉索引庫
        indexReader.close();
    }

? 9.1.2 TermQuery 指定要查詢的域和要查詢的關(guān)鍵詞

//使用Termquery查詢
    @Test
    public void testTermQuery() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        //創(chuàng)建查詢對象
        Query query = new TermQuery(new Term("content", "lucene"));
        //執(zhí)行查詢
        TopDocs topDocs = indexSearcher.search(query, 10);
        //共查詢到的document個數(shù)
        System.out.println("查詢結(jié)果總數(shù)量:" + topDocs.totalHits);
        //遍歷查詢結(jié)果
        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"));
        }
        //關(guān)閉indexreader
        indexSearcher.getIndexReader().close();
    }

? 9.1.3 NumericRangeQuery 可以根據(jù)數(shù)值范圍查詢

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

? 9.1.3 BooleanQuery 可可以組合查詢條件

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

Occur.MUST:必須滿足此條件劫乱,相當于and
Occur.SHOULD:應(yīng)該滿足织中,但是不滿足也可以,相當于or
Occur.MUST_NOT:必須不滿足衷戈。相當于not

9.2 使用queryparser查詢

? 通過QueryParser也可以創(chuàng)建Query狭吼,QueryParser提供一個Parse方法,此方法可以直接根據(jù)查詢語法來查詢殖妇。Query對象執(zhí)行的查詢語法可通過System.out.println(query);查詢刁笙。
需要使用到分析器。建議創(chuàng)建索引時使用的分析器和查詢索引時使用的分析器要一致谦趣。

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

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

查詢語法

  1. 基礎(chǔ)的查詢語法磅氨,關(guān)鍵詞查詢:
    域名+“:”+搜索的關(guān)鍵字
    例如:content:java
  2. 范圍查詢
    域名+“:”+[最小值 TO 最大值]
    例如:size:[1 TO 1000]
    范圍查詢在lucene中不支持數(shù)值類型尺栖,支持字符串類型嫡纠。在solr中支持數(shù)值類型。
  3. 組合條件查詢
    • +條件1 +條件2:兩個條件之間是并且的關(guān)系and
      例如:+filename:apache +content:apache
    • +條件1 條件2:必須滿足第一個條件延赌,應(yīng)該滿足第二個條件
      例如:+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);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末掐松,一起剝皮案震驚了整個濱河市踱侣,隨后出現(xiàn)的幾起案子粪小,更是在濱河造成了極大的恐慌,老刑警劉巖抡句,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件探膊,死亡現(xiàn)場離奇詭異,居然都是意外死亡待榔,警方通過查閱死者的電腦和手機逞壁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來锐锣,“玉大人腌闯,你說我怎么就攤上這事〉胥荆” “怎么了姿骏?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長橘茉。 經(jīng)常有香客問我工腋,道長,這世上最難降的妖魔是什么畅卓? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任擅腰,我火速辦了婚禮,結(jié)果婚禮上翁潘,老公的妹妹穿的比我還像新娘趁冈。我一直安慰自己,他們只是感情好拜马,可當我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布渗勘。 她就那樣靜靜地躺著,像睡著了一般俩莽。 火紅的嫁衣襯著肌膚如雪旺坠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天扮超,我揣著相機與錄音取刃,去河邊找鬼。 笑死出刷,一個胖子當著我的面吹牛璧疗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播馁龟,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼崩侠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坷檩?” 一聲冷哼從身側(cè)響起却音,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤改抡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后系瓢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀摘,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年八拱,在試婚紗的時候發(fā)現(xiàn)自己被綠了阵赠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡肌稻,死狀恐怖清蚀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情爹谭,我是刑警寧澤枷邪,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站诺凡,受9級特大地震影響东揣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜腹泌,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一嘶卧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凉袱,春花似錦芥吟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至涤躲,卻和暖如春棺耍,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背种樱。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工蒙袍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缸托。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓左敌,卻偏偏與公主長得像瘾蛋,于是被迫代替她去往敵國和親俐镐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容