Elasticsearch之match_phrase小坑記錄

一、問題拋出

某個詞組在Elasitcsearch中的某個document中存在,就必定經過某種匹配方式把它搜出來。 
舉例:app

title=公路局正在治理解放大道路面積水問題。

輸入關鍵詞:道路,可否搜索到這個document呢? 
實際應用中可能須要: 
1)檢索關鍵詞」理解」、」解放」、」道路」、「理解放大」,都能搜出這篇文檔。 
2)單個的字拆分「治」、「水」太多幹擾,不要被檢索出來。 
3)待檢索的詞不在詞典中,也必需要查到。 
4)待檢索詞只要在原文title或content中出現,都要檢索到。 
5)檢索要快,要摒棄wildcard模糊匹配性能問題。
性能

二、問題分析

經常使用的stand標準分詞,能夠知足要求1)、3)、4)、5)。 
標準分詞器是什麼鬼? 
標準分析儀是默認分析儀,若是沒有指定,則默認使用該分詞器。 它提供了基於語法的標記,而且適用於大多數語言。 
對於中文字符串,會逐個漢字分詞。 
標準分詞器的結果以下:搜索引擎

GET /ik_index/_analyze?analyzer=standard { "text":"公路局正在治理解放大道路面積水問題" } 公,路,局,正,在,治,理,解,放,大,道,路,面,積,水,問,題

但,會出現冗餘數據很是多。 spa

針對要求2),排除match檢索,排除stand分詞。 
針對要求5),排除wildcard模糊檢索。 
針對要求3)、4),新詞也要被檢索到好比:「聲臨其境」、「孫大剩」等也要能被搜索到。 
針對要求1),採用match_phrase貌似靠譜些。code

三、小試牛刀

先使用IK-max-word細粒度分詞器,結合match_phrase試一試?blog

步驟1:定義索引和Mapping

PUT ik_index { "mappings":{ "ik_type":{ "properties":{ "title":{ "type":"text", "fields":{ "ik_my_max":{ "type":"text", "analyzer":"ik_max_word" }, "ik_my_smart":{ "type":"text", "analyzer":"ik_smart" }, "keyword":{ "type":"keyword", "ignore_above":256 } } } } } } }

 

這裏,爲了驗證分詞,同時使用了ik_smart和ik_max兩種分詞。 索引

實際開發中不須要,由於:兩種分詞共存,會致使導入數據建立索引的時候,索引會很是大,對磁盤和檢索性能都會有影響。token

步驟2:插入文檔

POST ik_index/ik_type/3 { "title":"公路局正在治理解放大道路面積水問題" }

 

步驟3:實施檢索開發

 

POST ik_index/ik_type/_search { "profile":"true", "query": { "match_phrase": { "title.ik_my_max":"道路" } } }

 

搜索結果以下: 
無結果返回。
文檔

{ "took": 1, "timed_out": false, "_shards": { "total": 5, "successful": 5, "failed": 0 }, "hits": { "total": 0, "max_score": null, "hits": [] } }

爲何使用了max_word細粒度分詞,使用了match_pharse檢索,爲何沒有結果。 
分析一下: 
細粒度ik_max_word分詞結果爲:

GET /ik_index/_analyze?analyzer=ik_max_word { "text":"公路局正在治理解放大道路面積水問題" } 公路局 ,公路 ,路局 ,路 ,局正 ,正在 ,正 ,治理 ,治 ,理解 , 理 ,解放 ,解 ,放大 ,大道 ,大 ,道路 ,道 ,路面 ,路 , 面積 ,面 ,積水 ,積 ,水 ,問題

 

以上方式,除了能夠返回分詞結果外,還能返回詞所在的位置position。

構建索引的時候,道路被拆分爲:道路:16,道:17,路:19。(注意中間加了18:路面)

 

{ "token": "路面", "start_offset": 11, "end_offset": 13, "type": "CN_WORD", "position": 18 }

 

而檢索的時候,而道路拆分爲: 道路0 道1 路2

match_phrase檢索時候,文檔必須同時知足如下兩個條件,才能被檢索到: 
1)分詞後全部詞項都出如今該字段中; 
2)字段中的詞項順序要一致。 
位置信息能夠被存儲在倒排索引中,所以 match_phrase 查詢這類對詞語位置敏感的查詢, 就能夠利用位置信息去匹配包含全部查詢詞項,且各詞項順序也與咱們搜索指定一致的文檔,中間不夾雜其餘詞項。

爲了驗證如上的解釋,新增一篇「道路」相關的title,檢驗一下:

POST ik_index/ik_type/4 { "title":"黨員幹部堅持走馬克思主義道路的重要性" }

注意:這時,搜索道路是能夠匹配到的。

"hits": { "total": 1, "max_score": 1.9684901, "hits": [ { "_index": "ik_index", "_type": "ik_type", "_id": "4", "_score": 1.9684901, "_source": { "title": "黨員幹部堅持走馬克思主義道路的重要性" } } ] },

細粒度ik_max_word分詞結果爲:

黨員幹部, 黨員, 幹部, 堅持走, 堅持, 堅, 持, 走馬, 馬克思主義, 馬克思,
馬克, 馬, 克, 思, 主義, 道路, 道, 路, 重要性, 重要,
要性, 性

 

構建索引的時候,道路被拆分爲:15,16,17位置。 
與檢索的詞項順序是一致的。 
這裏解析更詳細:http://t.cn/R8pzw9e

四、match_pharse都搜不出來,還有沒有別的方案?

有,和match_pharse相似,不過match_phrase_prefix支持最後一個term前綴匹配。 
除了把查詢文本的最後一個分詞只作前綴匹配以外,match_phrase_prefix和match_phrase查詢基本同樣,參數 max_expansions 控制最後一個單詞會被重寫成多少個前綴,也就是,控制前綴擴展成分詞的數量,默認值是50(官網文檔建議50)。 
擴展的前綴數量越多,找到的文檔數量就越多; 
若是前綴擴展的數量太少,可能查找不到相應的文檔,遺漏數據。

POST ik_index/ik_type/_search { "profile":"true", "query": { "match_phrase_prefix" : { "title.ik_my_max" : { "query": "道路", "max_expansions": 50 } } } }

 

經驗證: 關鍵詞」理解」、」解放」、」道路」、「理解放大」,都能搜出這篇文檔。

五、應用場景

咱們本身開發搜索引擎的時候,常常會出現基於title或者content字段進行檢索。 
若是用match檢索,會出現噪音不少的狀況; 
若是用match_phrase,會出現某些字段檢索不出來的狀況,如上分析的「道路」; 
若是用wildcard,能檢索出來,但又有性能問題的存在。 
這時候,能夠考慮下: match_phrase_prefix。

六、小結

實際開發中,根據應用場景不一樣,採用不一樣的分詞器。 
若是選用ik,建議使用ik_max_word分詞,由於:ik_max_word的分詞結果包含ik_smart。 
匹配的時候,若是想盡量的多檢索結果,考慮使用match; 
若是想盡量精確的匹配分詞結果,考慮使用match_phrase; 
若是短語匹配的時候,怕遺漏,考慮使用match_phrase_prefix。

相關文章
相關標籤/搜索