我有一个包含数千个(约10,000个)关键字的数据库。当用户在我的网站上发布博客时,我希望自动在文本中搜索关键字,并使用任何直接匹配标记帖子。
到目前为止,我所能想到的只是拉出整个关键字列表,循环遍历它,并检查帖子中是否存在每个标签......这看起来非常低效(即10,000个循环)。
有更常见的方法吗?我是否应该使用MySQL查询来限制它?
我认为这不是一项非常罕见的任务。
答案 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
是的,这是特里。