### 12.10.2 布尔全文搜索

[](<>)[](<>)

MySQL 可以使用`在布尔模式下`修饰符。使用此修饰符，某些字符在搜索字符串中单词的开头或结尾具有特殊含义。在以下查询中，`+`和`-`运算符分别指示一个词必须存在或不存在，才能发生匹配。因此，查询检索所有包含单词“MySQL”的行，但*不是*包含单词“YourSQL”：

```
mysql> SELECT * FROM articles WHERE MATCH (title,body)
    AGAINST ('+MySQL -YourSQL' IN BOOLEAN MODE);
+----+-----------------------+-------------------------------------+
| id | title                 | body                                |
+----+-----------------------+-------------------------------------+
|  1 | MySQL Tutorial        | DBMS stands for DataBase ...        |
|  2 | How To Use MySQL Well | After you went through a ...        |
|  3 | Optimizing MySQL      | In this tutorial, we show ...       |
|  4 | 1001 MySQL Tricks     | 1. Never run mysqld as root. 2. ... |
|  6 | MySQL Security        | When configured properly, MySQL ... |
+----+-----------------------+-------------------------------------+
```

笔记

在实现这个特性时，MySQL 使用了有时被称为隐含布尔逻辑的东西，其中

-   `+`代表`和`

-   `-`代表`不是`

-   [*无操作员*]暗示`要么`

    布尔全文搜索具有以下特征：

-   它们不会自动按照相关性递减的顺序对行进行排序。

-   `InnoDB`表需要一个`全文`的所有列的索引[`匹配（）`](fulltext-search.html#function_match)执行布尔查询的表达式。针对 a 的布尔查询`MyISAM`搜索索引即使没有`全文`索引，尽管以这种方式执行的搜索会很慢。

-   全文最小和最大字长参数适用于`全文`使用内置创建的索引`全文`解析器和 MeCab 解析器插件。[`innodb_ft_min_token_size`](innodb-parameters.html#sysvar_innodb_ft_min_token_size)和[`innodb_ft_max_token_size`](innodb-parameters.html#sysvar_innodb_ft_max_token_size)用于`InnoDB`搜索索引。[`ft_min_word_len`](server-system-variables.html#sysvar_ft_min_word_len)和[`ft_max_word_len`](server-system-variables.html#sysvar_ft_max_word_len)用于`MyISAM`搜索索引。

    最小和最大字长全文参数不适用于`全文`使用 ngram 解析器创建的索引。ngram 标记大小由[`ngram_token_size`](server-system-variables.html#sysvar_ngram_token_size)选项。

-   停用词列表适用，由[`innodb_ft_enable_stopword`](innodb-parameters.html#sysvar_innodb_ft_enable_stopword),[`innodb_ft_server_stopword_table`](innodb-parameters.html#sysvar_innodb_ft_server_stopword_table)， 和[`innodb_ft_user_stopword_table`](innodb-parameters.html#sysvar_innodb_ft_user_stopword_table)为了`InnoDB`搜索索引，以及[`ft_stopword_file`](server-system-variables.html#sysvar_ft_stopword_file)为了`MyISAM`那些。

-   `InnoDB`全文搜索不支持对单个搜索词使用多个运算符，如下例所示：`'++苹果'`.在单个搜索词上使用多个运算符会将语法错误返回到标准输出。MyISAM 全文搜索成功地处理了相同的搜索，忽略除了紧邻搜索词的运算符之外的所有运算符。

-   `InnoDB`全文搜索仅支持前导加号或减号。例如，`InnoDB`支持`'+苹果'`但不支持`'苹果+'`.指定尾随加号或减号原因`InnoDB`报告语法错误。

-   `InnoDB`全文搜索不支持使用带通配符的前导加号 (`'+*'`)，加号和减号组合 (`'+-'`)，或前导加号和减号组合 (`'+-苹果'`）。这些无效查询返回语法错误。

-   `InnoDB`全文搜索不支持使用`@`布尔全文搜索中的符号。这`@`符号保留供使用`@距离`邻近搜索运算符。

-   他们不使用适用于`MyISAM`搜索索引。

    布尔全文搜索功能支持以下运算符：

-   `+`

    前导或尾随加号表示该词*必须*出现在返回的每一行中。`InnoDB`仅支持前导加号。

-   `-`

    前导或尾随减号表示该词必须*不是*出现在返回的任何行中。`InnoDB`仅支持前导减号。

    注意：`-`运算符仅用于排除与其他搜索词匹配的行。因此，布尔模式搜索只包含前面的词`-`返回一个空结果。它不会返回“除包含任何排除项的行之外的所有行”。

-   （无操作员）

    默认情况下（当两者都没有`+`也不`-`已指定），该词是可选的，但包含它的行的评级更高。这模仿了[`匹配（）对（）`](fulltext-search.html#function_match)没有`在布尔模式下`修饰符。

-   `@*`距离`*`

    该运算符适用于`InnoDB`仅限表。它测试两个或多个单词是否都在彼此指定的距离内开始，以单词为单位。在双引号字符串中指定搜索词`@*`距离`*`运算符，例如，`MATCH(col1) AGAINST('"word1 word2 word3" @8' 在布尔模式下)`

-   `> <`

    这两个运算符用于更改单词对分配给行的相关值的贡献。这`>`运营商增加贡献和`<`运算符减少它。请参阅此列表后面的示例。

-   `( )`

    括号将单词组合成子表达式。带括号的组可以嵌套。

-   `~`

    前导波浪号充当否定运算符，导致单词对行相关性的贡献为负。这对于标记“噪音”词很有用。包含这样一个词的行的评分低于其他行，但并不完全排除在外，因为它会与`-`操作员。

-   `*`

    星号用作截断（或通配符）运算符。与其他运算符不同，它是*附加的*到受影响的词。如果单词以前面的单词开头，则单词匹配`*`操作员。

    如果使用截断运算符指定单词，则不会从布尔查询中删除它，即使它太短或停用词也是如此。一个词是否太短由[`innodb_ft_min_token_size`](innodb-parameters.html#sysvar_innodb_ft_min_token_size)设置为`InnoDB`表，或[`ft_min_word_len`](server-system-variables.html#sysvar_ft_min_word_len)为了`MyISAM`表。这些选项不适用于`全文`使用 ngram 解析器的索引。

    通配符被认为是一个前缀，必须出现在一个或多个单词的开头。如果最小字长为 4，则搜索`'+*`单词`* +*'`可以返回比搜索更少的行`'+*`单词`* +'`，因为第二个查询忽略了太短的搜索词`这`.

-   `"`

    用双引号 (`"`) 字符仅匹配包含该短语的行*从字面上看，因为它是键入的*.全文引擎将短语拆分为单词并在`全文`词的索引。非单词字符不需要完全匹配：短语搜索只需要匹配包含与短语完全相同的单词并且顺序相同。例如，`“测试用语”`火柴`“测试，短语”`.

    如果短语不包含索引中的单词，则结果为空。由于多种因素的组合，这些词可能不在索引中：如果它们不存在于文本中，是停用词，或者比索引词的最小长度短。

    以下示例演示了一些使用布尔全文运算符的搜索字符串：

-   `'苹果香蕉'`

    查找至少包含两个单词之一的行。

-   `'+苹果+果汁'`

    查找包含这两个单词的行。

-   `'+苹果麦金塔'`

    查找包含单词“apple”的行，但如果它们还包含“macintosh”，则将行排名更高。

-   `'+苹果-Macintosh'`

    查找包含单词“apple”但不包含“macintosh”的行。

-   `'+苹果~Macintosh'`

    查找包含单词“apple”的行，但如果该行还包含单词“macintosh”，则将其评分低于如果该行不包含单词。这比搜索“更柔和”`'+苹果-Macintosh'`，“macintosh”的存在导致该行根本不返回。

-   `'+苹果+（>营业额<馅饼）'`

    查找包含单词“apple”和“turnover”或“apple”和“strudel”（按任意顺序）的行，但“apple rolling”的排名高于“apple strudel”。

-   `'苹果*'`

    查找包含“apple”、“apples”、“applesauce”或“applet”等单词的行。

-   `'“一些单词”'`

    查找包含确切短语“一些词”的行（例如，包含“一些智慧词”但不包含“一些干扰词”的行）。请注意，`"`包含该短语的字符是分隔该短语的运算符字符。它们不是包含搜索字符串本身的引号。

#### InnoDB 布尔模式搜索的相关性排名

[`InnoDB`](innodb-storage-engine.html)全文搜索以[狮身人面像](http://sphinxsearch.com/)全文搜索引擎，使用的算法基于[BM25](http://en.wikipedia.org/wiki/Okapi_BM25)和[特遣部队](http://en.wikipedia.org/wiki/TF-IDF)排名算法。由于这些原因，相关性排名`InnoDB`布尔全文搜索可能不同于[`MyISAM`](myisam-storage-engine.html)相关性排名。

`InnoDB`使用“词频-逆文档频率”的变体（`特遣部队`) 加权系统对给定全文搜索查询的文档相关性进行排名。这`特遣部队`权重基于一个词在文档中出现的频率，由该词在集合中所有文档中出现的频率来抵消。换句话说，一个词在文档中出现的频率越高，而该词在文档集合中出现的频率越低，文档的排名就越高。

##### 如何计算相关性排名

术语频率（`特遣部队`) 值是单词在文档中出现的次数。逆文档频率 (`以色列国防军`) 单词的值使用以下公式计算，其中`总记录`是集合中的记录数，并且`匹配记录`是搜索词出现的记录数。

```
${IDF} = log10( ${total_records} / ${matching_records} )
```

当一个文档多次包含一个词时，IDF 值乘以 TF 值：

```
${TF} * ${IDF}
```

使用`特遣部队`和`以色列国防军`值，文档的相关性排名使用以下公式计算：

```
${rank} = ${TF} * ${IDF} * ${IDF}
```

该公式在以下示例中进行了演示。

##### 单个词搜索的相关性排名

此示例演示了单词搜索的相关性排名计算。

```
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 (1.04 sec)

mysql> INSERT INTO articles (title,body) VALUES
('MySQL Tutorial','This database tutorial ...'),
("How To Use MySQL",'After you went through a ...'),
('Optimizing Your Database','In this database tutorial ...'),
('MySQL vs. YourSQL','When comparing databases ...'),
('MySQL Security','When configured properly, MySQL ...'),
('Database, Database, Database','database database database'),
('1001 MySQL Tricks','1. Never run mysqld as root. 2. ...'),
('MySQL Full-Text Indexes', 'MySQL fulltext indexes use a ..');
Query OK, 8 rows affected (0.06 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> SELECT id, title, body, MATCH (title,body)  AGAINST ('database' IN BOOLEAN MODE)
AS score FROM articles ORDER BY score DESC;
+----+------------------------------+-------------------------------------+---------------------+
| id | title                        | body                                | score               |
+----+------------------------------+-------------------------------------+---------------------+
|  6 | Database, Database, Database | database database database          |  1.0886961221694946 |
|  3 | Optimizing Your Database     | In this database tutorial ...       | 0.36289870738983154 |
|  1 | MySQL Tutorial               | This database tutorial ...          | 0.18144935369491577 |
|  2 | How To Use MySQL             | After you went through a ...        |                   0 |
|  4 | MySQL vs. YourSQL            | When comparing databases ...        |                   0 |
|  5 | MySQL Security               | When configured properly, MySQL ... |                   0 |
|  7 | 1001 MySQL Tricks            | 1. Never run mysqld as root. 2. ... |                   0 |
|  8 | MySQL Full-Text Indexes      | MySQL fulltext indexes use a ..     |                   0 |
+----+------------------------------+-------------------------------------+---------------------+
8 rows in set (0.00 sec)
```

总共有 8 条记录，其中 3 条与“数据库”搜索词匹配。第一条记录（`编号 6`) 包含搜索词 6 次，相关度排名为`1.0886961221694946`.此排名值是使用`特遣部队`值为 6（“数据库”搜索词在记录中出现 6 次`编号 6`） 和`以色列国防军`值为0.42596873216370745，计算如下（其中8是总记录数，3是搜索词出现的记录数）：

```
${IDF} = log10( 8 / 3 ) = 0.42596873216370745
```

这`特遣部队`和`以色列国防军`然后将值输入到排名公式中：

```
${rank} = ${TF} * ${IDF} * ${IDF}
```

在 MySQL 命令行客户端中执行计算返回排名值 1.088696164686938。

```
mysql> SELECT 6*log10(8/3)*log10(8/3);
+-------------------------+
| 6*log10(8/3)*log10(8/3) |
+-------------------------+
|       1.088696164686938 |
+-------------------------+
1 row in set (0.00 sec)
```

笔记

您可能会注意到返回的排名值略有不同`选择...匹配...反对`语句和 MySQL 命令行客户端 (`1.0886961221694946`相对`1.088696164686938`）。不同之处在于整数和浮点数/双精度数之间的转换是如何在内部执行的`InnoDB`（以及相关的精度和舍入决策），以及它们如何在其他地方执行，例如在 MySQL 命令行客户端或其他类型的计算器中。

##### 多词搜索的相关性排名

本例演示基于`文章`前面示例中使用的表和数据。

如果搜索多个词，则相关度排名值是每个词的相关度排名值之和，如下公式所示：

```
${rank} = ${TF} * ${IDF} * ${IDF} + ${TF} * ${IDF} * ${IDF}
```

对两个词（'mysql tutorial'）执行搜索会返回以下结果：

```
mysql> SELECT id, title, body, MATCH (title,body)  AGAINST ('mysql tutorial' IN BOOLEAN MODE)
    AS score FROM articles ORDER BY score DESC;
+----+------------------------------+-------------------------------------+----------------------+
| id | title                        | body                                | score                |
+----+------------------------------+-------------------------------------+----------------------+
|  1 | MySQL Tutorial               | This database tutorial ...          |   0.7405621409416199 |
|  3 | Optimizing Your Database     | In this database tutorial ...       |   0.3624762296676636 |
|  5 | MySQL Security               | When configured properly, MySQL ... | 0.031219376251101494 |
|  8 | MySQL Full-Text Indexes      | MySQL fulltext indexes use a ..     | 0.031219376251101494 |
|  2 | How To Use MySQL             | After you went through a ...        | 0.015609688125550747 |
|  4 | MySQL vs. YourSQL            | When comparing databases ...        | 0.015609688125550747 |
|  7 | 1001 MySQL Tricks            | 1. Never run mysqld as root. 2. ... | 0.015609688125550747 |
|  6 | Database, Database, Database | database database database          |                    0 |
+----+------------------------------+-------------------------------------+----------------------+
8 rows in set (0.00 sec)
```

在第一条记录中（`编号 8`)，“mysql”出现一次，“tutorial”出现两次。'mysql' 有六个匹配记录，'tutorial' 有两个匹配记录。当将这些值插入到多词搜索的排名公式中时，MySQL 命令行客户端会返回预期的排名值：

```
mysql> SELECT (1*log10(8/6)*log10(8/6)) + (2*log10(8/2)*log10(8/2));
+-------------------------------------------------------+
| (1*log10(8/6)*log10(8/6)) + (2*log10(8/2)*log10(8/2)) |
+-------------------------------------------------------+
|                                    0.7405621541938003 |
+-------------------------------------------------------+
1 row in set (0.00 sec)
```

笔记

返回的排名值略有不同`选择...匹配...反对`语句和 MySQL 命令行客户端在前面的示例中进行了说明。
