如何在字符串中搜索数千个可能的关键字

时间:2011-05-26 18:59:03

标签: php mysql performance keyword

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

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

有更常见的方法吗?我是否应该使用MySQL查询来限制它?

我认为这不是一项非常罕见的任务。

4 个答案:

答案 0 :(得分:7)

不,只是不要这样做。

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

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

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

以上内容将分割单词'boundary'上的文本,并且不会返回数组中的空元素。

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

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

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

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

答案 1 :(得分:3)

我不知道您打算使用哪种语言,但如果您对此感到满意,则标准trie(前缀树)可以解决此问题。

答案 2 :(得分:3)

我猜您可以动态构建正则表达式,这样您就可以匹配特定字符串中的关键字。你可以将所有这些包装在一个完成咕噜声工作的类中。

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或一个古老的基于文件的缓存来缓存构建的正则表达式。

答案 3 :(得分:2)

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

root(空字符串)

 |

fooba

/  \

foobar foobaz

是的,这是特里。