1. 程式人生 > >Lucene全文檢索之倒排索引實現原理、API解析【2018.11】

Lucene全文檢索之倒排索引實現原理、API解析【2018.11】

官網 http://lucene.apache.org/
下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/java/7.5.0/

Lucene的全文檢索是指什麼:

程式掃描文件,對文件document建立索引,並對文件進行分詞 ik,對每一個詞建立索引並關聯文件document的編號(索引);
當用戶進行搜尋時候,按照搜尋條件的分詞的索引,匹配出對應的文件索引,把具體的文件展現出來,整個過程就是全文索引(倒排索引基本原理);
->
->全文檢索的特點
1、只處理文字不處理語義
2、搜尋時英文不區分大小寫
3、結果列表有關度 排行

1、全文檢索和資料庫模糊查詢(或者模糊匹配)的區別:

1、資料庫模糊匹配會查詢出許多和期望無關的資料;
=>假設搜尋it相關的:
=>select * from db where name like ‘%’ it ‘%’ 會搜尋出git ,顯然結果並不是我們所期望的;
2、相關度排序問題資料庫無法解決;
3、資料庫模糊匹配效率較低;

2、Lucene與Solr的關係

/
->Lucene: 是全文檢索的底層API;
->Solr: 基於Lucene開發的搜尋伺服器;


3、使用Lucene需要的依賴:
    <dependencies>
        <!-- Junit單元測試 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- lucene核心庫 -->
        <
dependency
>
<groupId>org.apache.lucene</groupId> <artifactId>lucene-core</artifactId> <version>4.10.2</version> </dependency> <!-- Lucene的查詢解析器 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-queryparser</artifactId> <version>4.10.2</version> </dependency> <!-- lucene的預設分詞器庫 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-analyzers-common</artifactId> <version>4.10.2</version> </dependency> <!-- lucene的高亮顯示 --> <dependency> <groupId>org.apache.lucene</groupId> <artifactId>lucene-highlighter</artifactId> <version>4.10.2</version> </dependency> <!--ik分詞器 --> <dependency> <groupId>com.janeluo</groupId> <artifactId>ikanalyzer</artifactId> <version>2012_u6</version> </dependency> </dependencies>

》>1、建立測試類完成一次分詞:

1、建立文件,新增 StringField、TextField
2、對文件分詞:使用 IKAnalyzer 分詞器

  • FSDirectory建立目錄
  • DirectoryReader 讀取Directory
  • 搜尋工具IndexSearcher

3、建立寫的工具:

  • 配置IndexWriterConfig寫的配置,設定規則
  • 生成寫的物件IndexWriter

4、提交關閉

  • commit
  • close

以下測試原始碼分享地址:
https://github.com/medoo-Ai/lucene

/**
 * @auther SyntacticSugar
 * @data 2018/11/23 0023上午 11:37
 */
public class CreateIndexTest {
    /**
     * 給 文件新增fieldName /FieldText/store 是yes or no 控制是否儲存
     */
    @Test
    public void createIndexTest() {
        Document document = new Document();
        document.add(new StringField("id", "1", Field.Store.YES));
        document.add(new TextField("title", "谷歌地圖之父跳槽facebook厲害了我的哥碉堡了" +
                "藍瘦香菇", Field.Store.YES));
        // 建立儲存目錄、分詞
        try {
            FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
//            StandardAnalyzer analyzer = new StandardAnalyzer();
            IKAnalyzer analyzer = new IKAnalyzer();
            /**
             * 建立文件配置物件,IndexWriterConfig.OpenMode.CREATE  清空建立索引
             * IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
             */
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
            indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
            //索引
            IndexWriter indexWriter = new IndexWriter(indexDir, indexWriterConfig);
            //
            //寫入索引,提交,關閉
            indexWriter.addDocument(document);
            indexWriter.commit();
            indexWriter.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
       }

》生成如下indexDir目錄,目錄內是分詞;
生成

》2、建新測試,新增多個document

文件document中分詞,可以新增集合list
並且 ,建立文件配置物件 indexWriterConfig 時候:

  • IndexWriterConfig.OpenMode.CREATE 清空建立索引
  • IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
    /**
     * 給 文件新增filedName /FiledText/store 是yes or no
     */
    @Test
    public void createIndexTest2() throws IOException {
        ArrayList<Document> documents = new ArrayList<>();
        Document document1 = new Document();
        document1.add(new StringField("id", "1", Field.Store.YES));
        document1.add(new TextField("title", "谷歌地圖之父跳槽facebook厲害了我的哥碉堡了" +
                "藍瘦香菇", Field.Store.YES));
        Document document2 = new Document();
        document2.add(new StringField("id", "2", Field.Store.YES));
        document2.add(new TextField("title", "谷歌地圖之父拉斯加盟社交網站Facebook" +
                "藍瘦香菇", Field.Store.YES));
        //新增到集合中
        documents.add(document1);
        documents.add(document2);
        // 建立儲存目錄、分詞
        FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
//            StandardAnalyzer analyzer = new StandardAnalyzer();
        IKAnalyzer analyzer = new IKAnalyzer();
        /**
         * 建立文件配置物件,IndexWriterConfig.OpenMode.CREATE  清空建立索引
         * IndexWriterConfig.OpenMode.APPEND 在原來基礎上追加
         */
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(Version.LATEST, analyzer);
        indexWriterConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
        //索引
        IndexWriter indexWriter = new IndexWriter(indexDir, indexWriterConfig);
        //
        //寫入索引,提交,關閉s
        indexWriter.addDocuments(documents);
        indexWriter.commit();
        indexWriter.close();
    }

》3、新建測試類,使用分詞查詢 “谷歌地圖之父” ,為多條件查詢

3、Query查詢:

步驟如下:
1、建立目錄FSDirectory 、建立索引讀取工具DirectoryReader、建立索引搜尋工具indexSearcher
2、查詢解析器:QueryParser parser

  • 解析要查詢的 parse(String query)生成query物件

3、使用搜索工具indexSearcher進行查詢,把query物件傳參進去,返回topDocs文件物件

  • topDocs包含totalHits查詢的總數目、scoreDocs分數
  • 對scoreDocs 進行遍歷,獲取每一個文件的 編號docId 、得分
  • 通過搜尋工具 indexSearcher對文件docId 編號搜尋,找到具體的文件物件 document

4、document文件物件中,包含id / title 兩部分,get取出

  • String id = doc.get(“id”);
  • String title = doc.get(“title”);

對於查詢解析器 parse,原始碼如下:

解析

 @Test
    public void testSearch() throws IOException, ParseException {
        FSDirectory indexDir = FSDirectory.open(new File("indexDir"));
        /**
         * 索引讀取工具
         * 索引搜尋工具
         */
        IndexReader open = DirectoryReader.open(indexDir);
        IndexSearcher indexSearcher = new IndexSearcher(open);
        //建立查詢解析器[可以傳參入一個數組,多條件進行查詢],建立要查詢的物件
//        QueryParser parser = new QueryParser("title", new IKAnalyzer());
        QueryParser parser = new MultiFieldQueryParser(new String[]{"id", "title"}, new IKAnalyzer());
        Query query = parser.parse("谷歌地圖之父");
        //搜尋
        //topDocs包含totalHits、scoreDocs
        TopDocs topDocs = indexSearcher.search(query, 5);//引數5,就是排名前五名的
        System.out.println("查詢到資料的條數" + topDocs.totalHits);
        /**
         * 獲取文件編號中文件的編號以及文件的得分
         */
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            int docId = scoreDoc.doc;
            float score = scoreDoc.score;
            //根據文件編號查詢 doc
            Document doc = indexSearcher.doc(docId);
            String id = doc.get("id");
            String title = doc.get("title");
            System.out.println(id + ":" + title);
        }
    }

多


》抽取search查詢的工具類;

經常要用:
抽取了一個query查詢的工具類:

    public void search(Query query) throws IOException {
        //建新目錄,讀取工具,搜尋工具
        FSDirectory directory = FSDirectory.open(new File("indexDir"));
        DirectoryReader reader = DirectoryReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);
        /**
         * 搜尋符合條件的topDocs
         * 對文件進行解析  獲取  totalHits   ,scoreDocs
         */
        TopDocs topDocs = searcher.search(query, 10);
        System.out.println("本次搜尋到的條數:" + topDocs.totalHits);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        //遍歷
        for (ScoreDoc scoreDoc : scoreDocs) {
            int docId = scoreDoc.doc;
            float score = scoreDoc.score;
            //查詢到對應的文件
            Document doc = searcher.doc(docId);
            System.out.println(doc.get("id") + ":" + doc.get("title") + ":" + score);
        }
    }

》詞條查詢:

    /**
     * testTermQuery  詞條查詢
     */
    @Test
    public void testTermQuery() throws IOException {
        // 建立詞條查詢的物件
        TermQuery title = new TermQuery(new Term("title", "谷歌"));
        //呼叫查詢方法進行查詢
        search(title);
    }
    /**
     * WildcardQuery 萬用字元 ? 查詢
     */
    @Test
    public void WildcardQuery() throws IOException {
        TermQuery query = new TermQuery(new Term("title", "??"));
        search(query);
    }

詞條

》萬用字元查詢:

testFuzzyQuery 萬用字元查詢、 Fuzzy:英文有 :失真、模糊 的意思

  • 詞條中並沒有 “谷歌啊” 這樣的詞條,卻查出來資訊,因為模糊設定了編輯距離(容錯);
  • 編輯距離 0-2
    /**
     * testFuzzyQuery 萬用字元查詢  Fuzzy:失真、模糊
     * fscevool 編輯距離0-2
     */
    @Test
    public void testFuzzyQuery() throws IOException {
        FuzzyQuery query = new FuzzyQuery(new Term("title", "谷歌啊"), 1);
        search(query);
    }

斌
》組合查詢:

1、NumericRangeQuery 數字查詢範圍:

  • 可以進行精確的查詢
  • 引數:欄位名稱,最小值、最大值、是否包含最小值、是否包含最大值

2、 BooleanQuery(組合查詢)
建立組合查詢物件query,把查詢物件query1,query2分別新增到query中

    @Test
    public void testNumericRangeQuery() throws IOException {
        //Query介面
        Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
        search(query);
    }
    /**
     * BooleanQuery(組合查詢)
     * Occur.MUST_NOT  不是必須的
     * Occur.SHOULD    必須的
     */
    @Test
    public void testBooleanQuery() throws IOException {
        Query query1 = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
        Query query2 = NumericRangeQuery.newLongRange("id", 2L, 4L, true, true);
        //建立boolean查詢,然後新增query1/query2
        BooleanQuery query = new BooleanQuery();
        query.add(query1, BooleanClause.Occur.MUST_NOT);
        query.add(query2, BooleanClause.Occur.SHOULD);
        search(query);
    }

》修改,刪除

/**
     * 修改索引
     * 先刪除,後建立
     * 1、所以我們是修改   一把針對唯一的進行修改
     * 2、deleteDocuments 若id 是數字,直接刪除即可
     */
    @Test
    public void testUpdate() throws IOException {
        //建立目錄,建立IndexWriterConfig,建立索引寫出物件
        FSDirectory directory = FSDirectory.open(new File("indexDir"));
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //建立文件索引資料,而後進行修改
        Document document = new Document();
        document.add(new StringField("id", "1", Field.Store.YES));
        document.add(new TextField("title", "谷歌地圖之父跳槽facebook 為了加入復仇者聯盟 屌爆了啊", Field.Store.YES));
        /**
         * 修改
         * Term  詞條
         * document   文件
         */
        indexWriter.updateDocument(new Term("id", "1"), document);
        indexWriter.commit();
        indexWriter.close();
    }
    /**
     * testDelete
     * 刪除詞條 ,通過id 來刪除   indexWriter.deleteDocuments(new Term("id", "1"));
     * 1、刪除所有  deleteAll
     * 2、條件刪除   query來刪除
     */
    @Test
    public void testDelete() throws IOException {
        //建立目錄,建立IndexWriterConfig,建立索引寫出物件
        FSDirectory directory = FSDirectory.open(new File("indexDir"));
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new IKAnalyzer());
        IndexWriter indexWriter = new IndexWriter(directory, config);
        //建立文件索引資料
//        indexWriter.deleteDocuments(new Term("id", "1"));
        Query query = NumericRangeQuery.newLongRange("id", 2L, 2L, true, true);
        indexWriter.deleteDocuments(query);
        indexWriter.commit();
        indexWriter.close();
    }

》程式碼高亮顯示:

完成程式碼高亮顯示;
主要使用了格式化器和高亮工具:

  • 程式碼高亮工具構造器:public Highlighter(Formatter formatter, Scorer fragmentScorer) , 其中fragmentScorer 是切面評分,也就是queryScorer,對顯示的內容進行一個評分(因為要顯示的東西太多了,不能全部都高亮的吧,通過queryScorer來確定);
  • 格式化器SimpleHTMLFormatter 作為引數傳遞,指定了格式,本例中採用斜體
    @Test
    public void testHighlighter() throws Exception {
        // 目錄,directoryreader ,searcher
        FSDirectory directory = FSDirectory.open(new File("indexDir"));
        DirectoryReader reader = DirectoryReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);
        //分詞
        QueryParser parser = new QueryParser("title", new IKAnalyzer());
        Query query = parser.parse("谷歌地圖");
        searcher.search(query, 10);
        /**
         * 格式化器:
         * 準備高亮工具:
         */
        SimpleHTMLFormatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
        QueryScorer queryScorer = new QueryScorer(query);
        Highlighter highlighter = new Highlighter(formatter, queryScorer);
        //搜尋文件資料
        TopDocs topDocs = searcher.search(query, 10);
        System.out.println("本次搜尋到的條數:" + topDocs.totalHits);
        //bianli

        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            // 獲取文件編號,得分編號docId / score
            int docId = scoreDoc.doc;
            float score = scoreDoc.score;
            //查詢文件的 title  、得分
            Document doc = searcher.doc(docId);
            String title = doc.get("title");
            //   高亮處理  Analyzer analyzer, String fieldName, String text
            String bestFragment = highlighter.getBestFragment(new IKAnalyzer(), "title", title)
            
           

相關推薦

Lucene全文檢索索引實現原理API解析2018.11

》 官網 http://lucene.apache.org/ 下載地址:https://mirrors.tuna.tsinghua.edu.cn/apache/lucene/java/7.5.0/ 》 Lucene的全文檢索是指什麼: 程式掃描文件

Hadoop學習網路爬蟲+分詞+索引實現搜尋引擎案例

本專案實現的是:自己寫一個網路爬蟲,對搜狐(或者csdn)爬取新聞(部落格)標題,然後把這些新聞標題和它的連結地址上傳到hdfs多個檔案上,一個檔案對應一個標題和連結地址,然後通過分詞技術對每個檔案中的標題進行分詞,分詞後建立倒排索引以此來實現搜尋引擎的功能,建

Elastic 索引(二)

mage bsp post elastic 分享圖片 位置 png blog 通過 常規索引建立:   文檔--》關鍵詞的映射過程(正向索引) 缺點:費時 便利全部文檔 倒排反向建立索引:   關鍵詞--》文檔的映射 反向到倒排索引:將索引的關鍵詞出現的文檔的位置和出現頻率

大數據MapReduce入門索引

tsp 功能 nbsp bstr 生成 path 需要 turn 們的   在上一篇博客中我們講解了MapReduce的原理以及map和reduce的作用,相信你理解了他們的原理,今天講解的是mapreduce 的另一個就是倒排索引。 什麽是倒排索引呢?倒排索

一些演算法的MapReduce實現——索引實現

/** * input format * docid<tab>doc content * * output format * (term:docid)<tab>(tf in this doc) * */ public s

Hadoop 文件索引實現

在上黃宜華老師的MapReduce的課程中,會有實驗讓實現帶詞頻的文件倒排索引。一般情況下根據他的書就能實現基本的東西,但是根據書上的程式碼,執行的時候可能會有一些小的trick,會報出一些異常。其實如果參照這個文章 《Hadoop之倒排索引》就能實現所需要的功能了。但是本

mapreduce演算法索引

package mapreduce; import java.io.IOException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.

mapreduce典型應用案例索引

一、倒排索引的介紹 通俗的講,就是根據單詞找到包含這個單詞的所有文件。 二、mapreduce實現框架 1、首先要確定map、reduce、combiner中的key和value是什麼型別 2、然後確定key和value具體是什麼? Map : key為 單詞+檔名 value為

大資料開發索引詳解,拭目以待

在現如今,隨著網際網路技術和大資料+人工智慧的飛速發展,越來越多人想要學習大資料開發,那麼今天大資料培訓之倒排索引的詳細介紹,下面我們一起來看一下吧。 首先大資料在經濟、政治、文化等方面有著深遠的影響,大資料可以幫助人們開啟循“數”管理的模式,也是我們當下“大社會”的集中體現,大資料高階班的課程

2 Elasticsearch 篇索引與分詞

文章目錄 書的目錄與索引 正排與倒排索引簡介 倒排索引詳解 分詞介紹 analyze_api 自帶分詞器 Standard Analyzer Simple Analyzer W

MapReduce 案例索引

MapReduce 案例之倒排索引 1. 倒排索引 倒排索引是文件檢索系統中最常用的資料結構,被廣泛地應用於全文搜尋引擎。 它主要是用來儲存某個單詞(或片語) 在一個文件或一組文件中的儲存位置的對映,即提供了一種根據內容來查詢文件的方式。由於不是根據文件來確定文件所包含的內容,而是進行相反的操作,因

MapReduce程式設計索引

任務要求: //輸入檔案格式 18661629496 110 13107702446 110 1234567 120 2345678 120 987654 110 2897839274 18661629496 //輸出檔

Hadoop基礎教程9Hadoop索引

開發環境 硬體環境:Centos 6.5 伺服器4臺(一臺為Master節點,三臺為Slave節點) 軟體環境:Java 1.7.0_45、hadoop-1.2.1 1、倒排索引 倒排索引是文件檢索系統中最常用的資料結構,被廣泛用於全文搜尋引

40.索引核心原理

提升 兩個 知識點 初步 blog 微軟雅黑 spa word ttl 主要知識點 倒排索引核心原理:normalization 假設有兩個文檔,內容如下 doc1:I really liked my small dogs, and I think my mom

ElasticSearch最佳入門實踐(三十九)索引核心原理揭祕

1、例子,兩段文字 doc1:I really liked my small dogs, and I think my mom also liked them doc2:He never liked any dogs, so I hope that my m

索引查詢原理

Lucene 查詢過程 在lucene中查詢是基於segment。每個segment可以看做是一個獨立的subindex,在建立索引的過程中,lucene會不斷的flush記憶體中的資料持久化形成新的segment。多個segment也會不斷的被merge成一個大的segm

Lucene索引簡述

一、前言 上一篇《Lucene倒排索引簡述 之索引表》,已經對整個倒索引的結構進行大體介紹,並且詳細介紹了索引表(TermsDictionary)的內容。同時還詳細介紹了Lucene關於索引表的實現,相關檔案結構詳解,以及對索引表採用的資料結構進行剖析解讀。

Lucene索引簡述 索引

一、前言 倒排索引是全文檢索的根基,理解了倒排索引之後才能算是入門了全文檢索領域。倒排索引的的概念很簡單,也很好理解。但如你知道在全文檢索領域Lucene可謂是獨領風騷。所以你真的瞭解Lucene的倒排了嗎?Lucene是如何實現這個結構的呢? 倒排索引如此重

Lucene 4.X 索引原理實現: (1) 詞典的設計

詞典的格式設計 詞典中所儲存的資訊主要是三部分: Term字串 Term的統計資訊,比如文件頻率(Document Frequency) 倒排表的位置資訊 其中Term字串如何儲存是一個很大的問題,根據上一章基本原理的表述中,我們知道,寫入檔案的Term是按照字典順序排好序的,那麼如何將這些

Lucene索引原理實現:Term Dictionary和Index檔案 (FST詳細解析)

我們來看最複雜的部分,就是Term Dictionary和Term Index檔案,Term Dictionary檔案的字尾名為tim,Term Index檔案的字尾名是tip,格式如圖所示。 Term Dictionary檔案首先是一個Header,接下來是Pos