Lucene 源码系列——多个 SHOULD 的 Query 的倒排求并集

文档号合并

本篇文章通过一个例子介绍如何对满足搜索要求的文档进行合并(筛选),详细的合并过程可以看我的源码注释,GitHub 地址是:https://github.com/luxugang/Lucene-7.5.0/blob/master/solr-7.5.0/lucene/core/src/java/org/apache/lucene/search/BooleanScorer.java

例子

添加 10 篇文档到索引中。如下图:
图 1:
1.png

使用 WhiteSpaceAnalyzer 进行分词。
查询条件如下图。MinimumNumberShouldMatch 的值为 2,表示满足查询条件的文档中必须至少包含"a"、"b"、"c"、"e"中的任意两个。
图 2:
2.png

文档号合并

本篇文章中不会介绍如何根据关键字找到对应文档的过程,只介绍了如何合并(筛选)文档号的过程。
首先给出一个 Bucket[]数组,Bucket[]数组下标为文档号,数组元素为文档出现的频率,然后分别统计包含"a"、"b"、"c"、"e"的文档数,将文档出现的次数写入到 Bucket[]数组。

处理包含关键字“a”的文档

将包含“a”的文档号记录到 Bucket[]数组中。
图 3:
3.png

处理包含关键字“b”的文档

将包含“b”的文档号记录到 Bucket[]数组中,文档号 9 第二次出现,所以计数加 1。
图 4:
4.png

处理包含关键字“c”的文档

将包含“c”的文档号记录到 Bucket[]数组中,文档号 3、6、8 再次出现,所以对应计数都分别加 1;
图 5:
5.png



处理包含关键字“e”的文档

将包含“e”的文档号记录到 Bucket[]数组中
图 6:
6.png

统计文档号

在 Bucket 数组中,下标值代表了文档号,当我们处理所有关键字后,我们需要遍历文档号,然后判断每一个文档号出现的次数是否满足 MinimumNumberShouldMatch,为了使得只对出现的文档号进行遍历,Lucene 使用了一个 matching 数组记录了上面出现的文档号。matching 数组记录文档号的原理跟 FixedBitSet 一样,都是用一个 bit 位来记录文档号。不赘述。

在当前的例子中,我们只要用到 matching[]的第一个元素,第一个元素的值是 879(为什么只要用到第一个元素跟第一个元素的是怎么得来的,在 BooleanScorer 类中我加了详细的注释,这里省略)
图 7:
7.png

根据二进制中 bit 位的值为 1,这个 bit 位的位置来记录包含查询关键字的文档号,包含查询关键字的文档号只有 0,1,2,3,5,6,8,9 一共 8 篇文档,接着根据这些文档号,把他们作为 bucket[]数组的下标,去找到每一个数组元素中的值,如果元素的值大于等于 minShouldMatch,对应的文档就是我们最终的结果,我们的例子中

builder.setMinimumNumberShouldMatch(2);

所以根据最终的 bucket[]
图 8:
8.png

只有文档号 3,文档号 5,文档 6,文档 8,文档 9 对应元素值大于 minShouldMatch,满足查询要求。

结语

本文介绍了使用 BooleanQuery 并且所有的 TermQuery 之间是 SHOULD 关系的文档号合并原理,在后面的文章中会依次介绍 SHOULD、MUST、MUST_NOT、FILTER 的 TermQuery 的文档号合并原理。


本文地址:https://www.6aiq.com/article/1586695513057
本文版权归作者和AIQ共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出