Documentation Home
MySQL 8.0 参考手册  / 第 12 章函数和运算符  / 12.10 全文搜索功能  /  12.10.1 自然语言全文搜索

12.10.1 自然语言全文搜索

默认情况下或使用IN NATURAL LANGUAGE MODE修饰符,该 函数针对文本集合MATCH()对字符串执行自然语言搜索 。集合是包含在 索引中的一组一个或多个列。搜索字符串作为 的参数给出。对于表中的每一行,返回一个相关值;也就是说,搜索字符串与列表中指定列中该行中的文本之间的相似性度量。 FULLTEXTAGAINST()MATCH()MATCH()

mysql> CREATE TABLE articles (
    ->   id INT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY,
    ->   title VARCHAR(200),
    ->   body TEXT,
    ->   FULLTEXT (title,body)
    -> ) ENGINE=InnoDB;
Query OK, 0 rows affected (0.08 sec)

mysql> INSERT INTO articles (title,body) VALUES
    ->   ('MySQL Tutorial','DBMS stands for DataBase ...'),
    ->   ('How To Use MySQL Well','After you went through a ...'),
    ->   ('Optimizing MySQL','In this tutorial, we show ...'),
    ->   ('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
    ->   ('MySQL vs. YourSQL','In the following database comparison ...'),
    ->   ('MySQL Security','When configured properly, MySQL ...');
Query OK, 6 rows affected (0.01 sec)
Records: 6  Duplicates: 0  Warnings: 0

mysql> SELECT * FROM articles
    -> WHERE MATCH (title,body)
    -> AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----+-------------------+------------------------------------------+
| id | title             | body                                     |
+----+-------------------+------------------------------------------+
|  1 | MySQL Tutorial    | DBMS stands for DataBase ...             |
|  5 | MySQL vs. YourSQL | In the following database comparison ... |
+----+-------------------+------------------------------------------+
2 rows in set (0.00 sec)

默认情况下,搜索以不区分大小写的方式执行。要执行区分大小写的全文搜索,请对索引列使用二进制排序规则。例如,latin1可以为使用字符集 of 的列指定排序规则 oflatin1_bin以使其在全文搜索中区分大小写。

在子句MATCH()中使用 when 时WHERE,如前面所示的示例中所示,只要满足以下条件,返回的行将自动按照相关性最高的顺序排在第一位:

  • 不得有明确ORDER BY 的条款。

  • 必须使用全文索引扫描而不是表扫描来执行搜索。

  • 如果查询连接表,全文索引扫描必须是连接中最左边的非常量表。

ORDER BY考虑到刚刚列出的条件,在需要或需要时指定使用显式排序顺序 通常比较容易。

相关值是非负浮点数。零相关性意味着没有相似性。相关性是根据行(文档)中的单词数、行中唯一单词的数量、集合中的单词总数以及包含特定单词的行数来计算的。

笔记

术语文档可以与术语互换使用,这两个术语均指行的索引部分。术语集合 是指索引列并包含所有行。

要简单地计算匹配项,您可以使用这样的查询:

mysql> SELECT COUNT(*) FROM articles
    -> WHERE MATCH (title,body)
    -> AGAINST ('database' IN NATURAL LANGUAGE MODE);
+----------+
| COUNT(*) |
+----------+
|        2 |
+----------+
1 row in set (0.00 sec)

您可能会发现按如下方式重写查询会更快:

mysql> SELECT
    -> COUNT(IF(MATCH (title,body) AGAINST ('database' IN NATURAL LANGUAGE MODE), 1, NULL))
    -> AS count
    -> FROM articles;
+-------+
| count |
+-------+
|     2 |
+-------+
1 row in set (0.03 sec)

第一个查询做了一些额外的工作(按相关性对结果进行排序),但也可以使用基于 WHERE子句的索引查找。如果搜索匹配的行很少,索引查找可能会使第一个查询更快。第二个查询执行全表扫描,如果搜索词出现在大多数行中,这可能比索引查找更快。

对于自然语言全文搜索,MATCH()函数中命名的列必须与表中某些FULLTEXT索引中包含的列相同。对于前面的查询, MATCH()函数 (title和) 中命名的列与表 索引body定义中命名的列相同 。要单独搜索或 ,您将为每一列创建单独 的索引。 articleFULLTEXTtitlebodyFULLTEXT

您还可以执行布尔搜索或带有查询扩展的搜索。这些搜索类型在 第 12.10.2 节“布尔全文搜索”第 12.10.3 节“使用查询扩展的全文搜索”中进行了描述。

使用索引的全文搜索只能在MATCH() 子句中命名来自单个表的列,因为索引不能跨越多个表。对于 MyISAM表,可以在没有索引的情况下进行布尔搜索(尽管速度更慢),在这种情况下,可以从多个表中命名列。

前面的示例是一个基本说明,展示了如何使用按MATCH()相关性递减顺序返回行的函数。下一个示例显示如何显式检索相关值。返回的行未排序,因为该 SELECT语句既不包含 WHEREnorORDER BY 子句:

mysql> SELECT id, MATCH (title,body)
    -> AGAINST ('Tutorial' IN NATURAL LANGUAGE MODE) AS score
    -> FROM articles;
+----+---------------------+
| id | score               |
+----+---------------------+
|  1 | 0.22764469683170319 |
|  2 |                   0 |
|  3 | 0.22764469683170319 |
|  4 |                   0 |
|  5 |                   0 |
|  6 |                   0 |
+----+---------------------+
6 rows in set (0.00 sec)

下面的例子比较复杂。查询返回相关值,它还按相关性递减的顺序对行进行排序。要实现此结果,请指定 MATCH()两次:一次在 SELECT列表中,一次在 WHERE子句中。这不会导致额外的开销,因为 MySQL 优化器注意到这两个 MATCH()调用是相同的并且只调用一次全文搜索代码。

mysql> SELECT id, body, MATCH (title,body)
    ->   AGAINST ('Security implications of running MySQL as root'
    ->   IN NATURAL LANGUAGE MODE) AS score
    -> FROM articles
    ->   WHERE MATCH (title,body) 
    ->   AGAINST('Security implications of running MySQL as root'
    ->   IN NATURAL LANGUAGE MODE);
+----+-------------------------------------+-----------------+
| id | body                                | score           |
+----+-------------------------------------+-----------------+
|  4 | 1. Never run mysqld as root. 2. ... | 1.5219271183014 |
|  6 | When configured properly, MySQL ... | 1.3114095926285 |
+----+-------------------------------------+-----------------+
2 rows in set (0.00 sec)

包含在双引号 ( ") 字符中的短语仅匹配包含字面意思的短语的行,因为它是键入的。全文引擎将短语拆分为单词并在FULLTEXT索引中搜索单词。非单词字符不需要完全匹配:短语搜索只需要匹配包含与短语完全相同的单词并且顺序相同。例如,"test phrase"匹配"test, phrase". 如果短语不包含索引中的单词,则结果为空。例如,如果所有词都是停用词或短于索引词的最小长度,则结果为空。

MySQLFULLTEXT实现将任何真正的单词字符(字母、数字和下划线)序列视为一个单词。该序列还可以包含撇号 ( '),但连续不超过一个。意思是thataaa'bbb被当成一个词,却被aaa''bbb当成两个词。单词开头或结尾的撇号被FULLTEXT解析器去除; 'aaa'bbb'将被解析为 aaa'bbb.

FULLTEXT解析器通过查找特定的分隔符来确定单词的开始和结束位置 ;例如, (空格)、, (逗号)和.(句点)。如果单词没有用定界符分隔(例如中文),则 FULLTEXT解析器无法确定单词的开始或结束位置。为了能够将这些语言中的单词或其他索引术语添加到FULLTEXT索引中,您必须对它们进行预处理,以便它们由一些任意定界符分隔。

可以编写一个插件来替换内置的全文解析器(仅支持 MyISAM)。有关详细信息,请参阅 MySQL 插件 API。例如解析器插件源代码,参见plugin/fulltext一个MySQL源码分发目录。

全文搜索中会忽略某些词:

  • 任何太短的词都会被忽略。全文搜索找到的单词的默认最小长度对于InnoDB搜索索引是三个字符,对于MyISAM. 您可以通过在创建索引之前设置配置选项来控制截止: 搜索索引的 innodb_ft_min_token_size 配置选项InnoDB,或ft_min_word_len .MyISAM

  • 停用词列表中的词将被忽略。停用词是诸如thesome之类的词,它们非常常见以至于被认为具有零语义值。有一个内置的停用词列表,但它可以被用户定义的列表覆盖。InnoDB搜索索引和搜索索引 的停用词列表和相关配置选项不同 MyISAM。停用词处理由配置选项 innodb_ft_enable_stopwordinnodb_ft_server_stopword_tableinnodb_ft_user_stopword_table for InnoDBsearch indexes 以及 ft_stopword_filefor MyISAMones 控制。

请参阅第 12.10.4 节,“全文停用词”以查看默认停用词列表以及如何更改它们。默认的最小字长可以按照 第 12.10.6 节,“微调 MySQL 全文搜索”中的描述进行更改。

集合和查询中的每个正确单词都根据其在集合或查询中的重要性进行加权。因此,出现在许多文档中的词具有较低的权重,因为它在该特定集合中具有较低的语义值。相反,如果这个词很罕见,它就会获得更高的权重。组合单词的权重以计算行的相关性。此技术最适用于大型集合。

MyISAM 限制

对于非常小的表,词分布并不能充分反映它们的语义值,这种模型有时可能会在 MyISAM表上的搜索索引中产生奇怪的结果。例如,尽管单词MySQLarticles出现在前面显示的表的每一行中, 但在MyISAM搜索索引中搜索该单词不会产生任何结果:

mysql> SELECT * FROM articles
    -> WHERE MATCH (title,body)
    -> AGAINST ('MySQL' IN NATURAL LANGUAGE MODE);
Empty set (0.00 sec)

搜索结果为空,因为单词 MySQL出现在至少 50% 的行中,因此被有效地视为停用词。这种过滤技术更适合大型数据集,您可能不希望结果集从 1GB 的表中每隔一行返回一次,而不适合小型数据集,因为它可能会导致流行术语的结果不佳。

当您第一次尝试全文搜索以了解其工作原理时,50% 的阈值会让您大吃一惊,并使 InnoDB表格更适合全文搜索实验。如果您创建一个 MyISAM表并仅向其中插入一两行文本,则文本中的每个单词至少出现在 50% 的行中。因此,在表包含更多行之前,搜索不会返回任何结果。需要绕过 50% 限制的用户可以在 InnoDB表上构建搜索索引,或使用第 12.10.2 节“布尔全文搜索”中解释的布尔搜索模式。