• 首页
  • vue
  • TypeScript
  • JavaScript
  • scss
  • css3
  • html5
  • php
  • MySQL
  • redis
  • jQuery
  • 自然语言全文搜索

    默认情况下或使用IN NATURAL LANGUAGE MODE修饰符,该MATCH()函数将根据文本集合执行自然语言搜索字符串。集合是FULLTEXT索引中包含的一组一个或多个列。搜索字符串作为的参数给出AGAINST()。对于表中的每一行,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 will 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)
    

    默认情况下,搜索以不区分大小写的方式执行。要执行区分大小写的全文本搜索,请对索引列使用区分大小写或二进制校验规则。例如,utf8mb4可以将使用字符集的列分配为的校验规则,utf8mb4_0900_as_csutf8mb4_bin使其对全文搜索区分大小写。

    MATCH()一个在使用WHERE条款,如在前面的例子中,返回的行自动地具有最高相关第一排序。相关性值是非负浮点数。零相关性意味着没有相似性。相关性是根据行(文档)中单词的数量,行中唯一单词的数量,集合中单词的总数以及包含特定单词的行数量来计算的。

    注意

    术语“文档”可以与术语“行”互换使用,并且两个术语均指行的索引部分。术语“集合”是指索引的列并涵盖所有行。

    要简单地计算匹配数,您可以使用如下查询:

    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()函数(titlebody)中命名的列与article表的FULLTEXT索引定义中命名的列相同。要分别搜索titlebody,您将为FULLTEXT每列创建单独的索引。

    您还可以执行布尔搜索或带有查询扩展的搜索。“布尔型全文本搜索”和“具有查询扩展的全文本搜索”中介绍了这些搜索类型。

    使用索引的全文搜索只能在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)
    

    包含在双引号(")字符中的短语仅与按字面意义包含该短语的行匹配。全文引擎将短语分解为单词,并在FULLTEXT索引中搜索单词。非单词字符不必完全匹配:短语搜索仅要求匹配项包含与短语完全相同的单词,并且顺序相同。例如,"test phrase"matchs "test, phrase"。如果该短语不包含索引中的单词,则结果为空。例如,如果所有单词均为停用词或比索引单词的最小长度短,则结果为空。

    MySQL FULLTEXT实现将任何真单词字符(字母,数字和下划线)序列视为一个单词。该序列也可以包含撇号('),但不能超过一行。这意味着aaa'bbb被视为一个单词,但aaa''bbb被视为两个单词。FULLTEXT解析器将单词开头或结尾的撇号删除;'aaa'bbb'将被解析为aaa'bbb

    内置的FULLTEXT解析器通过查找某些定界符来确定单词的开头和结尾。例如(空格),,(逗号)和.(句点)。如果单词之间没有用定界符分隔(例如,中文),则内置FULLTEXT解析器无法确定单词的开始或结束位置。为了能够将这些语言中的单词或其他索引术语添加到FULLTEXT使用内置FULLTEXT解析器的索引中,您必须对其进行预处理,以使它们之间可以由任意定界符分隔。或者,您可以创建FULLTEXT使用ngram解析器插件(适用于中文,日文或韩文)或MeCab解析器插件(适用于日文)建立索引。

    可以编写一个替换内置全文分析器的插件。有关详细信息,请参见“ MySQL插件API”。有关解析器插件源代码的示例,请参见plugin/fulltextMySQL源发行版的目录。

    在全文搜索中,某些单词将被忽略:

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

      注意

      此行为不适用于FULLTEXT使用ngram解析器的索引。对于ngram解析器,令牌长度由ngram_token_size选项定义。

    • 停用词列表中的单词将被忽略。停用词是一个非常普遍的词,例如“ the ”或“ some ”,以至于它被认为具有零语义值。有一个内置的停用词列表,但是可以被用户定义的列表覆盖。InnoDB搜索索引和搜索索引的停用词列表和相关配置选项不同MyISAM。停用词处理由配置选项来控制innodb_ft_enable_stopwordinnodb_ft_server_stopword_tableinnodb_ft_user_stopword_table用于InnoDB搜索的索引,并且ft_stopword_file对于MyISAM那些。

    请参见“全文停用词”以参见默认停用词列表以及如何更改它们。可以如“微调MySQL全文搜索”中所述更改默认的最小字长。

    集合和查询中的每个正确单词都将根据其在集合或查询中的重要性进行加权。因此,存在于许多文档中的单词具有较低的权重,因为它在此特定集合中具有较低的语义值。相反,如果单词很少见,则其权重较高。单词的权重被组合以计算行的相关性。这项技术最适合大型收藏。

    MyISAM局限性

    对于很小的表,单词分布不能充分反映其语义值,并且此模型有时可能会为MyISAM表上的搜索索引产生奇怪的结果。例如,尽管在前面显示的表的每一行中都有单词“ MySQL ”,但是articlesMyISAM搜索索引中搜索该单词不会产生结果:

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

    搜索结果为空,因为至少有50%的行中包含单词“ MySQL ”,因此有效地将其视为停用词。这种过滤技术更适用于大型数据集,在小型数据集中,您可能不希望结果集从1GB表返回第二行,而在小型数据集中,对于流行术语而言,它可能导致较差的结果。

    首次尝试全文搜索以了解其工作原理时,50%的阈值可能会让您感到惊讶,并使InnoDB表格更适合进行全文搜索的实验。如果创建MyISAM表并仅在其中插入一两行文本,则文本中的每个单词至少出现在50%的行中。结果,在表包含更多行之前,没有搜索返回任何结果。需要绕过50%限制的用户可以在InnoDB表上建立搜索索引,或使用“布尔全文本搜索”中说明的布尔搜索模式。