【问题标题】:Indexing full text search queries for efficient fanout索引全文搜索查询以实现高效扇出
【发布时间】:2014-07-23 03:37:29
【问题描述】:

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

此问题的一个简单示例是 Twitter 流式搜索之类的工具的实现:在每秒有数千条新推文的情况下,有效地仅选择搜索查询可能与传入推文匹配的流式订阅者。

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

对于单词查询,一个实现是显而易见的:只需对传入文档进行标记,然后搜索词->(订阅者列表)的映射,但是当布尔查询成为可能时,事情变得更加困难。事实上,这个问题比全文搜索更普遍,但在这种情况下最容易理解。还有许多其他示例需要将大量布尔项组合起来以优化评估它们的成本。

例如,假设有 3 个搜索订阅:

  1. 谷歌和眼镜
  2. 谷歌和分析
  3. ((Glass 和 Google)NOT Knol)或 Twitter

一种可能性是将查询解析为树,然后访问每个节点,提取术语,并使用“术语映射”方法,但这需要针对每个术语的传入文档重新评估订阅者查询.如果订阅者足够多,这将很快开始变慢。

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

    一种方法是使用将术语映射到查询的简单字典。因此,鉴于这四个查询:

    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 上的红色玻璃来自 Google”这样的句子。

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

    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 程序用于在一次遍历文本中匹配多个正则表达式的算法非常相似。其中的细节远远超出了我在这里的简短说明中的能力。您可能想查找 Dobb 博士在期刊上发表的一篇名为“Parallel Pattern Matching and fgrep”之类的旧文章,据我回忆,这篇文章对如何做到这一点有相当好的解释。 (快速搜索没有找到文章文本,但您可能运气更好。)您还需要阅读原始的 Aho-Corasick 论文:Efficient String Matching: an Aid to Bibliographic Search。这讨论了并行模式匹配文字字符串,但基本思想适用于匹配正则表达式或布尔搜索查询。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-24
        • 2016-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-06
        相关资源
        最近更新 更多