如何使用 PHP 和 MySQL 全文布尔模式为 MVP 实现自动更正/替代拼写搜索系统

mic*_*all 7 php mysql search full-text-search autocorrect

注意:

  • 我可以使用像PspellAspellHunspell这样的字典,但这种情况不适用于企业名称或城市。此外,我不想查询数据库以获取所有建议的更正(尤其是每 300 毫秒触发一次预输入)(有关这些词典的更多问题

  • 我可以使用补充搜索引擎,例如ElasticsearchSphinx,但我没有为此 MVP 分配的财务或人力资源。正如在这个答案中所建议的,MySQL 全文应该足够了,而且不那么复杂。

可用技术:

MySQL 5.7 InnoDB,在所需字段上使用全文索引布尔模式,使用 php-fpm 的 PHP 7.0,使用 Centos 7 的 VPS,corejs-typeahead

目标:

我想从 MySQL 返回用户搜索的结果,无论是正确的搜索还是拼写错误的搜索。

常见问题示例:

连字符

  • 在部分搜索中搜索带有连字符“-”的单词很烦人。

潜在的解决方案:

  • 我必须将搜索查询包含在 "" 中以搜索短语(请参阅 [在此处输入链接描述] [来自 man 的示例]。仍然找不到名为 '"le dé-k-lé"' 的企业,因为to ft_min_word_len=3AND "de" 和 "le" 是停用词(在许多语言中过于频繁)

  • 我可以,但我不会进入以下解决方案,因为我不够熟练或这是不合适的。根据 MySQL 手册的建议修改 MySQL 源修改字符集文件添加新的排序规则。比如我以后想用减号(-)来过滤掉一些单词,就不行了。

撇号/单引号

  • 带有撇号的词经常在没有撇号的情况下搜索(尤其是在手机上)。例如,“A'trego”将被输入为“atrego”。它肯定会被全文索引遗漏,因为“A'trego”被认为是 2 个词“a”和“trego”

双字母遗漏

  • 带有双字母的单词经常被用户遗漏或拼错。例如,“Cerruti”可能拼错为“Cerutti”或“Cerruti”等。

潜在的解决方案:

  • 我可以使用 SOUNDEX() 但它主要是为英语设计的
  • 我可以使用levenshtein 函数,但对于大型数据集(例如包含所有欧洲城市的表)来说会很慢。看来还得做fullscan,再加上typeahead,绝对不是办法。尽管这里这里的一些建议很有趣

外来词和复数形式

  • Exonyms 在搜索中可能难以处理(从用户的角度来看)。例如,意大利的城市Firenze在德语中被命名为Florenz,在法语中被命名为Florence等。人们在城市本身时经常会从外来地名转换为当地名称。以前的算法不会正确处理外来词。此外,没有外来地名的城市名称也不是一个好的用户体验。i18n 也不好。

潜在的解决方案:

  • 使用Pspell或其他类似库的自制字典将返回在 MySQL 中存储和索引的字符串。

DIACRITICS - 类似于外来词,用户可能难以处理。i18n 也一样。例如,尝试在 ?ód? 中找到一家餐厅?在波兰使用您常用的键盘。波兰人和英国人绝对不会以同样的方式处理这个字符串。

潜在的解决方案: - 潜在的解决方案已经在前端由 corejs-typeahead 库使用的映射管理。剩下的用PHP清理$strCleaned = iconv('UTF-8', 'utf-8//TRANSLIT', $str);

缩写和首字母缩略词 - 缩写可互换用于公司名称,尤其是蓝筹股。例如,LVMH、HP、GM、GE、BMW。城市也是如此。使用缩写搜索时不返回公司或城市是用户体验方面的一大失败。

潜在的解决方案:

此列表并未详尽列出问题或潜在解决方案。

我的解决方案

我的解决方案是从这里的答案中得到启发和推断

基本上,在每次搜索之前,用户输入的字符都应该去掉,比如撇号、连字符;简化以删除相似的连续字母。

那些清理过的替代词将存储在一个用全文索引索引的列中。

这个解决方案很简单,并且适当地响应了我的要求。但是我的短暂经验告诉我要谨慎,因为它肯定会有缺点(我尚未确定)。

下面是我的代码的简化版本。

PHP

// Get input from the typeahead searched word
$query = (!empty($_GET['q'])) ? strtolower($_GET['q']) : null;

// end the script if empty query
if (!isset($query)) {
    die('Invalid query.');
}

// Clean and Strip input
$query = trim($query);
$query = str_replace("'","",$query);
$query = str_replace("-","",$query);
$query = preg_replace('{(.)\1+}','$1',$query);

// filter/sanitize query
if (!preg_match("/^([0-9 '@&\-\.\pL])+$/ui", $input[$field]) !== false) {exit;}
$query = mysqli_real_escape_string($conn, $query); // I will switch to PDO prepared statement soon as mysqli_real_escape_string do not offer enough protection
Run Code Online (Sandbox Code Playgroud)

MySQL查询

SELECT DISTINCT
company.company_name,
MATCH (company_name, company_alternative) AGAINST ('$query*' IN BOOLEAN MODE) AS relevance

FROM company

WHERE 
MATCH (company_name, company_alternative) AGAINST ('$query*' IN BOOLEAN MODE)
AND relevance > 1

ORDER BY
CASE
WHEN company_name = '$query' THEN 0
WHEN company_name LIKE '$query%' THEN 1
WHEN company_name LIKE '%$query' THEN 2
ELSE 3
END

LIMIT 20
Run Code Online (Sandbox Code Playgroud)

MySQL表

提醒一下,我在 (company_name,company_alternative) 上有一个两列全文索引

**company_name**    |   **company_alternative**
l'Attrego           |   lattrego latrego attrego atrego
le Dé-K-Lé          |   dekle dekale decale
General Electric    |   GE  
Run Code Online (Sandbox Code Playgroud)

我已经确定的解决方案的缺点

  • 在我手动将其添加到alternative_name列中或实施机器学习过程之前,替代词不会包含常见的拼写错误。因此,难以管理且不可扩展(由于我已经收集了所有搜索查询,因此机器学习可以消除这个缺点并不会太困难)。
  • 我必须管理一个动态且复杂的停用词列表
  • 由于降低ft_min_word_len到 2,我必须重建索引

所以我的问题是, 如何使用 PHP 和 MySQL 全文布尔模式为 MVP 实现自动更正/替代拼写搜索系统?,可以改写为,

  • 我的解决方案是最不可扩展的吗?

  • 你看到我没有看到的缺点吗?

  • 如果这是一个合理的方法,我该如何改进?