MySQL 5.7 中文全文檢索
在 MySQL 5.7.6 之前,全文索引只支持英文全文索引,不支持中文全文索引,需要利用分詞器把中文段落預處理拆分成單詞,然后存入數據庫。
從 MySQL 5.7.6 開始,MySQL內置了ngram全文解析器,用來支持中文、日文、韓文分詞。
本文使用的MySQL 版本是 5.7.24,InnoDB數據庫引擎。
ngram全文解析器
ngram就是一段文字里面連續的n個字的序列。
ngram全文解析器能夠對文本進行分詞,每個單詞是連續的n個字的序列。
例如,用ngram全文解析器對“恭喜發財”進行分詞:
n=1: '恭', '喜', '發', '財' n=2: '恭喜', '喜發', '發財' n=3: '恭喜發', '喜發財' n=4: '恭喜發財'
MySQL 中使用全局變量 ngram_token_size 來配置 ngram 中 n 的大小,它的取值范圍是1到10,默認值是 2。通常ngram_token_size設置為要查詢的單詞的最小字數。如果需要搜索單字,就要把ngram_token_size設置為1。在默認值是2的情況下,搜索單字是得不到任何結果的。因為中文單詞最少是兩個漢字,推薦使用默認值2。
全局變量 ngram_token_size 的兩種設置方法:
【方式1】:使用啟動命令 mysqld 時,傳參如下:
mysqld --ngram_token_size=2
【方式2】:在修改MySQL配置文件 my.ini 中,末尾增加一行 ngram_token_size 的參數設置:
[mysql] # 設置mysql客戶端默認字符集 default-character-set=utf8 [mysqld] #設置3306端口 port = 3306 server_id=100 # 設置mysql的安裝目錄 basedir=D:\mysql-5.7.24-winx64 # 設置mysql數據庫的數據的存放目錄 datadir=D:\mysql-5.7.24-winx64\data # 允許最大連接數 max_connections=200 # 服務端使用的字符集默認為8比特編碼的latin1字符集 character-set-server=utf8 # 創建新表時將使用的默認存儲引擎 default-storage-engine=INNODB # 全文檢索分詞數 ngram_token_size=2
創建全文索引
1、建表 t_article
建表語句如下:
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 50724 Source Host : localhost:3306 Source Schema : test_db Target Server Type : MySQL Target Server Version : 50724 File Encoding : 65001 Date: 07/07/2019 19:54:33 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_article -- ---------------------------- DROP TABLE IF EXISTS `t_article`; CREATE TABLE `t_article` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `content` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL, PRIMARY KEY (`id`) USING BTREE, FULLTEXT INDEX `fulltext_title_content`(`title`, `content`) WITH PARSER `ngram` ) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_article -- ---------------------------- INSERT INTO `t_article` VALUES (1, '八榮八恥 1', '以熱愛祖國為榮、以危害祖國為恥'); INSERT INTO `t_article` VALUES (2, '八榮八恥 2', '以服務人民為榮、以背離人民為恥'); INSERT INTO `t_article` VALUES (3, '八榮八恥 3', '以崇尚科學為榮,以愚昧無知為恥'); INSERT INTO `t_article` VALUES (4, '八榮八恥 4', '以辛勤勞動為榮,以好逸惡勞為恥'); INSERT INTO `t_article` VALUES (5, '八榮八恥 5', '以團結互助為榮,以損人利己為恥'); INSERT INTO `t_article` VALUES (6, '八榮八恥 6', '以誠實守信為榮,以見利忘義為恥'); INSERT INTO `t_article` VALUES (7, '八榮八恥 7', '以遵紀守法為榮,以違法亂紀為恥'); INSERT INTO `t_article` VALUES (8, '八榮八恥 8', '以艱苦奮斗為榮,以驕奢淫逸為恥'); INSERT INTO `t_article` VALUES (9, '滿江紅', '靖康恥,尤未雪'); INSERT INTO `t_article` VALUES (10, '第一生產力', '科學技術是第一 生產力'); INSERT INTO `t_article` VALUES (11, '團結互助', '團結就是力量'); INSERT INTO `t_article` VALUES (12, 'Blue Red', 'Red Black'); INSERT INTO `t_article` VALUES (13, '我是奇跡 1', '你好,我是奇跡2'); INSERT INTO `t_article` VALUES (14, '恭喜發財', '你好'); SET FOREIGN_KEY_CHECKS = 1;
2、創建全文索引
創建字段 title 和 content 的聯合全文索引,語句如下:
alter table `t_article` add fulltext index fulltext_title_content(`title`,`content`) WITH PARSER ngram;
重連數據庫,刷新查看索引的創建情況:
3、全文檢索查詢
1)查詢 title 或者 content 中包含“祖國”的記錄,查詢語句如下:
select *, MATCH (title, content) AGAINST ('祖國') as score from t_article where MATCH (title, content) AGAINST ('祖國' IN NATURAL LANGUAGE MODE);
查詢結果如下:
2)查詢 title 或者 content 中包含“團結勞動”的記錄,查詢語句如下:
select *, MATCH (title, content) AGAINST ('團結勞動') as score from t_article where MATCH (title, content) AGAINST ('團結勞動' IN NATURAL LANGUAGE MODE);
查詢結果如下(查詢結果,默認會按照得分 score ,從高到低排序):
3)查詢 title 或者 content 中包含“為榮”的記錄,查詢語句如下:
select *, MATCH (title, content) AGAINST ('為榮') as score from t_article where MATCH (title, content) AGAINST ('為榮' IN NATURAL LANGUAGE MODE);
查詢結果如下(可以看到,此處得分是一樣的):
4、特殊情況
1)查詢單個漢字
比如,查詢 title 或者 content 中包含“力”的記錄,查詢語句如下:
select *, MATCH (title, content) AGAINST ('力') as score from t_article where MATCH (title, content) AGAINST ('力' IN NATURAL LANGUAGE MODE);
查詢結果如下:
從上可以看到,查不到結果。原因是設置的全局變量 ngram_token_size 的值為 2。
如果想查詢單個漢字,需要在配置文件 my.ini 中修改 ngram_token_size = 1 ,並重啟 mysqld 服務。
使用如下命令查詢 ngram_token_size 的設置情況:
show VARIABLES like 'ngram_token_size';
查詢結果如下:
此時,再次執行上面的單個漢字“力”查詢語句,結果如下(由於記錄id = 10 中包含兩個“力”,記錄id = 11 中只有一個“力”。所以,查詢結果中,前者得到的分數是后者的兩倍):
2)查詢單個字段
比如,查詢字段 content 中包含“誠實”,查詢語句如下:
select *, MATCH (content) AGAINST ('誠實') as score from t_article where MATCH (content) AGAINST ('誠實' IN NATURAL LANGUAGE MODE);
查詢結果報錯如下:
select *, MATCH (content) AGAINST ('誠實') as score from t_article where MATCH (content) AGAINST ('誠實' IN NATURAL LANGUAGE MODE) > 1191 - Can't find FULLTEXT index matching the column list > 時間: 0.002s
原因是沒有給字段 content 單獨創建全文檢索的索引。
給字段 content 創建全文檢索索引,語句如下:
alter table `t_article` add fulltext index fulltext_content(`content`) WITH PARSER ngram;
執行結果如下:
再次執行 查詢字段 content 中包含“誠實”,查詢結果如下:
同理,如下需要單獨對字段 title 進行全文檢索,需要給字段 title 創建全文檢索的索引,創建語句如下:
alter table `t_article` add fulltext index fulltext_title(`title`) WITH PARSER ngram;
結果如下:
5、全文檢索模式
常用的全文檢索模式有兩種:
1、自然語言模式(NATURAL LANGUAGE MODE)
自然語言模式是 MySQL 默認的全文檢索模式。
自然語言模式不能使用操作符,不能指定關鍵詞必須出現或者必須不能出現等復雜查詢。
2、BOOLEAN 模式(BOOLEAN MODE)
BOOLEAN 模式可以使用操作符,可以支持指定關鍵詞必須出現或者必須不能出現或者關鍵詞的權重高還是低等復雜查詢。
BOOLEAN 模式,舉例如下:
'apple banana' 無操作符,表示或,要么包含apple,要么包含banana '+apple +juice' 必須同時包含兩個詞 '+apple macintosh' 必須包含apple,但是如果也包含macintosh的話,相關性會更高。 '+apple -macintosh' 必須包含apple,同時不能包含macintosh。 '+apple ~macintosh' 必須包含apple,但是如果也包含macintosh的話,相關性要比不包含macintosh的記錄低。 '+apple +(>juice <pie)' 查詢必須包含apple和juice或者apple和pie的記錄,但是apple juice的相關性要比apple pie高。 'apple*' 查詢包含以apple開頭的單詞的記錄,如apple、apples、applet。 '"some words"' 使用雙引號把要搜素的詞括起來,效果類似於like '%some words%', 例如“some words of wisdom”會被匹配到,而“some noise words”就不會被匹配。
舉例說明:
1)查詢字段 content 中包含 “團結”和“力量”的語句如下:
select *, MATCH (content) AGAINST ('+團結 +力量') as score from t_article where MATCH (content) AGAINST ('+團結 +力量' IN BOOLEAN MODE);
查詢結果如下:
2)查詢字段 content 中包含 “團結”,但不包含“力量”的語句如下:
select *, MATCH (content) AGAINST ('+團結 -力量') as score from t_article where MATCH (content) AGAINST ('+團結 -力量' IN BOOLEAN MODE);
查詢結果如下:
3)查詢字段 conent 中包含“團結”或者“力量”的語句如下:
select *, MATCH (content) AGAINST ('團結 力量') as score from t_article where MATCH (content) AGAINST ('團結 力量' IN BOOLEAN MODE);
查詢結果如下:
6、注意
1)只能在類型為 CHAR、VARCHAR 或者 TEXT 的字段上創建全文索引。
2)全文索引只支持 InnoDB 和 MyISAM 引擎。
3)MATCH (columnName) AGAINST ('keywords')。MATCH()函數使用的字段名,必須要與創建全文索引時指定的字段名一致。
如上面的示例,MATCH (title,content)使用的字段名與全文索引t_article(title,conent)定義的字段名一致。
如果要對 title 或者 content 字段分別進行查詢,就需要在 title 和 content 字段上分別創建新的全文索引。
4)MATCH()函數使用的字段名只能是同一個表的字段,因為全文索引不能夠跨多個表進行檢索。
5)如果要導入大數據集,使用先導入數據,再在表上創建全文索引的方式,要比先在表上創建全文索引再導入數據的方式快很多。