索引全文搜索查询以实现高效扇出

时间:2014-06-02 22:34:15

标签: algorithm data-structures full-text-search

在考虑我可能想要构建的各种应用程序的设计时,在某些情况下,我需要根据它们是否匹配来扇出一系列传入事件用户提供的大量全文搜索查询。

这个问题的一个简单示例是Twitter流媒体搜索等工具的实现:每秒发送数千条新推文,高效选择搜索查询可能与传入推文相匹配的流媒体订阅者。

问题的陈述类似于"反向全文搜索",其中全文是查询,搜索结果是与该文本匹配的搜索查询。

对于单项查询,实现是显而易见的:简单地标记化传入的文档,然后搜索term->(订阅者列表)的地图,但是当布尔查询可能时,事情变得更加困难。事实上,问题比全文搜索更普遍,但在该上下文中最简单的理解。还有许多其他例子,其中大量布尔术语需要以某种方式组合以优化评估它们的成本。

例如,想象3个搜索订阅:

  1. Google和Glass
  2. Google AND Analytics
  3. ((Glass AND Google)NOT Knol)或Twitter
  4. 一种可能性是将查询解析为树,然后访问每个节点,提取术语,并使用"术语映射"但是,这需要针对每个术语针对传入文档重新评估订阅者查询。有足够的订阅者,这将很快开始变慢。

    相反,我想知道是否有一个记录良好的方法可以将查询重写为单个查询,其中结果可以评估一次,并且树节点使用已知的订阅者查询列表进行注释。匹配树中该点的任何文档。

    例如,可能会重写上述查询,以便存在term->(查询树)的映射,例如:

    Google -> (Analytics[2] Glass[1,3]) Twitter -> ([3])

    是否有任何现有的公开记录系统可以做到这样的事情?理想情况下,该解决方案允许逐步添加和删除订阅者,而无需重复整个结构的一些昂贵步骤。

2 个答案:

答案 0 :(得分:3)

执行此操作的一种方法是使用将术语映射到查询的简单字典。所以考虑到这四个问题:

Query1: Google AND Glass
Query2: Google AND Analytics
Query3: ((Glass AND Google) NOT Knol) OR Twitter
Query4: Quick AND red AND fox

您构建了一个词典,用术语键入:

Google: Query1, Query2, Query3
Glass: Query1, Query3
Analytics: Query2
Knol: Query3
Twitter: Query3
Quick: Query4
red: Query4
fox: Query4

现在,考虑一句如“knol上的红色玻璃来自谷歌。”

解析每个单词并在字典中查找。对于字典中的每个单词,将其查询列表添加到主查询列表中。此外,对于在字典中找到的每个单词,将其添加到相关单词的哈希表中。在此步骤结束时,您将拥有两个结构:要检查的查询列表以及相关单词列表:

Queries list: Query1, Query2, Query3, Query4
Relevant words: Google, Glass, Knol, red

现在处理每个查询,检查单词是否在相关单词列表中。

例如,对于Query1,您需要检查相关的单词列表是否包含Google和Glass。

这种复杂性并不算太糟糕。您对文本中的每个已解析单词都有O(1)查找。对于在解析阶段识别的每个查询,您对相关的单词哈希表进行了一些数字N,O(1)查找。在进行布尔评估时涉及一些非常少量的逻辑,但大多数查询将是简单的“所有单词”或“任何单词”类型的查询(即“this AND that”,或“this OR that”)。

这个模型的好处在于它很容易扩展到多个处理器。您可以在单个线程中解析单词,将它们推送到并发队列。多个线程为该队列提供服务,执行查找并构建自己需要检查的查询列表。完成所有这些查找后,您将合并多个线程中的查询列表,并再次将它们放在多个线程可以服务的并发队列中。

假设你有一百万个查询,平均每个五个单词(这可能是一个很大的平均值)。这里绝对最坏的情况是有些文本包含来自每个查询的至少一个单词。所以你有一个100万个查询的列表来检查传递2.最坏的情况是,这是500万字典查找。

此算法的第一遍是O(n),其中n是传入文本中的单词数。这将创建k个查询列表。第二遍是O(km),其中m是每个查询的平均单词数。

这种方法的优点在于它的简单性,它适用于大量的查询,具体取决于您提供的文本的大小。有一种可能更快的方式,但它涉及的更多。

不是构建将术语映射到查询的字典,而是使用修改后的Aho-Corasick字符串搜索算法,该算法非常类似于Unix fgrep程序用于在单个传递中匹配多个正则表达式的算法。文本。这些细节超出了我在这里简短解释的能力。你可能想要找到一篇名为“Parallel Pattern Matching and fgrep”的老博士Dobb的期刊文章,我记得这篇文章对于如何完成这一过程有一个相当不错的解释。 (快速搜索没有找到文章文本,但你可能会有更好的运气。)你还想阅读最初的Aho-Corasick论文:Efficient String Matching: an Aid to Bibliographic Search。这讨论了并行模式匹配文字字符串,但基本思想适用于匹配正则表达式或布尔搜索查询。

答案 1 :(得分:1)

如果您可以将查询解析为布尔表达式,那么您所拥有的是一组规则,输入变量是搜索文本中是否存在术语。对于每个搜索文本,您可以使用解析+表查找或Aho-Corasick来确定存在哪些术语,然后使用Rete算法的实现(例如http://en.wikipedia.org/wiki/Drools)来确定在给定输入的情况下触发哪些规则。 / p>

(或者,你可以批量输入你的输入文本,从中构建一个小的文本搜索数据库,然后运行你的查询。我的猜测是,如果你能够在查询运行之间等待足够长的时间,这将停止愚蠢的低效率。文本搜索数据库大小可以与组合查询的大小相比较。)