本文來自:http://mysqlserverteam.com/innodb%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95%EF%BC%9An-gram-parser/
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的詞。
12345678910mysql> 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),通配符則被忽略。例如,(‘信息*’)轉換為(‘”信息”‘),(‘信息系*’)轉換為(‘”信息 息系”‘)。
123456789101112131415161718mysql> 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!