【问题标题】:How search for thousands of possible keywords in a string如何在字符串中搜索数千个可能的关键字
【发布时间】:2011-05-26 18:59:02
【问题描述】:

我有一个包含数千个(大约 10,000 个)关键字的数据库。当用户在我的网站上发布博客时,我想自动搜索文本中的关键字,并用任何直接匹配的方式标记帖子。

到目前为止,我能想到的只是拉出整个关键字列表,循环遍历它,并检查帖子中是否存在每个标签……这似乎非常低效(即 10,000 个循环)。

有没有更常见的方法来做到这一点?我是否应该使用 MySQL 查询来限制它?

我想这不是一个完全罕见的任务。

【问题讨论】:

  • 好问题 - 这将帮助很多人(包括我自己!)

标签: php mysql performance keyword


【解决方案1】:

不,只是不要那样做。

与其循环遍历 10000 个元素,不如从句子或文本中提取单词,然后将其添加到 SQL 查询中,这样您就可以获得所有需要的记录。这肯定比您提出的解决方案更有效。

您可以使用 PHP 通过以下方式执行此操作:

$possible_keywords = preg_split('/\b/', $your_text, PREG_SPLIT_NO_EMPTY);

上面将分割单词边界上的文本,并且不会在数组中返回空元素。

然后你可以用类似于下面的方式创建 SQL 查询:

SELECT * FROM `keywords` WHERE `keywords`.`keyword` IN (...)

(只需将提取的单词的逗号分隔列表放在括号中)

您可能应该在进行查询之前过滤$possible_keywords 数组(以仅包含具有适当长度的关键字并排除重复项)并为keyword 列编制索引。

【讨论】:

  • 那么,也许首先通过全文搜索从数据库中收集相关标签,这应该将它们缩小到最大 100,然后循环?或者你有什么不同的想法?
  • +1 我喜欢这个答案 - 我有一个“呃!”阅读本文的时刻。
  • @johnnietheblack 不,我正在考虑从文本中提取单词(我不知道文本可能有多长),然后过滤结果(排除太短、太长和重复的单词),然后将其传递给 SQL 查询以仅执行一个查询,该查询将返回可应用于给定文本的任何关键字。当然有很多替代方案,但我认为您想获取包含在特定文本和数据库中的所有关键字 - 并且建议的解决方案是我现在能想到的最有效的解决方案。
  • 假设用户发布了“嗨,我的名字是 John”。然后你应该像这样查询数据库:SELECT * from keywords WHERE keyword IN ('hi', 'my', 'name', 'is', 'John')。确保表在keyword上有索引
  • @Jakob 这正是我的想法和描述。使用它比从数据库中检查每个关键字的 10000 步更有效,你不觉得吗? ;) 此外,如果数据库中的关键字不是,例如。少于4个字符,可以过滤掉所有较短的元素,将查询简化为... IN ('name', 'John')的形式。我希望这是优化 OP 想要创建的功能的一个很好的起点。
【解决方案2】:

我不知道您打算使用哪种语言,但如果您愿意的话,标准的 trie(前缀树)可以解决这个问题。

【讨论】:

    【解决方案3】:

    我想您可以动态构建一个正则表达式,使您能够匹配特定字符串中的关键字。您可以将所有这些打包在一个可以完成 grunt 工作的类中。

    class KeywordTagger {
      static function getTags($body) {
        if(preg_match_all(self::getRegex(), $body, $keywords)) {
          return $keywords[0];
        } else {
          return null;
        }
      }
    
      private static $regex;
      private static function getRegex() {
        if(self::$regex === null) {
          // Load Keywords from DB here
          $keywords = KeywordsTable::getAllKeywords();
    
          // Let's escape
          $keywords = array_map('KeywordTagger::pregQuoteWords', $keywords);
    
          // Base Regex
          $regex = '/\b(?:%s)\b/ui';
    
          // Build Final
          self::$regex = sprintf($regex, implode('|', $keywords));
        }
    
        return self::$regex;
      }
    
      private static function pregQuoteWords($word) {
        return preg_quote($word, '/');
      }
    }
    

    然后,你所要做的就是,当用户写一篇文章时,通过类运行它:

    $tags = KeywordTagger::getTags($_POST['messageBody']);
    

    为了稍微加快速度,您可以使用 memcached、APC 或旧的基于文件的缓存来缓存构建的正则表达式。

    【讨论】:

      【解决方案4】:

      嗯,我认为 PHP 的 stripos 已经相当优化了。如果您想进一步优化此搜索,则必须利用关键字之间的相似性(例如,不要先查找“foobar”,然后再查找“foobaz”,而是查找“fooba”,然后检查每个“fooba”,如果后跟一个“r”、一个“z”或无)。但这需要对关键字进行某种树状表示,例如:

      根(空字符串)

       |
      
      fooba
      
      /  \
      

      foobar foobaz

      是的,这是一个尝试。

      【讨论】:

        猜你喜欢
        • 2014-12-08
        • 1970-01-01
        • 1970-01-01
        • 2018-12-14
        • 2011-07-28
        • 2019-03-13
        • 1970-01-01
        • 2015-03-10
        • 2014-07-16
        相关资源
        最近更新 更多