MySQL MATCH() AGAINST() FULLTEXT Index - 结合短语匹配实现部分字符串匹配

Posted

技术标签:

【中文标题】MySQL MATCH() AGAINST() FULLTEXT Index - 结合短语匹配实现部分字符串匹配【英文标题】:MySQL MATCH() AGAINST() FULLTEXT Index - Achieve Partial String Match Combined with Phrase Match 【发布时间】:2020-10-31 17:22:07 【问题描述】:

我有一个表,其列 content 具有 FULLTEXT 索引

我想利用 MATCH() 处理大文本的速度。

我希望搜索尽可能准确。

当我以这种方式搜索短语字符串“large truck”时:

SELECT * FROM MyTable WHERE MATCH(content) AGAINST('"large truck"' IN BOOLEAN MODE);

遗漏了一些实例。

我的表:

|   content    |
----------------
|Large \n truck| FOUND ✓
----------------
|large truck   | FOUND ✓
----------------
|large trucks  | *PLURAL MISSED!
----------------
|large truckl  | *TYPE-O MISSED!

如果我使用标准的 LIKE / 通配符 方法:

SELECT * FROM  `MyTable` WHERE  `content` LIKE  '%large truck%'

我的表:

|   content    |
----------------
|Large \n truck| *MISSED!
----------------
|large truck   | FOUND ✓
----------------
|large trucks  | FOUND ✓
----------------
|large truckl  | FOUND ✓

看来我也不能将 PHRASE 搜索与通配符一起使用:

SELECT * FROM MyTable WHERE MATCH(content) AGAINST('"large truck*"' IN BOOLEAN MODE); **DOES NOT WORK**

SELECT * FROM MyTable WHERE MATCH(content) AGAINST('"large truck"*' IN BOOLEAN MODE); **DOES NOT WORK**

所以...

如何成功使用 MATCH() AGAINST() 搜索短语,并返回所有实例 - 甚至 不区分大小写的部分字符串匹配?

【问题讨论】:

【参考方案1】:

这是一个使用 REGEXP 的快速破解,但它没有解决问题,因为它不使用全文索引:

SELECT * 
FROM MyTable 
WHERE content REGEXP("large[[:space:]]+truck*");

当然你也可以通过不使用精确词组的方式搜索来使用 FT 索引:

SELECT * 
FROM MyTable 
WHERE MATCH(content) AGAINST('+large +truck*' IN BOOLEAN MODE);

但这最终会包括您不想要的记录,因为它与精确短语搜索不同。

不幸的是,无论您使用 InnoDB 还是 MyISAM,短语搜索(双引号 - “”)和截断运算符(通配符 - *)都不起作用。 does not work with the distance operator 也带有 InnoDB(可能来自同一来源)。我想这与全文索引的数据如何存储在本质上有关。

【讨论】:

【参考方案2】:

我经常使用 FT 的技巧是分两步完成:

    做一个MATCH,希望得到所有想要的文本,但可能会有一些额外的结果。 AND 与另一个条件 - LIKE(更快)或REGEXP(更强大)。

MATCH 会因为 FT 而变快;另一部分将第二个执行,所以它会很快,因为要检查的行不多。

这符合您的条件:

SELECT * FROM MyTable
    WHERE MATCH(content) AGAINST('+large +truck*' IN BOOLEAN MODE)
      AND content REGEXP "large[[:space:]]+truck";

换个说法,查询将运行如下:

    假设表有 10K 行。 将评估 FT 表达式。 MATCH 会非常快(因为它的设计方式)。它会在content 的任何位置找到所有带有“large”和“truck*”的行。现在,假设有 30 行满足这一要求。 评估WHERE 的其余部分。但它只针对那 30 行。因此,尽管REGEXP 的成本很高,但并不经常这样做。 然后可能返回 14 行。

最终结果是整个查询“快速”运行,这是您的要求之一。

注意:我需要第二部分来防止这些

large green truck
the truck is large

根据版本,您可能需要此 REGEXP:"large\\s+truck"

【讨论】:

谢谢。我试图理解“另一部分将第二个执行,所以它会很快,因为没有多少行要检查”的逻辑。为什么“其他人会更快”?我的印象是 MATCH() 将是 FULLTEXT 索引上最快的方法。出于这个原因,我不再使用 LIKE。现在使用 LIKE 和 MATCH 的组合 - 似乎它会使我的时间加倍?在 MATCH 之后执行 LIKE 是否有条件使 LIKE 更快?谢谢。只是想理解。 @Stnfordly - 我添加了一个改写。是的,添加 REGEXP 所需的时间可能是 FT 部分的两倍。但它只有两倍长。只做正则表达式可能会慢 100 倍。只做 FT 会给你的行太少(见你的问题)或太多的行(根据我的版本)。 (我之所以选择 REGEXP,是因为我没有找到对这种特殊情况有用的 LIKE。) 嗨。谢谢。您是说您的查询示例将首先执行 MATCH(),然后仅对它在 MATCH() 中找到的内容执行 REGEX? 我要试试这个 - 但正则表达式不会找不到“|Large \n truck|”实例? @Stnfordly - 如果\n 确实是回车,那么它被视为“空格”(又名空格);如果它是两个字符(反斜杠和 n),那么事情会变得更加混乱。

以上是关于MySQL MATCH() AGAINST() FULLTEXT Index - 结合短语匹配实现部分字符串匹配的主要内容,如果未能解决你的问题,请参考以下文章

MySQL 部分单词匹配结合 MATCH...AGAINST 和 LIKE 作为备份

mysql全文索引用于 MATCH() AGAINST 但不用于 =

Mysql利用match...against进行全文检索

MySQL match() against() - 按相关性和列排序?

mysql 全文模糊搜索MATCH AGAINST方法

MySQL MATCH() AGAINST() FULLTEXT Index - 结合短语匹配实现部分字符串匹配