1. 程式人生 > >MySQL中文全文檢索

MySQL中文全文檢索

arch xxxxx 異或 檢索 擁有 優秀 模糊匹配 運算 query

一、概述
MySQL全文檢索是利用查詢關鍵字和查詢列內容之間的相關度進行檢索,可以利用全文索引來提高匹配的速度。

二、語法
MATCH (col1,col2,...) AGAINST (expr [search_modifier])
search_modifier: { IN BOOLEAN MODE | WITH QUERY EXPANSION }

例如:SELECT * FROM tab_name WHERE MATCH (‘列名1,列名2...列名n‘) AGAINST(‘詞1 詞2 詞3 ... 詞m‘);

即:MATCH 相當於要匹配的列,而 AGAINST 就是要找的內容。
這裏的table需要是MyISAM類型的表,col1、col2 必須是char、varchar或text類型,在查詢之前需要在 col1 和 col2 上分別建立全文索引(FULLTEXT索引)。

三、檢索方式
1、自然語言檢索: IN NATURAL LANGUAGE MODE

2、布爾檢索: IN BOOLEAN MODE
剔除一半匹配行以上都有的詞,譬如說,每個行都有this這個字的話,那用this去查時,會找不到任何結果,這在記錄條數特別多時很有用,
原因是數據庫認為把所有行都找出來是沒有意義的,這時,this幾乎被當作是stopword(中斷詞);但是若只有兩行記錄時,是啥鬼也查不出來的,
因為每個字都出現50%(或以上),要避免這種狀況,請用IN BOOLEAN MODE。

● IN BOOLEAN MODE的特色:
·不剔除50%以上符合的row。
·不自動以相關性反向排序。
·可以對沒有FULLTEXT index的字段進行搜尋,但會非常慢。
·限制最長與最短的字符串。
·套用Stopwords。

● 搜索語法規則:
+ 一定要有(不含有該關鍵詞的數據條均被忽略)。
- 不可以有(排除指定關鍵詞,含有該關鍵詞的均被忽略)。
> 提高該條匹配數據的權重值。
< 降低該條匹配數據的權重值。
~ 將其相關性由正轉負,表示擁有該字會降低相關性(但不像 - 將之排除),只是排在較後面權重值降低。
* 萬用字,不像其他語法放在前面,這個要接在字符串後面。
" " 用雙引號將一段句子包起來表示要完全相符,不可拆字。

SELECT * FROM articles WHERE MATCH (title,content) AGAINST (‘+apple -banana‘ IN BOOLEAN MODE);
+ 表示AND,即必須包含。- 表示NOT,即必須不包含。即:返回記錄必需包含 apple,且不能包含 banner。

SELECT * FROM articles WHERE MATCH (title,content) AGAINST (‘apple banana‘ IN BOOLEAN MODE);
apple和banana之間是空格,空格表示OR。即:返回記錄至少包含apple、banana中的一個。

SELECT * FROM articles WHERE MATCH (title,content) AGAINST (‘+apple banana‘ IN BOOLEAN MODE);
返回記錄必須包含apple,同時banana可包含也可不包含,若包含的話會獲得更高的權重。

SELECT * FROM articles WHERE MATCH (title,content) AGAINST (‘+apple ~banana‘ IN BOOLEAN MODE);
~ 是我們熟悉的異或運算符。返回記錄必須包含apple,若也包含了banana會降低權重。
但是它沒有 +apple -banana 嚴格,因為後者如果包含banana壓根就不返回。

SELECT * FROM articles WHERE MATCH (title,content) AGAINST (‘+apple +(>banana <orange)‘ IN BOOLEAN MODE);
返回必須同時包含“apple banana”或者必須同時包含“apple orange”的記錄。
若同時包含“apple banana”和“apple orange”的記錄,則“apple banana”的權重高於“apple orange”的權重。


3、查詢擴展檢索: WITH QUERY EXPANSION


四、MySQL全文檢索的條件限制
1、在MySQL5.6以下,只有MyISAM表支持全文檢索。在MySQL5.6以上Innodb引擎表也提供支持全文檢索。
2、相應字段建立FULLTEXT索引


五、與全文檢索相關的系統變量:
ft_min_word_len = 全文檢索的最小許可字符(默認4,通過 SHOW VARIABLES LIKE ‘ft_min_word_len‘ 可查看),
中文通常是兩個字就是一個詞,所以做中文的話需要修改這個值為2最好。


六、總結事項
1、預設搜尋是不分大小寫,若要分大小寫,columne 的 character set要從utf8改成utf8_bin。

2、預設 MATCH...AGAINST 是以相關性排序,由高到低。

3、MATCH(title, content)裏的字段必須和FULLTEXT(title, content)裏的字段一模一樣。
如果只要單查title或content一個字段,那得另外再建一個 FULLTEXT(title) 或 FULLTEXT(content),也因為如此,MATCH()的字段一定不能跨table,但是另外兩種搜尋方式好像可以。

4、MySQL不支持中文全文索引,原因很簡單:與英文不同,中文的文字是連著一起寫的,中間沒有MySQL能找到分詞的地方,截至目前MySQL5.6版本是如此,但是有變通的辦法,就是將整句的中文分詞,並按urlencode、區位碼、base64、拼音等進行編碼使之以“字母+數字”的方式存儲於數據庫中。


七、實驗部分

◆ 步驟1 配置my.ini,在my.ini末尾添加如下:

# 修改全文檢索的最小許可字符為2個字符或漢字
ft_min_word_len = 2

完成後“重啟 MySQL 服務”,並用 SHOW VARIABLES LIKE ‘ft_min_word_len‘ 查詢下是否得到了正確的結果值為2,如下圖:
技術分享

◆ 步驟2 創建數據庫(視情況可跳過此步)
CREATE DATABASE search DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;


◆ 步驟3 創建數據表
CREATE TABLE `zzx_articles` (
`id` int(10) unsigned NOT NULL auto_increment,
`title` char(254) default NULL COMMENT ‘標題‘,
`content` text COMMENT ‘內容‘,
`author` char(60) default NULL COMMENT ‘作者‘,
`title_fc` char(254) default NULL COMMENT ‘標題的分詞‘,
`content_fc` text COMMENT ‘內容的分詞‘,
PRIMARY KEY (`id`),
FULLTEXT KEY `zzx_title_fc` (`title_fc`),
FULLTEXT KEY `zzx_content_fc` (`content_fc`),
FULLTEXT KEY `zzx_title_con_fc` (`title_fc`,`content_fc`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

# 如果後期需要添全文加索引可以用如下語句:
alter table `zzx_articles` add fulltext zzx_title_fc(`title_fc`);
alter table `zzx_articles` add fulltext zzx_con_fc(`content_fc`);
alter table `zzx_articles` add fulltext zzx_title_con_fc(`title_fc`,`content_fc`);


◆ 步驟4 插入測試數據
INSERT INTO `zzx_articles` (title_fc,content_fc) VALUES
(‘MySQL Tutorial Linux red‘,‘DBMS stands for DataBase ok‘),
(‘How To Use MySQL Well‘,‘After you went through blue‘),
(‘Optimizing MySQL ok‘,‘In this tutorial we will optimizing‘),
(‘MySQL vs this YourSQL blue red‘,‘1. Never run mysqld as root red‘),
(‘MySQL Tricks blue‘,‘In the following database‘),
(‘MySQL Security‘,‘When configured properly, MySQL‘),
(‘中華‘,‘中華人民共和國 ‘),
(‘中華情 和諧‘,‘上海 和諧‘),
(‘汙染之都‘,‘你好 我是 北京 人‘),
(‘北京精神‘,‘創新 愛國 包容 厚顏‘)

插入結果如下圖:
技術分享


◆ 步驟5 搜索語法規則、排序 實驗
說明:匹配語句 MATCH (col1,col2,...) AGAINST (expr [search_modifier]) 匹配完成後,會返回此條數據的權重值(權重值1 ≈ 各個詞的匹配結果權重值之和),我們利用此權重值“由高到低”排序可優化查詢結果。

------------------------------------------------------------------------------------------------------------------------------

? 實驗1:只對 title_fc 索引字段做全文檢索,並顯示每條數據的權重值

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:1.當沒有加 + - 這樣的過濾符號時,這些關鍵詞是“或(or)”的關系,即:要麽匹配optimizing,要麽匹配ok,要麽匹配red,要麽匹配blue。
2.通過上面實驗,發現當某條數據有多個關鍵詞匹配時(如:red blue),此條數據的權重值會略高:
此條數據權重值 ≈ optimizing權重值 + ok權重值 + red權重值 + blue權重值
理論上來說,當一條數據能匹配上的關鍵詞越多,則此條數據的權重值越高,排名越靠前。

------------------------------------------------------------------------------------------------------------------------------

? 實驗2:過濾條件:必須包含"red"關鍵詞

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok +red‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:使用了過濾符號 + ,表示查詢結果中,任一條數據都必須包含"red"這個詞,不包含"red"這個詞的行均被忽略。

------------------------------------------------------------------------------------------------------------------------------

? 實驗3:過濾條件:必須包含"red"關鍵詞,如果匹配到的行中還含有"blue"關鍵詞,則會對此條數據增加權重:

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok +red blue‘ IN BOOLEAN MODE) order by title_score DESC

或下面寫法:
SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red >blue‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok +red >blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:與實驗2比較,當包含了red的行中,若也包含blue關鍵詞,權重確實增加了(如:id=4這條)。

------------------------------------------------------------------------------------------------------------------------------

? 實驗4:過濾條件:必須包含"red"關鍵詞,並且不能包含"blue"關鍵詞

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok +red -blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:可見 + - 這兩個符號是表示“並且(and)”的意思,即:必須包含red關鍵詞 and 不能包含blue關鍵詞。

------------------------------------------------------------------------------------------------------------------------------

? 實驗5:過濾條件:必須包含"red"關鍵詞,如果匹配到的行中還包含"blue"關鍵詞則降低此條數據權重

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok +red ~blue‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok +red ~blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:這個實驗沒有看到明顯效果,但 ~ 過濾符確實是降低此權重符

------------------------------------------------------------------------------------------------------------------------------

? 實驗6:過濾條件:匹配包含單詞“red”和“Linux” 的行,或包含“red” 和“blue”的行(無先後順序)
然而包含 “apple Linux”的行較包含“apple blue”的行有更高的權重值。

SELECT *,MATCH (title_fc) AGAINST (‘+red +(>Linux <blue)‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘+red +(>Linux <blue)‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

------------------------------------------------------------------------------------------------------------------------------

? 實驗7:過濾條件:匹配關鍵詞以 re 開頭,或以 bl 開頭的數據行

SELECT *,MATCH (title_fc) AGAINST (‘re* bl*‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘re* bl*‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:註意 * 是寫在後面哦,此時相當於 like 模糊匹配,沒有權重值了

------------------------------------------------------------------------------------------------------------------------------

? 實驗8:過濾條件:匹配查找字符串“To Use MySQL”關鍵詞

SELECT *,MATCH (title_fc) AGAINST (‘"To Use MySQL"‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘"To Use MySQL"‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:此時是把雙引號內的的字符串看成一個關鍵詞,若不用雙引號則是將 To Use MySQL 三個關鍵詞去分別匹配,兩者有區別;

------------------------------------------------------------------------------------------------------------------------------

? 實驗9:在實驗1基礎上,將blue的權重值忽視不要(註意與實驗1比較)

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:在實驗1的基礎上,此時去除select字段條件裏的blue關鍵詞,但在where裏去仍保留blue關鍵詞。
我的本意是想正常匹配“optimizing ok red blue”這幾個關鍵詞,但不想得到blue的權重值(忽視blue的權重值)。
查詢的結果是含有blue關鍵詞的數據的權重值會略降低了。
通過“降重”——忽略某些關鍵詞權重值的方式可使部分數據權重值減小,進而影響排序。

------------------------------------------------------------------------------------------------------------------------------

? 實驗10:在實驗9的基礎上,在select字段條件裏增加幾個red關鍵詞,where裏的關鍵詞保持不變(註意與實驗1 實驗9比較)。

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red red red‘) as title_score
FROM zzx_articles
WHERE MATCH (title_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by title_score DESC

技術分享

總結:發現只要含有 red 關鍵詞的數據的權重值都增加了,排序也發生了變化。
說明通過“提重”——重復多次某些關鍵詞權重值的方式可使部分數據權重值增加,進而影響排序。

------------------------------------------------------------------------------------------------------------------------------

? 實驗11:同時對 title_fc 和 content_fc 兩字段做全文檢索

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score,MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) as content_score
FROM zzx_articles
WHERE MATCH (title_fc,content_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by title_score DESC,content_score DESC

技術分享

總結:通過實驗發現,又成功的取到了 content_fc 字段匹配的權重值,排序方式是首要按title字段權重降序排序,次要按 content_fc 權重降序排序。
另外我發現 ok 這個關鍵詞在對“title_fc char(254)”字段匹配時得到匹配值為2.1xxxxxxx,但對“content_fc text”字段匹配時權重值去為0,
這是MySQL對各英文單詞權重值的給予有自己的算法,我們無權幹涉。所以當我們發現有些單詞的權重值為零甚至為負時,不用過於糾結,
因為MySQL有自己的算法。

關於排序,首要按 title_score 字段權重降序排序,次要按 content_score 權重降序排序,這樣的排序規則看起來更科學了,但我想優秀的搜索引擎絕不至於簡單如此吧,繼續下面的實驗:

------------------------------------------------------------------------------------------------------------------------------

? 實驗12:進一步優化 排序規則

看一個SQL語句原型,查詢“字段1,字段2”兩字段,同時將每條數據的“字段1”與“字段2”的求和作為“字段3”字段:
select 字段1,字段2,字段1 + 字段2 as 字段3
from 表名
where .....

下面將 title_fc 和 content_fc 兩權重值求和,放入新字段 score1 中,並按 score1 首要排序,title_score 次之,content_score再次之:

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score,MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) as content_score,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) + MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) as score1
FROM zzx_articles
WHERE MATCH (title_fc,content_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by score1 DESC,title_score DESC,content_score DESC

技術分享

總結:相對而言,如果 title_fc 和 content_fc 都匹配上了,應給予靠前的排名吧。所以首要按其 title_fc 和 content_fc 兩權重值之和排名,
次要再考慮 title_fc、content_fc 排序。

------------------------------------------------------------------------------------------------------------------------------

? 實驗13:另一個角度看排序
看一個SQL語句原型,如果性別字段值為“1”顯示“男”否則顯示“女”
select *,IF(sex=1,"男","女") as ssva from 表名 where id = 1

我的新排序思路:如果 title_fc 和 content_fc 同時匹配上的行做首要排序,然後對只匹配上 title_fc 的做次要排序,只匹配上 content_fc 的再次要排序。 (對於實驗5的排序不科學之處在於:如果有一個對content_fc關鍵詞的匹配權重很高,導致了求和後 score1 的值也高,但對title_fc的匹配權重去為0,由於score1值高卻排到了前面這不一定科學吧?)

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score,MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) as content_score,IF(MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) > 0 AND MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) > 0,1,0) as score1
FROM zzx_articles
WHERE MATCH (title_fc,content_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by score1 DESC,title_score DESC,content_score DESC

技術分享

總結:如果 title_fc 和 content_fc 都匹配上了,做為優先排序理所當然,但也應考慮局部權重值過高的問題哦。

------------------------------------------------------------------------------------------------------------------------------

? 實驗14:優化實驗13,可支持更復雜的條件排序
看一個SQL語句原型,CASE WHEN THEN END 結構:

CASE <單值表達式>
WHEN <表達式值> THEN <SQL語句或者返回值>
WHEN <表達式值> THEN <SQL語句或者返回值>
...
WHEN <表達式值> THEN <SQL語句或者返回值>
ELSE <SQL語句或者返回值>
END

有一表查詢:大於或等於80表示顯示為“優秀”,大於或等於60顯示為“及格”,小於60分顯示為“不及格”。
select (CASE WHEN 語文>=80 THEN ‘優秀‘ WHEN 語文>=60 THEN ‘及格‘ ELSE ‘不及格‘ END) as 語文,
(CASE WHEN 數學>=80 THEN ‘優秀‘ WHEN 數學>=60 THEN ‘及格‘ ELSE ‘不及格‘ END) as 數學,
(CASE WHEN 英語>=80 THEN ‘優秀‘ WHEN 英語>=60 THEN ‘及格‘ ELSE ‘不及格‘ END) as 英語
from table_name

# 實例測試一下
select *,(CASE WHEN id<8 THEN ‘5‘ WHEN id=8 THEN ‘4‘ ELSE ‘0‘ END) as newfield
from zzx_articles
where id>5

我的新排序思路:如果 title_fc 和 content_fc 的權重值“同時大於0且相等”為首要排序,“同時大於0且不相等”的為次要排序,
“title_fc 大於0的再次要排序”,如果用 IF() 貌似不好實現,看下面語句:

SELECT *,MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) as title_score,MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) as content_score,
(CASE WHEN MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) > 0 AND MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) = MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) THEN ‘3‘ WHEN MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) > 0 AND MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) <> MATCH (content_fc) AGAINST (‘optimizing ok red blue‘) THEN ‘2‘ WHEN MATCH (title_fc) AGAINST (‘optimizing ok red blue‘) > 0 THEN ‘1‘ ELSE ‘0‘ END) AS score1
FROM zzx_articles
WHERE MATCH (title_fc,content_fc) AGAINST (‘optimizing ok red blue‘ IN BOOLEAN MODE) order by score1 DESC,title_score DESC,content_score DESC

技術分享

總結:本實驗的排序未必合乎科學,但引出一個更復雜規則的排序方式、角度,多種排序結合使用才能做出更合理的排序,才能使你的搜索引擎更加智能。拋磚引玉,或許你有更好的排序,請也分享給我。

------------------------------------------------------------------------------------------------------------------------------

? 實驗15:中文全文檢索
MySQL不支持中文全文檢索,因為中文一句話是連著寫的,不像英文單詞間有空格分隔。解決辦法就是中文分詞(關於中文分詞請參閱其它文章),
如果你的MySQL是安裝在Windows平臺上的,可以不用轉碼直接存儲中文就可以使用全文索引,如本例。但是如果你的MySQL是安裝在Linux上的則需要進行轉編碼(urlencode / base64_encode / json_encode / 區位 / 拼音)等方案,具體方案參看其它博文。

SELECT *,MATCH (title_fc) AGAINST (‘中華 北京 和諧 security‘) as title_score,MATCH (content_fc) AGAINST (‘中華 北京 和諧 security‘) as content_score,(case when MATCH (title_fc) AGAINST (‘中華 北京 和諧 security‘) > 0 and MATCH (content_fc) AGAINST (‘中華 北京 和諧 security‘) > 0 then ‘5‘ when MATCH (title_fc) AGAINST (‘中華 北京 和諧 security‘) > 0 and MATCH (content_fc) AGAINST (‘中華 北京 和諧 security‘) = 0 then ‘4‘ else ‘0‘ end) as score1
FROM zzx_articles
WHERE MATCH (title_fc,content_fc) AGAINST (‘中華 北京 和諧 security‘ IN BOOLEAN MODE) order by score1 DESC,title_score DESC,content_score DESC

技術分享

來源:http://www.cnblogs.com/martinzhang/p/3220345.html

MySQL中文全文檢索