【问题标题】:Implementing keyword comparison scheme (reverse search)实施关键字比较方案(反向搜索)
【发布时间】:2010-09-29 07:18:38
【问题描述】:

我有一个不断增长的关键字数据库。我需要解析传入的文本输入(文章、提要等)并查找数据库中的哪些关键字出现在文本中。关键字的数据库比文本大得多。

由于数据库不断增长(用户添加了越来越多的关键字来关注),我认为最好的选择是将文本输入分解为单词并将其与数据库进行比较。我的主要困境是实现这个比较方案(这个项目将使用 PHP 和 MySQL)。

最简单的实现是针对关键字表创建一个简单的 SELECT 查询,其中有一个巨大的 IN 子句列出所有找到的关键字。

SELECT user_id,keyword FROM keywords WHERE keyword IN ('keyword1','keyword2',...,'keywordN');

另一种方法是在内存中创建一个哈希表(使用 memcache 之类的东西)并以相同的方式对其进行检查。

有没有人对这种搜索有任何经验,并且对如何更好地实现这一点有任何建议?我还没有尝试过这些方法中的任何一种,我现在只是在收集想法。

【问题讨论】:

  • 这里有很多问题。有多少个独特的关键字(每个用户是否倾向于拥有相似的关键字)?你有多少内存?您需要数据是最新的还是可以定期对其进行一些处理?
  • 正如第一段所说,关键字数据库的大小比文本中的单词大得多。文章在收到时进行处理,并向针对特定关键字注册的用户发送警报。目前内存不是问题。
  • 我看不出你将如何变得更快,然后在内存(堆)中复制你的关键字表并建立索引并执行你在上面所做的事情或加入每篇文章在内存中创建的内容关键字表。(参与用户也会有所帮助)。不过,复制必须在您的应用中。
  • 这也可能有用 - 我正在研究类似的问题:stackoverflow.com/questions/47762/how-to-ranking-search-results

标签: php mysql search keyword tokenize


【解决方案1】:

在文本流中搜索多个关键字的经典方法是Aho-Corasick finite automaton,它在要搜索的文本中使用时间线性。您需要进行细微的调整以仅在单词边界上识别字符串,或者只检查找到的关键字并确保它们没有嵌入更大的单词中可能会更简单。

您可以在fgrep 中找到一个实现。更好的是,Preston Briggs 用 C 语言编写了一个非常不错的实现,它完全可以进行您所说的关键字搜索。 (它在程序中搜索“有趣的”标识符的出现。)Preston 的实现作为Noweb literate-programming tool 的一部分分发。你可以找到一种方法从 PHP 调用这段代码,或者你可以用 PHP 重写它——识别本身大约是 220 行 C,而主程序是另外 135 行。

所有提议的解决方案,包括 Aho-Corasick,都具有以下共同特性:

  • 一个预处理步骤,所花费的时间和空间与数据库中的关键字数量成正比。

  • 一个搜索步骤,所花费的时间和空间与文本长度加上找到的关键字数量成正比。

Aho-Corasick 在搜索步骤中提供了相当好的比例常数,但如果您的文本很小,这无关紧要。事实上,如果您的文本很小而数据库很大,您可能希望尽量减少预处理步骤中使用的内存量。来自the world's fastest scrabble program 的 Andrew Appel 的 DAWG 数据结构可能会成功。

【讨论】:

  • 我不是在文本中搜索单词 - 我将文本标记为单词,然后与单词数据库进行比较。
  • Preston 的代码完全符合您的要求。它将标记化和搜索结合在一个步骤中。您提供关键字列表,构建自动机,然后用自动机点击字符串。
  • 你还是不明白。我不想根据文本搜索数据库中的每个关键字 - 但反过来。将文本分解成单词,并找出数据库中存在哪些单词。数据库比文本大得多。
  • 好的,添加了关键信息后,对数据库进行任何简单的预处理步骤都可以——将所有关键字添加到哈希表中。然后标记你的小文本,在哈希表中查找每个单词,你就完成了。 Aho-Corasick 也可以,但您不需要复杂性。
  • 是的,我对预处理步骤不是很感兴趣,我已经选择了攻击它的方法。在一个非常大的表格中搜索大量关键字是我所关心的。
【解决方案2】:

一般来说,

  1. 将文本分解成单词

    b.将单词转换回规范根形式

    c。去掉常用的连词

    d。去除重复项

  2. 将单词插入临时表,然后对关键字表进行内部联接, 或者(按照您的建议)将关键字构建到复杂的查询条件中

缓存一个 3 或 4 个字母的哈希数组可能是值得的,用它来预过滤潜在的关键字;您将不得不尝试在内存大小和效率之间找到最佳平衡点。

【讨论】:

  • 对内存表与内存缓存阵列的性能有何经验?我认为数组访问会更快?
  • 使用 MEMORY 存储引擎对表执行 JOIN 比连接 memcached 更容易。
【解决方案3】:

我不是 100% 清楚你在问什么,但也许你正在寻找的是 inverted index

更新:

您可以使用倒排索引一次匹配多个关键字。

将新文档拆分为标记,并将与文档标识符配对的标记插入倒排索引表中。一个(相当非规范化的)倒排索引表:

inverted_index
-----
document_id keyword

如果您手动搜索 3 个关键字:

select document_id, count(*) from inverted_index
  where keyword in (keyword1, keyword2, keyword3)
  group by document_id 
  having count(*) = 3

如果您有一个您关心的关键字表,只需使用内连接而不是 in() 操作:

keyword_table
----
keyword othercols

select keyword_table.keyword, keyword_table.othercols from inverted_index 
   inner join keyword_table on keyword_table.keyword=inverted_index.keyword
   where inverted_index.document_id=id_of_some_new_document

这是否更接近你想要的?

【讨论】:

  • 他正试图做相反的事情。在这种情况下,对于传入的大量测试,找出数据库已经知道的关键字。
  • 正如尼克所说,我正在寻找一次找到匹配多个关键字的记录的最佳方法。
  • 我在问题中提出了这个确切的解决方案。我正在寻找替代方案,因为这可能是最低效的解决方案。这也不是针对文档表 - 而是针对用户表。一次只有一个文档被标记化并与用户关键字进行比较
  • 我建议如果你正确索引它实际上会非常有效。
  • 有可能,我不排除这个选项。只是在寻找更多的方法来解决它。
【解决方案4】:

您是否考虑过使用全文解决方案,例如Sphinx

我在这里说的是我的帽子,因为我自己没有使用它。但它作为一种高速全文搜索解决方案受到了广泛关注。它可能比您使用的任何关系解决方案都具有更好的扩展性。

这是一个blog,关于在 MySQL 中使用 Sphinx 作为全文搜索解决方案。

【讨论】:

  • 其实我有很多使用狮身人面像的经验,而且很棒。但这不是文本搜索 - 而是与关键字列表完全匹配。
【解决方案5】:

我会在这里做两件事。

首先(这与问题没有直接关系)我会按用户分解和划分用户关键字。拥有更多表和更少数据,理想情况是在不同服务器上进行分布式查找,其中切片或用户范围存在于不同切片上。又名,usera 的所有数据都存在于切片 1 上,userb 存在于切片 2 上,等等。

其次,我需要某种内存哈希表来确定关键字的存在。这也可能被联合起来分发查找。对于 n 个存在关键字的服务器,对关键字进行哈希处理并按 n 对其进行修改,然后将这些关键字的范围分布在所有 memcached 服务器上。这种快速方法可以让您说出正在监视的关键字 x,对其进行哈希处理并确定它 将在哪个服务器上运行。然后进行查找并收集/聚合被跟踪的关键字。

到那时,您至少会知道哪些关键字正在被跟踪,并且您可以获取用户切片并执行后续查找以确定哪些用户正在跟踪哪些关键字。

简而言之:SQL 在这里并不是一个理想的解决方案。

【讨论】:

  • 按用户划分关键字表可能不是一个好主意 - 每个用户只有 4-5 个关键字,而且用户数量很多 - 会导致表格很多。正如你所说,我倾向于使用非 SQL 解决方案。
  • 即使用户之间存在高关键字分布,您仍然需要迭代更小的数据集。在大多数情况下,基于用户的数据分区非常有效,并且不需要大量工作。
  • 其他人建议从文章关键字创建的内存表或临时表将尽可能优化。你有什么可以支持SQL在这里不是一个理想的解决方案吗?
【解决方案6】:

我破解了一些代码,用于使用 dawg 扫描多个关键字(正如上面提到的 Scrabble 论文所建议的那样),尽管我是根据第一原理编写的,但我不知道它是否类似于 AHO 算法。

http://www.gtoal.com/wordgames/spell/multiscan.c.html

在我第一次将代码发布到文字游戏程序员邮件列表后,一位朋友对我的代码进行了一些修改,他的版本可能更有效:

http://www.gtoal.com/wordgames/spell/multidawg.c.html

相当不错...

G

【讨论】:

    猜你喜欢
    • 2016-04-11
    • 2012-08-26
    • 1970-01-01
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-15
    • 1970-01-01
    相关资源
    最近更新 更多