1. 程式人生 > >Lucene學習總結

Lucene學習總結

.get hits can 條件 ont param 便在 1.8 static

Lucene是什麽?

Lucene在維基百科的定義

Lucene是一套用於全文檢索和搜索的開放源代碼程序庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程序接口,能夠做全文索引和搜索,在Java開發環境裏Lucene是一個成熟的免費開放源代碼工具;就其本身而論,Lucene是現在並且是這幾年,最受歡迎的免費Java信息檢索程序庫。

Lucene和solr

我想提到Lucene,不得不提solr了。

很多剛接觸Lucene和Solr的人都會問這個明顯的問題:我應該使用Lucene還是Solr?

答案很簡單:如果你問自己這個問題,在99%的情況下,你想使用的是Solr. 形象的來說Solr和Lucene之間關系的方式是汽車及其引擎。 你不能駕駛一臺發動機,但可以開一輛汽車。 同樣,Lucene是一個程序化庫,您不能按原樣使用,而Solr是一個完整的應用程序,您可以立即使用它。

全文檢索是什麽?

全文檢索在百度百科的定義

全文數據庫是全文檢索系統的主要構成部分。所謂全文數據庫是將一個完整的信息源的全部內容轉化為計算機可以識別、處理的信息單元而形成的數據集合。全文數據庫不僅存儲了信息,而且還有對全文數據進行詞、字、段落等更深層次的編輯、加工的功能,而且所有全文數據庫無一不是海量信息數據庫。

全文檢索首先將要查詢的目標文檔中的詞提取出來,組成索引,通過查詢索引達到搜索目標文檔的目的。這種先建立索引,再對索引進行搜索的過程就叫全文檢索(Full-text Search)。

全文檢索(Full-Text Retrieval)是指以文本作為檢索對象,找出含有指定詞匯的文本。

全面、準確和快速是衡量全文檢索系統的關鍵指標。

關於全文檢索,我們要知道:

只處理文本。
不處理語義。
搜索時英文不區分大小寫。
結果列表有相關度排序。

(查出的結果如果沒有相關度排序,那麽系統不知道我想要的結果在哪一頁。我們在使用百度搜索時,一般不需要翻頁,為什麽?因為百度做了相關度排序:為每一條結果打一個分數,這條結果越符合搜索條件,得分就越高,叫做相關度得分,結果列表會按照這個分數由高到低排列,所以第1頁的結果就是我們最想要的結果。) 在信息檢索工具中,全文檢索是最具通用性和實用性的。

全文檢索和數據庫搜索的區別

簡單來說,這兩者解決的問題是不一樣。數據庫搜索在匹配效果、速度、效率等方面都遜色於全文檢索。

Lucene實現全文檢索流程是什麽?

技術分享圖片

全文檢索的流程分為兩大部分:索引流程、搜索流程

索引流程:即采集數據構建文檔對象分析文檔(分詞)創建索引。

搜索流程:即用戶通過搜索界面創建查詢執行搜索,搜索器從索引庫搜索渲染搜索結果

簡單案例

1.Lucene實現向文檔寫索引並讀取文檔

        <!-- https://mvnrepository.com/artifact/org.apache.lucene/lucene-core -->
        <!-- Lucene核心庫 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>7.2.1</version>
        </dependency>
        <!-- Lucene解析庫 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-queryparser</artifactId>
            <version>7.2.1</version>
        </dependency>
        <!-- Lucene附加的分析庫 -->
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>7.2.1</version>
        </dependency>
package com.xiaobai.lucene;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Paths;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
/**
 *
 *TODO  索引文件
 * @author Snaiclimb
 * @date 2018年3月30日
 * @version 1.8
 */
public class Indexer {
    // 寫索引實例
    private IndexWriter writer;

    /**
     * 構造方法 實例化IndexWriter
     *
     * @param indexDir
     * @throws IOException
     */
    public Indexer(String indexDir) throws IOException {
        //得到索引所在目錄的路徑
        Directory directory = FSDirectory.open(Paths.get(indexDir));
        // 標準分詞器
        Analyzer analyzer = new StandardAnalyzer();
        //保存用於創建IndexWriter的所有配置。
        IndexWriterConfig iwConfig = new IndexWriterConfig(analyzer);
        //實例化IndexWriter
        writer = new IndexWriter(directory, iwConfig);
    }

    /**
     * 關閉寫索引
     *
     * @throws Exception
     * @return 索引了多少個文件
     */
    public void close() throws IOException {
        writer.close();
    }

    public int index(String dataDir) throws Exception {
        File[] files = new File(dataDir).listFiles();
        for (File file : files) {
            //索引指定文件
            indexFile(file);
        }
        //返回索引了多少個文件
        return writer.numDocs();

    }

    /**
     * 索引指定文件
     *
     * @param f
     */
    private void indexFile(File f) throws Exception {
        //輸出索引文件的路徑
        System.out.println("索引文件:" + f.getCanonicalPath());
        //獲取文檔,文檔裏再設置每個字段
        Document doc = getDocument(f);
        //開始寫入,就是把文檔寫進了索引文件裏去了;
        writer.addDocument(doc);
    }

    /**
     * 獲取文檔,文檔裏再設置每個字段
     *
     * @param f
     * @return document
     */
    private Document getDocument(File f) throws Exception {
        Document doc = new Document();
        //把設置好的索引加到Document裏,以便在確定被索引文檔
        doc.add(new TextField("contents", new FileReader(f)));
        //Field.Store.YES:把文件名存索引文件裏,為NO就說明不需要加到索引文件裏去
        doc.add(new TextField("fileName", f.getName(), Field.Store.YES));
        //把完整路徑存在索引文件裏
        doc.add(new TextField("fullPath", f.getCanonicalPath(), Field.Store.YES));
        return doc;
    }

    public static void main(String[] args) {
        //索引指定的文檔路徑
        String indexDir = "D:\\lucene\\dataindex";
        ////被索引數據的路徑
        String dataDir = "D:\\lucene\\data";
        Indexer indexer = null;
        int numIndexed = 0;
        //索引開始時間
        long start = System.currentTimeMillis();
        try {
            indexer = new Indexer(indexDir);
            numIndexed = indexer.index(dataDir);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                indexer.close();
            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        //索引結束時間
        long end = System.currentTimeMillis();
        System.out.println("索引:" + numIndexed + " 個文件 花費了" + (end - start) + " 毫秒");
    }

}

運行結果:

技術分享圖片

索引目錄多出的文件:

技術分享圖片

2.根據索引搜索

package com.xiaobai.lucene;


import java.nio.file.Paths;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
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.Directory;
import org.apache.lucene.store.FSDirectory;
/**
 * 根據索引搜索
 *TODO
 * @author Snaiclimb
 * @date 2018年3月25日
 * @version 1.8
 */
public class Searcher {

    public static void search(String indexDir, String q) throws Exception {

        // 得到讀取索引文件的路徑
        Directory dir = FSDirectory.open(Paths.get(indexDir));
        // 通過dir得到的路徑下的所有的文件
        IndexReader reader = DirectoryReader.open(dir);
        // 建立索引查詢器
        IndexSearcher is = new IndexSearcher(reader);
        // 實例化分析器
        Analyzer analyzer = new StandardAnalyzer();
        // 建立查詢解析器
        /**
         * 第一個參數是要查詢的字段; 第二個參數是分析器Analyzer
         */
        QueryParser parser = new QueryParser("contents", analyzer);//帶分詞器的查詢解析
        // 根據傳進來的p查找
        Query query = parser.parse(q);//這裏進行了分詞,Spring Cloud被分成Spring和Cloud兩個詞
        // 計算索引開始時間
        long start = System.currentTimeMillis();
        // 開始查詢
        /**
         * 第一個參數是通過傳過來的參數來查找得到的query; 第二個參數是要出查詢的行數
         */
        TopDocs hits = is.search(query, 10);//將分詞後的關鍵字封裝為查詢條件,從索引庫查詢
        // 計算索引結束時間
        long end = System.currentTimeMillis();
        System.out.println("匹配 " + q + " ,總共花費" + (end - start) + "毫秒" + "查詢到" + hits.totalHits + "個記錄");
        // 遍歷hits.scoreDocs,得到scoreDoc
        /**
         * ScoreDoc:得分文檔,即得到文檔 scoreDocs:代表的是topDocs這個文檔數組
         *
         * @throws Exception
         */
        for (ScoreDoc scoreDoc : hits.scoreDocs) {
            Document doc = is.doc(scoreDoc.doc);
            System.out.println(doc.get("fullPath"));
        }

        // 關閉reader
        reader.close();
    }

    public static void main(String[] args) {
        String indexDir = "D:\\lucene\\dataindex";
        //我們要搜索的內容
        String q = "Spring Cloud";//目前這個分詞器,無法搜索中文內容,或缺少中文分詞器jar包和其他中文編碼支持
        try {
            search(indexDir, q);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

執行結果:

技術分享圖片

Lucene全文檢索組件分析

技術分享圖片

在Lucene中,采集數據(從網站爬取或連接數據庫)就是為了創建索引,創建索引需要先將采集的原始數據加工為文檔,再由文檔分詞產生索引。文檔(Document) 中包含若幹個Field域。

IndexWriter索引過程的核心組件,通過IndexWriter可以創建新索引、更新索引、刪除索引操作。IndexWriter需要通過Directory對索引進行存儲操作。


Directory描述了索引的存儲位置,底層封裝了I/O操作,負責對索引進行存儲。它是一個抽象類,它的子類常用的包括FSDirectory(在文件系統存儲索引)、RAMDirectory(在內存存儲索引)。


在對Docuemnt中的內容索引之前需要使用分詞器進行分詞 ,分詞的主要過程就是分詞、過濾兩步。 分詞就是將采集到的文檔內容切分成一個一個的詞,具體應該說是將Document中Fieldvalue值切分成一個一個的詞。


過濾包括去除標點符號、去除停用詞(的、是、a、an、the等)、大寫轉小寫、詞的形還原(復數形式轉成單數形參、過去式轉成現在式等)。
停用詞是為節省存儲空間和提高搜索效率,搜索引擎在索引頁面或處理搜索請求時會自動忽略某些字或詞,這些字或詞即被稱為Stop Words(停用詞)。比如語氣助詞、副詞、介詞、連接詞等,通常自身並無明確的意義,只有將其放入一個完整的句子中才有一定作用,如常見的“的”、“在”、“是”、“啊”等。
Lucene中自帶了StandardAnalyzer,它可以對英文進行分詞。

Lucene學習總結