1、概述
MySQL全文檢索是利用查詢關鍵字和查詢列內容之間的相關度進行檢索,能夠利用全文索引來提升匹配的速度。mysql
2、語法
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類型的表,col一、col2 必須是char、varchar或text類型,在查詢以前須要在 col1 和 col2 上分別創建全文索引(FULLTEXT索引)。算法
3、檢索方式
一、天然語言檢索: IN NATURAL LANGUAGE MODEsql
二、布爾檢索: IN BOOLEAN MODE
剔除一半匹配行以上都有的詞,譬如說,每一個行都有this這個字的話,那用this去查時,會找不到任何結果,這在記錄條數特別多時頗有用,
緣由是數據庫認爲把全部行都找出來是沒有意義的,這時,this幾乎被看成是stopword(中斷詞);可是若只有兩行記錄時,是啥鬼也查不出來的,
由於每一個字都出現50%(或以上),要避免這種情況,請用IN BOOLEAN MODE。數據庫
● IN BOOLEAN MODE的特點:
·不剔除50%以上符合的row。
·不自動以相關性反向排序。
·能夠對沒有FULLTEXT index的字段進行搜尋,但會很是慢。
·限制最長與最短的字符串。
·套用Stopwords。
● 搜索語法規則:
+ 必定要有(不含有該關鍵詞的數據條均被忽略)。
- 不能夠有(排除指定關鍵詞,含有該關鍵詞的均被忽略)。
> 提升該條匹配數據的權重值。
< 下降該條匹配數據的權重值。
~ 將其相關性由正轉負,表示擁有該字會下降相關性(但不像 - 將之排除),只是排在較後面權重值下降。
* 萬用字,不像其餘語法放在前面,這個要接在字符串後面。
" " 用雙引號將一段句子包起來表示要徹底相符,不可拆字。json
SELECT * FROM articles WHERE MATCH (title,content) AGAINST ('+apple -banana' IN BOOLEAN MODE);
+ 表示AND,即必須包含。- 表示NOT,即必須不包含。即:返回記錄必需包含 apple,且不能包含 banner。app
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壓根就不返回。this
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」的權重。搜索引擎
三、查詢擴展檢索: WITH QUERY EXPANSION
4、MySQL全文檢索的條件限制
一、在MySQL5.6如下,只有MyISAM表支持全文檢索。在MySQL5.6以上Innodb引擎表也提供支持全文檢索。
二、相應字段創建FULLTEXT索引
5、與全文檢索相關的系統變量:
ft_min_word_len = 全文檢索的最小許可字符(默認4,經過 SHOW VARIABLES LIKE 'ft_min_word_len' 可查看),
中文一般是兩個字就是一個詞,因此作中文的話須要修改這個值爲2最好。
6、總結事項
一、預設搜尋是不分大小寫,若要分大小寫,columne 的 character set要從utf8改爲utf8_bin。
二、預設 MATCH...AGAINST 是以相關性排序,由高到低。
三、MATCH(title, content)裏的字段必須和FULLTEXT(title, content)裏的字段如出一轍。
若是隻要單查title或content一個字段,那得另外再建一個 FULLTEXT(title) 或 FULLTEXT(content),也由於如此,MATCH()的字段必定不能跨table,可是另外兩種搜尋方式好像能夠。
四、MySQL不支持中文全文索引,緣由很簡單:與英文不一樣,中文的文字是連着一塊兒寫的,中間沒有MySQL能找到分詞的地方,截至目前MySQL5.6 版本是如此,可是有變通的辦法,就是將整句的中文分詞,並按urlencode、區位碼、base6四、拼音等進行編碼使之以「字母+數字」的方式存儲於 數據庫中。
7、實驗部分
◆ 步驟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