段内排序IndexSort(Lucene 8.9.0)

  段内排序IndexSort是Lucene在索引(Indexing)阶段提供的一个功能,该功能使得在执行flushcommit或者NRT操作后,新生成的段其包含的文档是有序的,即在索引阶段实现了文档的排序。

  在之前的一些文章中已经简单的介绍了IndexSort,例如在文章构造IndexWriter对象(一) 说到,通过在IndexWriter的配置信息中添加IndexSort信息来开启段内排序的功能;在文章文档提交之flush(三)中提到了对文档进行段内排序的时机点;在文章Collector(三)中提到了在Search阶段,如何通过IndexSort实现高效(提前结束)收集满足查询条件的文档集合。

  本系列文章将会详细介绍IndexSort在索引阶段相关的内容,以及它将如何影响索引文件的生成、段的合并、以及在查询阶段的用途。

IndexSort的应用

  我们先通过一个例子来了解如何使用IndexSort这个功能。完整的demo地址见:https://github.com/LuXugang/Lucene-7.5.0/blob/master/LuceneDemo8.9.0/src/main/java/index/IndexSortTest.java

图1:

图2:

  图1中的第44、45行代码定义了两个排序规则。在继续展开介绍之前,我们先简单的说下Lucene中正排索引SortedSetDocValuesField(见图2)的一些概念。

SortedSetDocValuesField

  使用SortedSetDocValuesField可以使得我们在同一篇文档中定义一个或多个具有相同域名不同域值的SortedSetDocValuesField域。这种域的其中一个应用方式即在索引阶段,对于一篇文档,我们可以选择其包含的SortedSetDocValuesField域的某一个域值参与段内排序。

  例如在图2中,文档3中(代码第80、81行)定义了2个域名为"sort0",域值分别为"b1"、"b2"的SortedSetDocValuesField域。并且在图1中的第44行代码定义了一个段内排序规则,该规则描述的是每个文档会使用域名为"sort0",并且将最小的域值来参与排序。那么对于文档3,它将使用域值为"b1"(字符串使用字典序进行排序)参与段内排序。

SortedSetSelector.Type

  图2中SortedSetSelector.Type.MIN规定了使用最小的域值参与段内排序。SortedSetSelector.Type的所有选项如下所示:

图3:

  图3中MIN、MAX就不做介绍了,我们说下MIDDLE_MIN跟MIDDLE_MAX。

MIDDLE_MIN、MIDDLE_MAX

  如果域值的数量是奇数,那么MIDDLE_MIN、MIDDLE_MAX具有相同的作用,比如有以下的域值:

  那么将会选择"b3"参与排序。

  如果域值的数量是偶数,假设有以下的域值:

  那么在MIDDLE_MIN条件下会选择"b3"、在MIDDLE_MAX下会选择"b4"。

  另外使用SortedSetDocValuesField的一个场景是,我们在搜索阶段可以根据SortedSetDocValuesField对查询结果进行排序,并且可以通过指定不同的SortedSetSelector.Type获取不同的排序结果。

排序方式概述

  我们接着看下图1。图1中定义了两个规则,那么在段内排序的过程中,先按照"sort0"进行排序,当无法比较出先后关系时,接着按照"sort1"进行排序,如果两个排序规则都无法比较出先后关系,则最终比较文档的添加顺序。

文档之间的排序比较方式

图4:

图5:

  图4中,我们的搜索条件没有对结果增加额外的排序规则,那么查询结果将会按照段内排序后的顺序输出

  我们结合图2,分别介绍下一些文档之间排序比较的方式。

文档0

  这里说的文档0指的是图2中添加的顺序,如图2中第53行的代码所示。

  根据"sort0"的排序规则,将按照域值从大到小排序(SortedSetSortField的第二个参数reverse的值为true),所以文档0~3这四篇文档将分别使用"c1"、"b1"、"b1"、"b1"进行比较,另外由于文档4中没有"sort0"域,那么它将被排到最末位置。可见根据"sort0",只能确定文档0是排在最前面的以及文档4是排在最后面,如下所示。故需要通过"sort1"对文档1、2、3进一步排序。

文档1、2、3

  根据"sort1"的排序规则,将按照域值从大到小排序,所以文档1、2、3这四篇文档将分别使用"e2"、"f2"、"e2"进行比较,可见文档2在这三篇文档中排在最前面,由于文档1、3无法通过"sort1"区分出先后关系,并且没有其他的排序排序规则了,那么由于文档1先被添加,故文档1排在文档3前面,如下所示:

搜索阶段的排序

  上文中说到SortedSetDocValuesField可以用于在索引阶段排序,同样的它也可以用于搜索阶段的排序。在设置了图1中的排序规则前提下,如果我们在搜索阶段提供了以下的排序规则:

图6:

  其查询结果如下所示:

图7:

  其排序的比较过程跟IndexSort是一样的,这里就不赘述了。

用于排序的域

  下图中列出了其他可以用于段内排序的域:

图8:

  这些域的使用方法可以通过查看每个类的注释就可以完全理解,故不展开介绍了。

结语

  下一篇文章中,我们将介绍段内排序在索引阶段的排序方式、排序时机点以及其他相关内容。

点击下载附件