InnoDB默认的全文索引parser非常合适于Latin,因为Latin是通过空格来分词的。但对于像中文,日文和韩文来说,没有这样的分隔符。一个词可以由多个字来组成,所以我们需要用不同的方式来处理。在MySQL 5.7.6中我们能使用一个新的全文索引插件来处理它们:n-gram parser.
什么是N-gram?
在全文索引中,n-gram就是一段文字里面连续的n个字的序列。例如,用n-gram来对”信息系统”来进行分词,得到的结果如下:
1
2
3
4
|
N
=
1
:
'信'
,
'息'
,
'系'
,
'统'
;
N
=
2
:
'信息'
,
'息系'
,
'系统'
;
N
=
3
:
'信息系'
,
'息系统'
;
N
=
4
:
'信息系统'
;
|
如何在InnoDB中使用N-gram Parser?
N-gram parser是默认加载到MySQL中并可以直接使用的。我们只需要在DDL中创建全文索引时使用WITH PARSER ngram。比如,下面的SQL语句在MySQL 5.7.6及更高版本上可以运行。
1
2
3
4
5
6
7
8
9
|
mysql
>
CREATE
TABLE
articles
(
FTS_DOC_ID
BIGINT
UNSIGNED
AUTO_INCREMENT
NOT
NULL
PRIMARY
KEY
,
title
VARCHAR
(
100
)
,
FULLTEXT
INDEX
ngram_idx
(
title
)
WITH
PARSER
ngram
)
Engine
=
InnoDB
CHARACTER
SET
utf8mb4
;
Query
OK
,
0
rows
affected
(
0.06
sec
)
mysql
>
# ALTER TABLE articles ADD FULLTEXT INDEX ngram_idx(title) WITH PARSER ngram;
mysql
>
# CREATE FULLTEXT INDEX ngram_idx ON articles(title) WITH PARSER ngram;
|
我们引入了一个新的全局变量叫ngram_token_size。由它来决定n-gram中n的大小,也就是词的大小。它的默认值是2,这个时候,我们使用的是bigram。它的合法的取值范围是1到10。现在,我们很自然会想到一个问题:实际应用中应该如何设置ngram_token_size值的大小呢?当然,我们推荐使用2。但是你也可以通过如下这个简单的规则来可以选择任何合法的值:设置到你希望能查询到的最小的词的大小。如果你想查询到单个字,那么我们需要设置为1。 ngram_token_size的值设置的越小,全文索引占用的空间也越小。一般来说,查询正好等于ngram_token_size的词,速度会更快,但是查询比它更长的词或短语,则会变慢。
N-gram分词处理
N-gram parser和系统默认的全文索引parser有如下不同点:
- 词大小检查:因为有了ngram_token_size,所以innodb_ft_min_token_size和innodb_ft_max_token_size将不适用于n-gram。
- 无用词(stopword)处理:通常,对于一个新的词,我们会查找stopwords表,看是否有匹配的词。如果有,这个词就不会加入到全文索引中。但是在n-gram中,我们会查找stopwords表,看是否包含里面的词。这样处理的原因是,在中日韩的文本中,有很多没有意义的字符,词语和标点符号。比如,如果我们把‘的’加入到stopwords表中,那么对于句子‘信息的系统’,在默认情况下我们分词结果为‘信息’,‘系统’。其中‘息的’和‘的系’被过滤掉了。
我们可以通过查询INFORMATION_SCHEMA.INNODB_FT_INDEX_CACHE和INFORMATION_SCHEMA.INNODB_FT_TABLE_TABLE来查询哪些词在全文索引里面。这是一个非常有用的调试工具。如果我们发现一个包含某个词的文档,没有如我们所期望的那样出现在查询结果中,那么这个词可能是因为某些原因不在全文索引里面。比如,它含有stopword,或者它的大小小于ngram_token_size等等。这个时候我们就可以通过查询这两个表来确认。下面是一个简单的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
mysql
>
INSERT
INTO
articles
(
title
)
VALUES
(
'信息系统'
)
;
Query
OK
,
1
row
affected
(
0.01
sec
)
mysql
>
SET
GLOBAL
innodb_ft_aux_table
=
"test/articles"
;
Query
OK
,
0
rows
affected
(
0.00
sec
)
mysql
>
SELECT *
FROM
INFORMATION_SCHEMA
.
INNODB_FT_INDEX_CACHE
;
+
--
--
--
--
+
--
--
--
--
--
--
--
+
--
--
--
--
--
--
-
+
--
--
--
--
--
-
+
--
--
--
--
+
--
--
--
--
--
+
|
WORD
|
FIRST_DOC_ID
|
LAST_DOC_ID
|
DOC_COUNT
|
DOC_ID
|
POSITION
|
+
--
--
--
--
+
--
--
--
--
--
--
--
+
--
--
--
--
--
--
-
+
--
--
--
--
--
-
+
--
--
--
--
+
--
--
--
--
--
+
|
信息
|
1
|
1
|
1
|
1
|
0
|
|
息系
|
1
|
1
|
1
|
1
|
3
|
|
系统
|
1
|
1
|
1
|
1
|
6
|
+
--
--
--
--
+
--
--
--
--
--
--
--
+
--
--
--
--
--
--
-
+
--
--
--
--
--
-
+
--
--
--
--
+
--
--
--
--
--
+
3
rows
in
set
(
0.00
sec
)
|
N-gram查询处理
文本查询(Text Searches)
- 在自然语言模式(NATURAL LANGUAGE MODE)下,文本的查询被转换为n-gram分词查询的并集。例如,(‘信息系统’)转换为(‘信息 息系 系统’)。下面一个例子:
自然语言模式示例12345678910111213141516mysql > INSERT INTO articles ( title ) VALUES ( '信息系统' ) , ( '信息 系统' ) , ( '信息的系统' ) , ( '信息' ) , ( '系统' ) , ( '息系' ) ;Query OK , 6 rows affected ( 0.01 sec )Records : 6 Duplicates : 0 Warnings : 0mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '信息系统' IN NATURAL LANGUAGE MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| 1 | 信息系统 || 6 | 息系 || 2 | 信息 系统 || 3 | 信息的系统 || 4 | 信息 || 5 | 系统 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +6 rows in set ( 0.01 sec ) - 在布尔模式(BOOLEAN MODE),文本查询被转化为n-gram分词的短语查询。例如,(‘信息系统’)转换为(“‘信息 息系 系统'”)。
布尔模式示例1234567mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '信息系统' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| 1 | 信息系统 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +1 row in set ( 0.00 sec )
通配符查询(Wildcard Searches)
- 如果前缀的长度比ngram_token_size小,那么查询结果将返回在全文索引中所有以这个词作为前缀的n-gram的词。
通配符查询示例-112345678910mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '信*' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| 1 | 信息系统 || 2 | 信息 系统 || 3 | 信息的系统 || 4 | 信息 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +4 rows in set ( 0.00 sec ) - 如果前缀的长度大于等于ngam_token_size,那么这个查询则转换为一个短语(phrase search),通配符则被忽略。例如,(‘信息*’)转换为(‘”信息”‘),(‘信息系*’)转换为(‘”信息 息系”‘)。
通配符查询示例-2123456789101112131415161718mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '信息*' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +| 1 | 信息系统 || 2 | 信息 系统 || 3 | 信息的系统 || 4 | 信息 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- -- - +4 rows in set ( 0.00 sec )mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '信息系*' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| 1 | 信息系统 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +1 row in set ( 0.00 sec )
短语查询(Phrase Searches)
- 短语查询则被转换为n-gram分词的短语查询。比如,(‘信息系统’)转换为(‘”信息 息系 系统”‘)。
短语查询示例123456789101112131415mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '"信息系统"' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +| 1 | 信息系统 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- +1 row in set ( 0.00 sec )mysql > SELECT * FROM articles WHERE MATCH ( title ) AGAINST ( '"信息 系统"' IN BOOLEAN MODE ) ;+ -- -- -- -- -- -- + -- -- -- -- -- -- -- - +| FTS_DOC_ID | title |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- - +| 2 | 信息 系统 |+ -- -- -- -- -- -- + -- -- -- -- -- -- -- - +1 row in set ( 0.01 sec )
如果您想了解更多关于InnoDB全文索引的详细内容,可以参考用户手册中InnoDB全文索引的部分,还有Jimmy在Dr. Dobb上的精彩文章。如果您想了解更多关于n-gram的详细内容,则可以参考用户手册中n-gram parser的部分。
我们很高兴在MySQL 5.7全文索引中增强对中日韩文的支持,这也是我们工作中很重要的部分,希望这个功能对大家有帮助。如果您有任何问题,可以在本blog中进行评论,提交一个服务需求,或者提交一个bug报告。
最后,感谢您使用MySQL