如何在 MariaDB/MySQL 中转义字符串以进行全文搜索

How to escape string for full-text search in MariaDB/MySQL

有些字符(运算符)会影响 MariaDB 中的全文搜索行为。它们是 +-<>()~*",它们的功能在 documentation.

中进行了描述

我希望能够搜索包含这些运算符之一的单词,并且我希望 MariaDB 将其作为普通字符而不是运算符来处理。我该怎么做?

示例:

让我们用全文索引创建table:

CREATE TABLE users (username TEXT, FULLTEXT(username)) ENGINE=InnoDB;

INSERT INTO users(username) VALUES ('joseph'), ('jose'), ('jose*');

现在我想搜索恰好包含 jose*:

的行
SELECT * FROM users WHERE MATCH(username) AGAINST('jose*' IN BOOLEAN MODE);
+----------+
| username |
+----------+
| joseph   |
| jose     |
| jose*    |
+----------+

但我只想要 jose* 的行。当我尝试以我期望的方式转义该字符串时,结果相同。

SELECT * FROM users WHERE MATCH(username) AGAINST('jose\*' IN BOOLEAN MODE);
+----------+
| username |
+----------+
| joseph   |
| jose     |
| jose*    |
+----------+

SELECT * FROM users WHERE MATCH(username) AGAINST('jose\*' IN BOOLEAN MODE);
+----------+
| username |
+----------+
| joseph   |
| jose     |
| jose*    |
+----------+

在 MariaDB/MySQL 中为全文搜索转义字符串的正确方法是什么?

全文搜索是一种有效搜索出现在(全文)文本任何位置的单词(或单词开头)的工具。如果您的数据不包含分隔的 "words"(以任何您想要定义它们的方式),则全文索引不是您任务的正确工具(因为它将完全无用)。默认情况下,* 是一个单词分隔符,就像 space(例如 'abc*def''abc def' 是两个单词,在全文索引中有两个单独的条目, none 其中将包含 *)。您可以指定要作为分隔符的内容,但 MySQL 不支持通过在搜索表达式中转义它们来动态指定它;您需要在创建索引时执行此操作,因此索引实际上将包含 jose*,而不仅仅是 jose

如果您没有单词(或一组非常有限的分隔符),您可以使用例如username = 'jose*username like 'jose*' 或类似的;或者,您可以使用 regular expressions,它很慢,但可以作为复杂需求的后备工具(例如,如果全文不适合您的情况),其中全文索引不可用(and/or 您无法更改配置以满足您的要求)。

要更改 MySQL 将哪些字符视为定界符,您可以更改字符映射,请参阅 Adding a Collation for Full-Text Indexing:

  • index.xml 添加新排序规则
  • 将该排序规则添加到字符文件(例如 latin1.xml),并编辑 ctype 以将特定字符定义为(非)定界符;仅 *,将其更改为“48 10 10 10 10 10 10 10 10 10 01 10 10 10 10 10”);对您希望可搜索的所有字符执行此操作(但再次请记住,如果您没有至少一个剩余的分隔符,则全文搜索将毫无用处)。
  • 重新启动后,对您的列使用此排序规则(例如 ... (username TEXT collate 'latin1_fulltext_ci', ...)并重新创建全文索引,MySQL 会将这些字符包含到索引中。
  • 请记住,您需要在每台要使用此行为的服务器上执行此操作

现在以下三个搜索应该 return 预期结果:

... MATCH(username) AGAINST('"jose*"' IN BOOLEAN MODE);

... MATCH(username) AGAINST('jose*');

... MATCH(username) AGAINST('"jose*"');

"..." 将查找完全匹配(例如单词组合);它的工作方式类似于转义,但不完全是,因为它只适用于非定界符。

... MATCH(username) AGAINST('jose*' IN BOOLEAN MODE);

用于 InnoDB(它将被视为通配符),但将用于 MyISAM(它们之间的一些细微差别之一)。

如果你真的想使用布尔模式,但需要一个不同于 * 的通配符,你可以使用 ft_boolean_syntax, although due to a bug in InnoDB 定义一个不同的通配符符号,这也只适用于 MyISAM。它也是一个全局设置,因此会改变其他表(和数据库)中所有其他全文搜索的行为。您可能必须指定要使用此模式实现的目标,以查看是否有办法使全文搜索满足这些要求,但最终,您可能不得不退回到使用 like.