04 全文本搜索

4.1 概述

并非所有引擎都支持全文本搜索,MyISAM支持,InnoDB不支持。

在创建表时,可以使用FULLTEXT来指明那些列支持全文本搜索。mysql会对指定的列进行索引。之后mysql自动维护改索引,在增加、更新或删除时,索引也会随之更新。

CREATE TABLE `productnotes` (
`note_id` int(11) NOT NULL AUTO_INCREMENT,
`prod_id` char(10) NOT NULL,
`note_date` datetime NOT NULL,
`note_text` text,
PRIMARY KEY (`note_id`),
FULLTEXT (`note_text`)
) ENGINE=MyISAM AUTO_INCREMENT=115 DEFAULT CHARSET=latin1

注意不要在导入数据时启动FULLTEXT索引。应当先导入数据,在修改表的定义来启用。这样有助于更快的导入数据。

4.2 进行全文本搜索

Match指定被搜索的列,Against指定要使用的搜索表达式。注意搜索不区分大小写。

mysql> SELECT note_text
-> FROM productnotes
-> WHERE Match(note_text) Against('rabbit');
+----------------------------------------------------------------------------------------------------------------------+
| note_text |
+----------------------------------------------------------------------------------------------------------------------+
| Customer complaint: rabbit has been able to detect trap, food apparently less effective now. |
| Quantity varies, sold by the sack load.
All guaranteed to be bright and orange, and suitable for use as rabbit bait. |
+----------------------------------------------------------------------------------------------------------------------+

4.3 搜索结果的排序

搜索结果根据一个等级值来排序,值越大越排前面。Match和Against直接的结果就是这个值

SELECT note_text, Match(note_text) Against('rabbit') AS rank
FROM productnotes;

注意这里是在SELECT中使用,而不是WHERE中。可以看到不包含rabbit的列值是0。

如果搜索项(Against中的内容)有多个,则包含多数匹配词的行比包含少数匹配词的行的等级值高。

4.4 使用查询扩展

基本用法如下

SELECT note_text
FROM productsnotes
WHERE Match(note_text) Against('heavy' WITH QUERY EXPANSION);

在使用查询扩展时,MySQL对数据和索引进行两遍扫描来完成搜索。

  1. 首先,进行一个基本的全文本搜索,找出与搜索条件匹配的所有行

  2. MySQL检查这些匹配行,并选择所有有用的词

  3. MySQL在用原来的词加上有用的词,一起再次进行一次全文本搜索。

利用查询扩展,能找出可能相关的结果。

4.5 布尔文本搜索

对于布尔文本搜索,没有定义FULLTEXT的列也可以使用,但是速度非常慢。

下面是一个基本的用例,同普通的全文本搜索是一样的,因为我们没有指定全文本布尔操作符。

SELECT note_text
FROM productsnotes
WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE);

一些全文本布尔操作符的说明

布尔操作符

说明

+

包含,必须存在

-

排除,必须不存在

>

包含,且增加等级值

\<

包含,且减少等级值

~

取消一个词的排序值

*

词尾的通配符

下面用几个例子来更具体的说明这些操作符的用法

匹配heavy,但是排除任何包含以repo开头的词的行

SELECT note_text
FROM productsnotes
WHERE Match(note_text) Against('heavy -repo*' IN BOOLEAN MODE);

匹配包含rabbit和bait的行

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+rabbit +bait' IN BOOLEAN MODE);

下面没有指定操作符,匹配包含rabbit或bait的行

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+rabbit +bait' IN BOOLEAN MODE);

匹配rabbit bait

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('"rabbit bait"' IN BOOLEAN MODE);

匹配rabbit和carrot,前者增加等级,后者降低等级

SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('>rabbit <carrot' IN BOOLEAN MODE);

4.6 使用全文本搜索一些需要注意的地方

  • 短词被忽略,且从索引中排除。(短词默认是指3个或3个字符以下的词,也可以自己定义)

  • MySQL有一个非用词(stopword)的列表,这些词在搜索时被忽略。

  • 如果一个词的出现频率高于50%,则自动被作为非用词。(因为搜索出现频率很高的词没用什么用)

  • 忽略词中的单引号。如don't索引为dont

  • 不具有词分隔符的语言(如汉语和日语)不能有效的返回结果。