为什么使用全文本搜索?
LIKE关键字的搜索机制存在几个重要限制。
- 性能:通配符和正则表达式匹配通常要求MySQL尝试匹配表中所有行(而且这些搜索极少使用表索引);
- 明确控制:使用通配符和正则表达式匹配,很难明确地控制匹配什么和不匹配什么。
- 智能化结果:它们都不能提供一种智能化的选择结果的方法。
在使用全文本搜索时,MySQL不需要分别查看每个行,不需要分别分析和处理每个词。MySQL创建指定列中各词的一个索引,搜索可以针对这些词进行。
两个最常使用的引擎为MyISAM和InnoDB,前者支持全文本搜索,而后者不支持、
如何使用全文本搜索?
为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断地重新索引。在对表列进行适当设计后,MySQL会自动进行所有的索引和重新索引。
在索引之后,SELECT可与Match()和Against()一起使用以实际执行搜索。
如何启用?
一般在创建表时启用全文本搜索。
CREATE TABLE productnotes
(
note_id int NOT NULL AUTO_INCREMENT,
prod_id char(10) NOT NULL,
note_date datetime NOT NULL,
note_text text NULL,
PRIMARY KEY(note_id),
FULLTEXT(note_text)
) ENGINE=MyISAM;
这里的FULLTEXT索引单个列,如果需要也可以指定多个列。在定义之后,MySQL自动维护该索引。在增加、更新或删除行时,索引随之自动更新。
不要在导入数据时使用FULLTEXT :应该首先导入所有数据,然后再修改表,定义FULLTEXT。这样可以更快的导入数据,而且使用索引数据的总时间小于在导入每行时分别进行索引所需要的总时间。
基本使用方法
在索引之后,使用两个函数Match()和Against()执行全文本搜索,其中Match()指定被搜索的列,Against()指定要使用的搜索表达式。
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit');
- 传递给Match()的值必须与FULLTEXT()定义中的相同,如果指定多个列,则必须列出它们(而且次序正确)。
- 除非使用BINARY方式,否则全文本搜索不区分大小写。
上述例子若用LIKE完成。
SELECT note_text
FROM productnotes
WHERE note_text LIKE '%rabbit%';
虽然在此例中,返回结果的两行一样,但是LIKE以不特别有用的顺序返回数据,全文本搜索返回以文本匹配的良好程度排序的数据。
全文本搜索的一个重要部分就是对结果排序。具有较高等级的行先返回。
SELECT note_text,
Match(note_text) Against('rabbit') AS rank_s
FROM productnotes;
别名rank_s的列包含全文本搜索计算出的等级值,等级由MySQL根据行中词的数目、唯一词数目、整个索引中词的总数以及包含该词的行的数目计算出来。文本中词靠前的行的等级值比词靠后的行的等级值高。
全文本搜索提供了简单LIKE搜索不能提供的功能,而且,全文本搜索还相当快。
使用查询扩展
如果想找出可能与搜索有关的所有其他行,即使它们不包含词anvils。
- 首先,进行一个基本的全文本搜索,找出与搜索条件匹配的所有行;
- 其次,MySQL检查这些匹配行并选择所有有用的词;
- 最后,MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。
查询拓展功能实在MySQL 4.1.1中引入的,不能用于之前的版本。
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('anvils' WITH QUERY EXPANSION);
表中的行越多,使用查询扩展返回的结果越好。
布尔文本搜索
全文本搜索的另一种形式 布尔文本搜索,可以提供如下内容:
- 要匹配的词;
- 要排斥的词;
- 排列提示;
- 表达式分组;
- 另外一些内容。
布尔文本搜索即时没有定义FULLTEXT索引,也可以使用它,但是会很缓慢。
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy' IN BOOLEAN MODE);
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy -rope*' 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和bait,增加前者的等级,降低后者的等级
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('>rabbit <bait' IN BOOLEAN MODE);
# 匹配safe和bait,降低后者的等级
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('+safe +(<bait)' IN BOOLEAN MODE);
不过,在布尔方式中,不按等级值降序排序返回的行。
使用说明
- 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为具有3个或3个一下字符的词。如果需要,这个数目可以更改。
- MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表。
- MySQL规定了一个50%规则,如果一个词出现在50%以上的行中,则将其作为一个非用词忽略。
- 如果表中的行数少于3行,则全文本搜索不返回结果。
- 忽略词中的单引号。例如don’t索引为dont。
- 不具有词分隔符的语言(包括日语和汉语)不能恰当地返回全文本搜索的结果。
- 仅在MyISAM数据库引擎中支持。
- MySQL暂时还不支持邻近操作符。