1. 程式人生 > >ElasticSearch term和match查詢機制解析和隱藏的查詢問題

ElasticSearch term和match查詢機制解析和隱藏的查詢問題

2. 關於預設分析使用term查詢的問題

  之前說過es的預設分析器會講中文拆分成一個個的單個漢子,搜尋條件“內科”會被分析為“內”和“科”,從而進行搜尋。而對於搜尋我們常用的match搜尋類似於資料庫的模糊查詢,term搜尋為精確查詢。使用的時候會出現以下情況:

2.1 場景

預設不對索引下的欄位進行mapping操作時,使用的是預設分析器,假設有如下資料內容:

內科
內一科
內二科
普通內科
腫瘤內科

我們使用match搜尋來搜尋“腫瘤”,得到的結果顯而易見是“腫瘤內科”,搜尋“腫瘤內科”,得到的不僅是“腫瘤內科”,“內一科”等另外幾個包含“內”、“科”的也會列出來,但是當我們使用term搜尋“腫瘤”時,我們得到的是空的結果,這也是顯而易見的,因為term是精確查詢,“腫瘤”和“腫瘤內科”是不同的。那麼如果我們term搜尋“腫瘤內科”,理論上應該出現“腫瘤內科”才符合我們的預期。但是:

{
    "query":{
        "term":{"name":"腫瘤內科"}
    }
}
<-----------    result    ----------->
{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 5,
        "successful": 5,
        "skipped": 0,
        "failed": 0
    },
    "hits": {
        "total": 0,
        "max_score": null,
        "hits": []
    }
}

得到的結果依然是空的,我們更換搜尋條件,輸入“內科”,得到的依然是空的。但是當我們輸入“腫”或者“腫瘤內科”的任意一個單字時,都可以的到“腫瘤內科”的結果,於此同時,“內”和“科”將會得到所有包含“內”和“科”字的資料。

2.2 分析原因

原因在上一節內容裡已經提及,預設的分析器會講中文分析稱單個字,不會有任何聯字,也就是說,4個字的“腫瘤內科”倒排索引是“腫“、”瘤“、”內“、”科”,使用term查詢時,必須是以上四個單字中的某一個才行。那麼這時候是不是和之前提到的搜尋條件也需要進行對應的分析,根據分析的倒排索引就行查詢相矛盾?這裡我們需要先明白match查詢和term查詢的機制。

2.2.1 match查詢的原理

  • 檢查欄位型別 。
    name 欄位是一個 string 型別( analyzed )已分析的全文欄位,這意味著查詢字串本身也應該被分析。這裡的“腫瘤內科”會被分析為“腫“、”瘤“、”內“、”科”
  • 分析查詢字串 。
    將查詢的字串 “腫瘤內科” 傳入標準分析器中,輸出的結果是單個項 。因為只有一個單詞項,所以 match 查詢執行的是單個底層 term 查詢。在這裡也就是會將“腫“、”瘤“、”內“、”科”四個單字分別進行term查詢
  • 查詢匹配文件 。
    用 term 查詢在倒排索引中查詢 “腫“、”瘤“、”內“、”科” 然後獲取一組包含該項的文件,本例的結果是文件:“內科”,“內一科”,“內二科”,“普通內科”,“腫瘤內科”,“內科”。
  • 為每個文件評分 。
    用 term 查詢計算每個文件相關度評分 _score ,這是種將 詞頻(term frequency,即 “腫“、”瘤“、”內“、”科” 在相關文件的 name 欄位中出現的頻率)和反向文件頻率(inverse document frequency,即詞 “腫“、”瘤“、”內“、”科” 在所有文件的 name 欄位中出現的頻率),以及欄位的長度(即欄位越短相關度越高)相結合的計算方式。

2.2.2 term查詢原理

首先需要注意的一點是,term查詢是非評分查詢,而match是評分查詢,其次,term查詢時,不會進行分詞,而match查詢前會跟去蓋子段已經配置好的分析器進行相應的分析。其查詢內部的操作為:

  • 查詢匹配文件.
    term 查詢在倒排索引中查詢 “腫瘤內科” 然後獲取包含該 term 的所有文件。本例中,顯然沒有符合的。

    切記index內的“腫瘤內科”並不是以直接的“腫瘤內科”存在,而是以“腫“、”瘤“、”內“、”科”四個單字存在

  • 建立 bitset.
    過濾器會建立一個 bitset (一個包含 0 和 1 的陣列),它描述了哪個文件會包含該 term 。匹配文件的標誌位是 1 。本例中,bitset 的值為 [0,0,0,0,0] 。在內部,它表示成一個 “roaring bitmap”,可以同時對稀疏或密集的集合進行高效編碼。
  • 迭代 bitset(s)
    一旦為每個查詢生成了 bitsets ,Elasticsearch 就會迴圈迭代 bitsets 從而找到滿足所有過濾條件的匹配文件的集合。執行順序是啟發式的,但一般來說先迭代稀疏的 bitset (因為它可以排除掉大量的文件)。
  • 增量使用計數
    Elasticsearch 能夠快取非評分查詢從而獲取更快的訪問,但是它也會不太聰明地快取一些使用極少的東西。非評分計算因為倒排索引已經足夠快了,所以我們只想快取那些我們 知道 在將來會被再次使用的查詢,以避免資源的浪費。
    所以這就解釋了為什麼使用term查詢“腫瘤內科”,得不到“腫瘤內科”的額,而使用單子卻能搜尋到的問題了。

2.3 解決方式

通過對預設分析器的解讀,其實無論是用預設的還是自行設定shingle等方式,對於漢子的term查詢,沒有一個標準的方式,使用shingle配置時,可以滿足term搜尋“腫瘤內科”得到“腫瘤內科”的結果,但是使用“瘤內科”,也會搜尋到“腫瘤內科”,同樣“內”也是,這取決於與在建立索引時的mapping中shingle最長度和最大長度的設定。所以,對於文字的搜尋,term查詢並非像mysql中的=一樣。但是對於非文字型別,如bool和數字型別,或者大於小於等比較操作,使用term會非常的精準。