Lucene 源码系列——索引文件的生成(八)之 dim&&dii(Lucene 8.4.0)
在前面的文章中,我们介绍了在 Lucene7.5.0 中索引文件.dim&&.dii 的数据结构,从本篇文章开始介绍其生成索引文件。dim&&.dii 的内容,注意的是,由于是基于 Lucene8.4.0 来描述其生成过程,故如果出现跟 Lucene7.5.0 中不一致的地方会另外指出,最后建议先阅读下文章 Bkd-Tree 简单的了解下 Lucene 中点数据的使用。
在文章索引文件的生成(一)之 doc&&pay&&pos 中,简单的介绍了生成索引文件。dim&&.dii 的时机点,为了能更好的理解其生成过程,会首先介绍下在生成索引文件之前,Lucene 是如何收集每篇文档的点数据信息(Point Value),随后在 flush 阶段,会根据收集到的信息生成索引文件。dim&&.dii。
收集文档的点数据信息
在源码中,通过 PointValuesWriter 对象来实现文档的点数据的收集,并且具有相同域名的点数据使用同一个 PointValuesWriter 对象来收集,例如下图中添加三篇文档:
图 1:
在图 1 中,由于第 49 行、50 行、55 行。60 行、61 行,添加了具有相同的域名“content”的点数据,故他们三个的点数据信息会使用同一个 PointValuesWriter 对象来收集,同理第 51 行、56 行。
PointValuesWriter 对象收集的内容主要包括以下信息:
numPoints:
int 类型,numPoints 是一个从 0 开始递增的值,可以理解为是每一个点数据的一个唯一编号,并且通过这个编号能映射出该点数据属于哪一个文档(document)(下面会介绍),由于是每一个点数据的唯一编号,所以该值还可以用来统计某个域的点数据的个数,在图 1 中,域名为"content"的点数据共有 5 个,那么这么 5 个点数据的 numPoints 的值分别为 0、1、2、3、4。
docIDs
int 类型数组,每添加一条点数据,会将该点数据所属文档号作为数组元素添加到 docIDs 数组中 ,并且数组下标为该点数据对应的 numPoints,对于域名为"content"的点数据,在完成三篇文档的添加后,docIds 数组如下所示:
图 2:
numDocs
该值描述的是对于某个点数据域,包含它的文档数量,例如在图 1 中,域名为"content"的点数据,包含该域的文档有文档 0、文档 1、文档 2,故 numDocs 的值为 3,同理对于域名为"titile"的点数据,numDocs 的值为 1。
bytes
该值是 ByteBlockPool 类型,ByteBlockPool 为 Lucene 中的类,在这里我们只需要知道,在这个类中使用了 buff(byte[ ]数组)来存储点数据的域值,在 Lucene 中,数值类型的域值需要通过转化为字节类型来存储,故我们先介绍下在 Lucene 中数值类型到字节类型的转化实现。
数值类型到字节类型(无符号)的转化
Lucene 中的提供了 BigInteger、int、long、float、double 到字节类型 byte 的转化,在 NumericUtils 类中有具体的实现,本文通过例子只介绍下 int 类型到 byte 类型的转化。
待转化的数值为 3,过程分为两个步骤:
步骤一:将待转化的数值跟 0x80000000 执行异或操作
为什么要执行异或操作:
- 等介绍完步骤二再做解释
步骤二:写入到数组大小为 4 的字节数组中
由于 int 类型的数值占用 4 个字节,所以只需要数组大小为 4 的字节数组存储即可,如下所示:
图 3:
为什么要执行异或操作:
- 当转化为字节数组后,可以通过比较两个字节数组的相同下标对应的数组元素大小来描述这两个字节数组的大小关系,使得转化后的字节数组依然能具有数值类型的比较功能(大小比较),比如说有两个数值类型 3 和 4,假设不执行上文中的步骤一,即不跟 0x80000000 执行异或操作,转化后的字节数组如下所示:
图 4:
图 4 中,从第 0 个字节开始,我们依次比较两个字节数组相同下标对应的数组元素,显而易见,第 0 个、第 1 个、第 2 个字节都是相同的,当比较到第 3 个字节时,能区分出大小关系,如果我们比较的两个数值是-5、7,假设不执行上文中的步骤一,即不跟 0x80000000 执行异或操作,转化后的字节数组如下所示:
图 5:
从图 5 可知,由于在 Java 中使用补码来表示负数,所以当比较第 0 个字节时,会得出-5 比 7 大的错误结果,如果我们先执行上文中的步骤一,即跟 0x80000000 执行异或操作,那么数值-5 跟 7 转化后的字节数组如下所示:
图 6:
由图 6 可知,可以正确的比较-5 与 7 的大小关系了。
上文中我们说到,ByteBlockPool 类型的变量 bytes 使用字节数组 buff 来存储点数据的域值,存储的过程十分简单,就是将点数据的域值转化为字节数组后,拷贝到 bytes 的字节数组 buff 中,例如有下面的例子,同图 1:
图 7:
我们将要介绍在添加了 3 篇文档后,域名为"content"的点数据的域值在字节数组 buff 中的数据分布,如下所示:
图 8:
由图 8 可以看出,在字节数组 buff[ ]中,下标 07 对应的数组元素存储的是图 7 中代码第 49 行的点数据的域值,下标 2431 对应的数组元素存储的是图 7 中代码第 60 行的点数据的域值。
生成索引文件。dim&&.dii 阶段,会读取 buff 中的域值,其读取的过程将会在后面的文章中介绍。
结语
本文介绍了在执行 flush 之前,Lucene 是如何收集点数据的信息,即上文中的 numPoints、docIDs、numDocs、bytes,那么在 flush 阶段,就可以通过这些信息来生成索引文件。dim&&.dii。