1.使用方法
默认情况下,或者使用IN NATURAL LANGUAGE MODE修饰符,MATCH()函数对文本集合的字符串执行自然语言搜索。
1.1 集合
集合是FULLTEXT索引中包含的一个或多个列的集合。
1.2 搜索字符串
搜索字符串作为AGINST()的参数提供。
1.3 相关性值
对于表中的每一行,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: 0mysql> 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)
1.4 大小写
默认情况下,搜索是以不区分大小写的方式执行的。若要执行区分大小写的全文搜索,请对索引列使用区分大小写或二进制排序规则。
例如,可以为使用的utf8mb4字符集的列分配utf8mb4_0900_as_cs或utf8mb4 _bin的排序规则,使其对全文搜索区分大小写。
1.5 MATCH()
当在WHERE子句中使用MATCH()时,如前所示,只要满足以下条件,返回的行将首先自动以最高相关性进行排序:
-
不能有显式ORDER BY子句。
-
必须使用全文索引扫描而不是表扫描来执行搜索。
-
如果查询联接表,则全文索引扫描必须是联接中最左边的非常量表。
考虑到刚刚列出的条件的必要性,通常不需要使用ORDER BY来指定显式排序顺序。
1.6 相关性值计算
相关值是非负浮点数。零相关性意味着没有相似性。相关性是根据行(文档)中的单词数、行中唯一单词数、集合中的单词总数以及包含特定单词的行数来计算的。
提示:
“文档”项可以与“行”项互换使用,并且这两个项都指的是行的索引部分。“集合”项指的是索引列,包括所有行。
要简单地计算匹配项,可以使用以下查询:
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()函数中命名的列(标题和正文)与文章表的FULLTEXT索引定义中命名的那些列相同。要分别搜索标题或正文,需要为每列创建单独的FULLTEXT索引。
您还可以执行布尔搜索或带有查询扩展的搜索。这些搜索类型在第14.9.2节“布尔全文搜索”和第14.9.3节“带查询扩展的全文搜索”中进行了描述。
使用索引的全文搜索只能命名MATCH()子句中单个表中的列,因为索引不能跨越多个表。对于MyISAM表,可以在没有索引的情况下进行布尔搜索(尽管速度较慢),在这种情况下,可以命名多个表中的列。
前面的例子是一个基本的说明,显示了如何使用MATCH()函数,其中按相关性递减的顺序返回行。下一个示例显示了如何显式地检索相关性值。返回的行没有排序,因为SELECT语句既不包括WHERE子句也不包括ORDER 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)
1.7 匹配规则
包含在双引号(“)字符中的短语只与键入时包含该短语的行匹配。
全文引擎将该短语拆分为多个单词,并在FULLTEXT索引中搜索这些单词。
非单词字符不必完全匹配:
短语搜索只要求匹配项中包含与该短语完全相同的单词,且顺序相同。
例如,“测试短语”与“测试,短语”匹配。
如果该短语不包含索引中的单词,则结果为空。
例如,如果所有单词都是停止词或短于索引单词的最小长度,则结果是空的。
MySQL FULLTEXT实现将任何真实单词字符序列(字母、数字和下划线)视为一个单词。
1.该序列也可能包含撇号('),但一行中不能超过一个。
这意味着aaa'bbb被视为一个词,但aaa''bb被视为两个词。
2.单词开头或结尾的撇号会被FULLTEXT语法分析器剥离;'aaa'bb'将被解析为aaa'bb。
内置的FULLTEXT解析器通过查找某些分隔符来确定单词的起始和结束位置;
例如,(空格)、、(逗号)和。时期如果单词没有用分隔符分隔(例如,在中文中),则内置的FULLTEXT解析器无法确定单词的开头或结尾。
为了能够将这些语言中的单词或其他索引项添加到使用内置FULLTEXT语法分析器的FULLTEXT索引中,必须对它们进行预处理,以便用任意分隔符将它们分隔开。或者,您可以使用ngram解析器插件(适用于中文、日语或韩语)或MeCab解析器插件(用于日语)创建FULLTEXT索引。
可以编写一个插件来替换内置的全文解析器。有关详细信息,请参阅MySQL插件API。例如,解析器插件源代码,请参阅MySQL源发行版的plugin/fulltext目录。
在全文搜索中会忽略某些单词:
任何太短的单词都会被忽略。
通过全文搜索找到的默认最小单词长度:
InnoDB中:为三个字符,
MyISAM中:则为四个字符。
您可以通过在创建索引之前设置一个配置选项来控制截止:
Innodb:innodb_ft_min_token_size
MyISAM:ft_min_word_len
注意:
此行为不适用于使用ngram语法分析器的FULLTEXT索引。对于ngram解析器,标记长度由ngram_token_size选项定义。
停止字“stopword”列表中的单词将被忽略。
停止语是一个单词,如“the”或“some”,它非常常见,以至于被认为没有语义价值。
有一个内置的停止语列表,但它可以被用户定义的列表覆盖。
InnoDB搜索索引和MyISAM搜索索引的停止词列表和相关配置选项不同。
停止字处理由配置选项:
innodb_ft_enable_stopword, innodb_ft_server_stopword_table和innodb_ft_user_stopword_table控制(对于innodb搜索索引),
以及ft_Stopword_file控制(对于MyISAM索引)。
请参阅“全文停止字”,查看默认停止语列表以及如何更改它们。
【MySQL精通之路】全文索引-全文停止字-CSDN博客
默认的最小单词长度可以更改,如第14.9.6节“微调MySQL全文搜索”所述。
1.8 加权
集合和查询中的每个正确单词都根据其在集合或查询中的重要性进行加权。
因此,出现在许多文档中的单词的权重较低,因为它在这个特定集合中的语义值较低。
相反,如果这个词很少见,它的权重会更高。
将单词的权重组合起来计算行的相关性。这种技术最适用于大型数据集。
1.9 MyISAM限制
对于非常小的表,单词分布不能充分反映其语义值,并且该模型有时可能会对MyISAM表上的搜索索引产生奇怪的结果。
例如,尽管单词“MySQL”出现在前面显示的articles表的每一行中,但在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表上构建搜索索引,或者使用“布尔全文搜索”中提到的布尔搜索模式。