Lucene(Solr / ElasticSearch)如何快速执行过滤器术语计数?

12
从数据结构的角度来看,Lucene(Solr/ElasticSearch)是如何快速进行过滤术语计数的?例如,给定包含单词“bacon”的所有文档,查找这些文档中所有单词的计数。
首先,为了背景,我了解到Lucene依赖于类似于CONCISE的压缩位数组数据结构。在概念上,这个位数组对于每个不匹配术语的文档都保持0,对于每个匹配术语的文档都保持1。但酷/令人惊叹的部分是,这个数组可以高度压缩,并且在布尔运算方面非常快。例如,如果您想知道哪些文档包含术语“red”和“blue”,则获取与“red”对应的位数组和与“blue”对应的位数组,并将它们AND在一起,以获取与匹配文档相对应的位数组。
但是,Lucene如何快速确定与“bacon”匹配的所有文档中单词的计数?在我天真的理解中,Lucene必须将与bacon相关联的位数组与每个其他单词的位数组进行AND运算。我错过了什么吗?我不明白这怎么可能是有效的。此外,这些位数组是否必须从磁盘上取出?那听起来更糟糕!魔法是如何运作的?

1
你能否提供一下你所说功能的JavaDoc链接吗?我正在查找“过滤术语计数”,但没有查到相关内容。 - bcoughlan
2个回答

12
您可能已经知道,但再次强调一下,Lucene使用倒排索引。在这种索引技术中,创建了一个包含所有文档中出现的每个单词的字典,并针对每个单词存储关于该单词出现情况的信息,类似于此图像 enter image description here

为了实现这一点,Lucene将文档、索引及其元数据存储在不同的文件格式中。有关文件详细信息,请参阅此链接:http://lucene.apache.org/core/3_0_3/fileformats.html#Overview

如果您阅读了“文档编号”部分,则会发现每个文档都被赋予内部ID,因此当使用单词“consign”找到文档时,Lucene引擎具有对其元数据的引用。请参阅概述部分,以查看在不同的Lucene索引中保存哪些数据。现在我们已经了解了指向存储的文档的指针,那么Lucene可能会按以下方式之一获取它:
  1. 如果存储了文档,则真正计算单词数。
  2. 使用Term Dictionary、frequency和proximity数据获取计数。
最后一点,您正在使用哪个API来“快速确定所有单词的计数”?
图片来源:http://leanjavaengineering.wordpress.com/

在此处检查索引文件格式的详细信息:http://lucene.apache.org/core/8_2_0/core/org/apache/lucene/codecs/lucene80/package-summary.html#package.description

我不知道Lucene本身中的这个API是什么,但在Solr或ElasticSearch中,我会使用facet来执行此操作。基本上,我将运行一个“consign”的查询,然后对所有仅存在于包含“consign”的文档中的标记进行facet计数。 - JnBrymn

2
没有使用位集:这是倒排索引。每个术语映射到一组文档。在Lucene中,算法使用这些“列表”的迭代器工作,因此迭代器中的项是按需读取的,而不是一次性读取所有项。
此图显示了一个非常简单的并置算法,只使用next()操作:http://nlp.stanford.edu/IR-book/html/htmledition/processing-boolean-queries-1.html 在Lucene中,它的背后就像上面的图表一样。我们的列表是增量编码和位打包的,并且通过跳表进行增强,这使我们可以比上述算法更有效地进行交集(通过附加的advance()操作)。
DocIDSetIterator是Lucene中的“枚举器”。它有两种主要方法,next()和advance()。是的,你可以决定读取整个列表+将其转换为内存中的位集,并在该内存中实现此迭代器。如果使用CachingWrapperFilter,则会发生这种情况。

所以,对于给定的listOfDocIdsThatMatchQuery和倒排索引中的给定docIdList,在跑到跳表末尾之前,计算我们可以多少次为每个id在listOfDocIdsThatMatchQuery中执行docIdList.Advance(id)。无论这个计数是多少,都是该项术语的值。听起来正确吗? - JnBrymn
所有这些都保留在内存中吗?还是我们从磁盘上取出了一些东西?我想知道当语料库足够大以至于无法再放入内存时会发生什么。 - JnBrymn
如果您的操作系统将其缓存,页面就在RAM中了 :) 这本质上类似于BufferedInputStream:我们一次读取128个文档块,任何剩余部分都会逐个文档进行压缩。您还可以使用skiplist数据跳转到流的后面部分(它是块对齐的,因此如果术语很少且具有<128个帖子,则不会构建skiplists)。 - Robert Muir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接