暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

MySQL的函数和运算符 - 全文搜索 - 自然语言全文搜索

数据库杂货铺 2021-06-29
394
自然语言全文搜索
 
默认情况下或使用 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 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_cs utf8mb4_bin,使其在全文搜索时区分大小写。
     
    如前面例子所示,当在 WHERE 子句中使用 MATCH() 时,返回的行将自动按相关度排序,最高的在最前面。相关性值是非负浮点数。零相关性意味着没有相似性。相关性是根据行(文档)中的单词数、行中唯一的单词数、集合中的单词总数以及包含特定单词的行数计算的。
     
    注意
     
    术语“文档”(document)可以和术语“行”(row)互换使用,这两个术语都指行的索引部分。术语“集合”(collection)指的是索引列,包含所有行。
     
    仅计算匹配数,你可以使用这样的查询:
     
      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)article 表的 FULLTEXT 索引定义中指定的列相同。要单独搜索 title body,需要为每个列创建单独的 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" 匹配 "test, phrase"。如果短语不包含索引中的单词,则结果为空。例如,如果所有单词都是终止词或比索引单词的最小长度更短,则结果为空。
             
            MySQL FULLTEXT 实现将任何真实的单词字符序列(字母、数字和下划线)视为一个单词。该序列也可以包含撇号('),但在一行中不能超过一个。这意味着 aaa'bbb 被视为一个单词,而 aaa''bbb 被视为两个单词。FULLTEXT 解析器会去除单词开头或结尾的撇号;'aaa'bbb' 将被解析为 aaa'bbb
             
            内置的 FULLTEXT 解析器通过查找特定的分隔符来确定单词的开始和结束位置;例如: (空格), (逗号) .(句号)。如果单词之间没有分隔符(例如,中文),内置的 FULLTEXT 解析器就不能确定单词从哪里开始或结束。为了能够将这些语言中的单词或其他索引项添加到使用内置 FULLTEXT 解析器的 FULLTEXT 索引中,必须对它们进行预处理,以便用某些分隔符分隔它们。或者,可以使用 ngram 解析器插件(用于中文、日文或韩文)MeCab 解析器插件(用于日文)创建 FULLTEXT 索引。
             
            可以编写一个插件来替换内置的全文解析器。有关解析器插件的源代码示例,请参见 MySQL 源代码分发的 plugin/fulltext 目录。
             
            有些词在全文搜索中会被忽略:
             
            ● 任何太短的词都会被忽略。对于 InnoDB 搜索索引,全文搜索查找的单词的默认最小长度是3个字符,对于 MyISAM 则是4个字符。可以通过在创建索引之前设置配置选项来控制这种限制:InnoDB 搜索索引的 innodb_ft_min_token_size 配置选项,MyISAM ft_min_word_len 配置选项。
             
            注意
             
            这种行为不适用于使用 ngram 解析器的 FULLTEXT 索引。对于 ngram 解析器,令牌长度由 ngram_token_size 选项定义。
             
            ● 终止词列表中的单词将被忽略。终止词是像 the” 或 “some” 这样的词,它们非常常见,以至于被认为没有语义价值。MySQL 有内置的终止词列表,但它可以被用户定义的列表覆盖。InnoDB MyISAM 搜索索引的终止词列表和相关配置选项是不同的。对于 InnoDB 搜索索引,终止词处理由配置选项 innodb_ft_enable_stopwordinnodb_ft_server_stopword_table innodb_ft_user_stopword_table 控制,对于 MyISAM 搜索索引,则由配置选项 ft_stopword_file 控制。
             
            集合和查询中的每个正确单词都根据其在集合或查询中的重要性进行加权。因此,出现在许多文档中的单词具有较低的权重,因为它在这个特定集合中的语义值较低。相反,如果单词比较稀少,它将获得更高的权重。单词的权重被合并来计算行相关性。这种技术最适合大型集合。
             
            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 表上建立搜索索引,或者使用布尔搜索模式。
               
               
               
               
               
              官方文档:
              https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
              文章转载自数据库杂货铺,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

              评论