【问题标题】:How to escape string for full-text search in MariaDB/MySQL如何在 MariaDB/MySQL 中转义字符串以进行全文搜索
【发布时间】:2018-09-01 20:15:22
【问题描述】:

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

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

示例:

让我们创建带有全文索引的表:

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 中转义字符串以进行全文搜索的正确方法是什么?

【问题讨论】:

    标签: mysql full-text-search mariadb


    【解决方案1】:

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

    如果您没有单词(或一组非常有限的分隔符),您可以使用例如username = 'jose*username like 'jose*' 或类似名称;或者,您可以使用速度较慢的regular expressions,但是对于全文索引不可用的复杂要求(例如,如果全文不适用于您的情况)的后备工具(和/或您无法更改配置以适应它符合您的要求)。

    要更改 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 会将这些字符包含到索引中。
    • 请记住,您需要在要使用此行为的每台服务器上执行此操作

    现在以下三个搜索应该返回预期的结果:

    ... 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定义一个不同的通配符,尽管由于bug in InnoDB,这也只适用于MyISAM。它也是一个全局设置,因此会改变其他表(和数据库)中所有其他全文搜索的行为。您可能必须指定要使用此模式实现的目标,看看是否有办法让全文搜索满足这些要求,但最终,您可能不得不回退到使用 like

    【讨论】:

      猜你喜欢
      • 2014-12-17
      • 1970-01-01
      • 2014-08-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      • 1970-01-01
      • 2011-10-27
      相关资源
      最近更新 更多