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 但不用于 =