如何在 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
.
有些字符(运算符)会影响 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
.