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 * 文档长度加权因子(上面提到的)

欢迎关注同名公众号:“我就算饿死也不做程序员”。
交个朋友,一起交流,一起学习,一起进步。在这里插入图片描述

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