Lucene源碼(三):全文檢索的底層原理


Lucene源碼(一):分詞器的底層原理
Lucene源碼(二):文本相似度TF-IDF原理

核心代碼是下面這幾句。Query query中存儲搜索文本的分詞結果。具體在Lucene源碼(一):分詞器的底層原理仔細瞭解分詞的底層原理。

IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(index)));
IndexSearcher searcher = new IndexSearcher(reader);

Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser(field, analyzer);
Query query = parser.parse(line);

TopDocs results = searcher.search(query, 5 * hitsPerPage);

index是創建的索引目錄,filed是索引的字段,5 * hitsPerPage其實就是搜索返回的最大數量。
搜索結果存儲在result中,如下:篩選出了分數較高的兩個文檔ScoreDoc,doc爲文檔id,score爲匹配分數。詳細計算方法Lucene源碼(二):文本相似度TF-IDF原理
在這裏插入圖片描述

IndexSearcher

searchAfter

TopDocs results = searcher.search(query, 5 * hitsPerPage),在search方法中,又會進到下面這裏來:

第一部分的代碼很簡單,就是計算出需要遍歷的最大數量
在這裏插入圖片描述

CollectorManager

第二部分的代碼是創建一個CollectorManager實例manage,這個實例的作用是管理collectors(可以理解爲數據查詢,因爲數據結構是樹,所以就是收集葉子節點),並且更好的進行並行處理。
在這裏插入圖片描述
這個類需要重寫兩個方法:

  1. 新建collector的方法
  2. 對帶有結果的所有獨立的collector進行聚合

search

在這裏插入圖片描述
Lower-level search API,其實就是新建collector,然後進入另一個search方法,這裏需要先執行createNormalizedWeight方法
在這裏插入圖片描述

createNormalizedWeight

在這裏插入圖片描述
請記住,這裏的query是BooleanQuery,在createWeight裏面會進入BooleanWeight的構造方法,在裏面又會重新進入IndexSearchercreateWeight,但這個時候的query是TermQuery

createWeight

在這裏插入圖片描述

TermQuery

createWeight

在這裏插入圖片描述
這裏的變量termSate是TermContext對象,裏面存儲着含有單詞的文檔數量docFreq單詞的詞頻totalTermFreq,那麼前面部分的代碼作用就是做這個統計的。

然後進入TermWeight的構造方法,但是在TermQuery類裏面實現的

TermWeight

進來之後,前面都是一些賦值和初始化。
在這裏插入圖片描述
接着,看關鍵代碼。searcher.collectionStatistics方法統計文檔中的所有單詞的詞頻,即所有文檔總共有多少個單詞,統計結果存儲在變量collectionStats中;

第二句的話,就是對象轉換了:TermContext–>TermStatistics
在這裏插入圖片描述
最後,就開始計算TF_IDF了。
在這裏插入圖片描述

TFIDFSimilarity

Lucene在這個類裏面實現TF-IDF計算方法,跟以往的TF-IDF計算是不一樣的。所以,如果我們想自定義TF-IDF的計算方式,可以參考這個類重新實現一個TF-IDF的實現類。

進來computeWeight方法之後就跳轉到idfExplain方法
在這裏插入圖片描述
idfExplain方法的功能其實就是計算IDF,返回一個Explanation
在這裏插入圖片描述
計算完idf之後,Lucene還會進行標準化,返回TFIDFSimilarity對象
在這裏插入圖片描述
最後,一層層return,還記得最終在哪裏返回嗎…

再梳理一遍,調用鏈是這麼回事

IndexSearch.createNormalizedWeight–>IndexSearcher.createWeight(BooleanQuery)–>BooleanWeight構造器–>IndexSearcher.createWeight(TermQuery)–>TermQuery.createWeight–>TermQuery.TermWeight–>TFIDFSimilarity.computeWeight–>TFIDFSimilarity.IDFStats

所以,最終是返回到了IndexSearch.createNormalizedWeight方法裏,然後又進行一些IDF標準化處理和加入了一些評分因子,具體看上面IndexSearch.createNormalizedWeight截圖。

下面就開始計算我們輸入的搜索內容跟每個文檔的相似度了。

其實,在前面計算好每個Term即單詞的TF-IDF值之後,基本上就可以直接得到相似度了,只是把文檔中出現的單詞的TF-IDF累加起來,即是文檔的相似度了,當然這是常規的做法。

但是Lucene的計算方式不一樣,它還引入了文檔長度的加權因子,作用就是提高短文檔的分數,降低長文檔的分數。

對應到代碼中,做法當然是:遍歷每個文檔,然後進行計算。前面的代碼就是獲取文檔對應的葉子節點了。
在這裏插入圖片描述
然後,新建一個對象scorer來計算和存儲最終的相似度。這裏的BulkScorer是一個接口類,對應的實現類是BooleanScorer,所以,最終是BooleanScorer.score方法中完成計算的。
在這裏插入圖片描述

BooleanScorer

在這裏插入圖片描述
從代碼不難看出,這裏實際就是在對每個單詞的分數進行累加,核心的算法還是在TFIDFSimilarity中計算的,即scorer.score()獲取分數的方法是TFIDFSimilarity實現方法。如下:
在這裏插入圖片描述
這個其實就是TF * IDF * 文檔長度加權因子(上面提到的)

歡迎關注同名公衆號:“我就算餓死也不做程序員”。
交個朋友,一起交流,一起學習,一起進步。在這裏插入圖片描述

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