Elasticsearch Query DSL 整理總結(二)—— 要搞懂 Match Query,看這篇就夠了

引言

昨天是感恩節,上幼兒園的女兒在老師的叮囑下,晚上爲我和老婆洗了腳(形式上的^_^),還給咱們每人端了一杯水。看着孩子一每天的長大,懂事,感受很開心,話說我們程序員這麼辛苦是爲了什麼?不就是爲了老婆,孩子,熱炕頭,有一個溫暖幸福的家庭,再捎帶着用代碼改變一下世界嗎?想到這裏,頓時以爲學習,創做博客的勁頭也的更足了。哈哈,扯遠了,書歸正傳,今天咱們來聊聊 Match Query。java

Match Query 是最經常使用的 Full Text Query 。不管須要查詢什麼字段, match 查詢都應該會是首選的查詢方式。它既能處理全文字段,又能處理精確字段。程序員

構建示例

爲了可以在後面能深刻理解 Match Query 中的各個屬性的意義,咱們先構建一個 index 示例(有興趣的同窗只要將下面字段粘貼到 sense 中就能夠建立)。json

PUT matchtest
{ 
}

PUT matchtest/_mapping/people
{
  "properties": {
    "age": {
      "type": "integer"
    },
    "hobbies": {
      "type": "text"
    },
    "name": {
      "type": "keyword"
    }
  }
}

PUT matchtest/people/1
{
  "name" : "Jim",
  "age": 10,
  "hobbies": "football, basketball, pingpang"
}


PUT matchtest/people/2
{
  "name" : "Tom",
  "age": 12,
  "hobbies": "swimming, football"
}

match

operator 參數

match 查詢是一種 bool 類型的查詢。什麼意思呢?舉個例子,查詢 people type 的 hobbies 爲 football basketballapi

GET matchtest/people/_search
{
  "query": {
    "match": {
      "hobbies": "football basketball"
    }
  }
}

會將上面的兩個文檔都搜索出來。爲何?上面的查詢其實隱藏了一個默認參數operator , 它的默認值是 or ,也就是說上面的查詢也能夠寫成這種形式app

GET matchtest/people/_search
{
  "query": {
    "match": {
      "hobbies": {
        "query": "football basketball",
        "operator": "or"
      }
    }
  }
}

這樣就比較容易理解了,既然是 or 操做符,就表示只要查詢的文檔的 hobbies 字段中含有 footballbasketball 任意一個,就能夠被匹配到。elasticsearch

若是將 operator 操做符的值改成 and ,則表示須要同時包含 footballbasketball , 獲得的結果就只能是 文檔 1 Jim 小朋友了。ide

analyzer

analyzer 屬性是指在對查詢文本分析時的分析器學習

  • 若是沒有指定則會使用字段mapping 時指定的分析器
  • 若是字段在 mapping 時也沒有明顯指定,則會使用默認的 search analyzer。

這裏咱們也沒有指定,就會使用默認的,就不舉例了,在後面文章講解 analyzer 時再拓展。優化

lenient 參數

默認值是 false , 表示用來在查詢時若是數據類型不匹配且沒法轉換時會報錯。若是設置成 true 會忽略錯誤。

例如, 例子中的 ageinteger 類型的,若是查詢 age=xxy ,就會致使沒法轉換而報錯。

GET matchtest/_search
{
  "query": {
    "match": {
      "age" : {
        "query": "xxx"
      }
    }
  }
}

而若是將 lenient 參數設置爲 true ,就會忽略這個錯誤

GET matchtest/_search
{
  "query": {
    "match": {
      "age" : {
        "query": "xxx",
        "lenient": true
      }
    }
  }
}

注意,若是將 age 字段的值設置爲字符串 "10", 來查詢,因爲可以轉換成整數,這時 elastic 內部會將 字符串先轉換成整數再作查詢,不會報錯。

Fuzziness

fuzzniess 參數

fuzziness 參數可使查詢的字段具備模糊搜索的特性。來先了解下什麼是模糊搜索。

什麼是模糊搜索?

模糊搜索是指系統容許被搜索信息和搜索提問之間存在必定的差別,這種差別就是「模糊」在搜索中的含義。例如,查找名字Smith時,就會找出與之類似的Smithe, Smythe, Smyth, Smitt等。

——百度百科

經過模糊搜索能夠查詢出存在必定類似度的單詞,那麼怎麼計算兩個單詞是否有類似度以及類似度的大小呢?這就要了解下另一個概念:Levenshtein Edit Distance

Levenshtein Edit Distance

Levenshtein Edit Distance 叫作萊文斯坦距離**,是編輯距離的一種。指兩個字串之間,由一個轉成另外一個所需的最少編輯操做次數。容許的編輯操做包括將一個字符替換成另外一個字符,插入一個字符,刪除一個字符。

例如,單詞 "god" 只須要插入一個 'o' 字符就能夠變爲 "good",所以它們之間的編輯距離爲 1。

fuzziness 參數取值規則

瞭解了上面兩個概念,回過頭再來看下 fuzziness 參數。

在查詢 text 或者 keyword 類型的字段時, fuzziness 能夠看作是萊文斯坦距離。

fuzziness 參數的取值以下

  • 0,1,2 表示最大可容許的萊文斯坦距離

  • AUTO

    會根據詞項的長度來產生可編輯距離,它還有兩個可選參數,形式爲AUTO:[low],[high], 分別表示短距離參數和長距離參數;若是沒有指定,默認值是 AUTO:3,6 表示的意義以下

    • 0..2

      單詞長度爲 0 到 2 之間時必需要精確匹配,這其實很好理解,單詞長度過短是沒有類似度可言的,例如 'a' 和 'b'。

    • 3..5

      單詞長度 3 到 5 個字母時,最大編輯距離爲 1

    • >5

      單詞長度大於 5 個字母時,最大編輯距離爲 2

    最佳實踐: fuzziness 在絕大多數場合都應該設置成 AUTO

若是不設置 fuziness 參數,查詢是精確匹配的。

來看例子,上面建立了一個 doc

PUT matchtest/people/1
{
  "name" : "Jim",
  "age": 10,
  "hobbies": "football, basketball, pingpang"
}

設置 fuzzinessAUTO

  • 其中 hobbies 字段的值 football 長度 > 5, 此時咱們找一個編輯距離爲 2 的單詞 footba22 來查詢,應該匹配到
  • 其中 name 字段的值 jim 長度在 3 和 5 之間,此時找一個編輯距離爲 1 的單詞 jiO 應匹配到,而編輯距離爲 2 的 jOO 就不該匹配到。

來,驗證下

GET matchtest/_search
{
  "query": {
    "match": {
      "hobbies": {
        "query": "footba22",
        "fuzziness": "AUTO"
      }
    }
  }
}

GET matchtest/_search
{
  "query": {
    "match": {
      "name": {
        "query": "jiO",
        "fuzziness": "AUTO"
      }
    }
  }
}


GET matchtest/_search
{
  "query": {
    "match": {
      "name": {
        "query": "jOO",
        "fuzziness": "AUTO"
      }
    }
  }
}

prefix_length

prefix_length 表示不能沒模糊化的初始字符數。因爲大部分的拼寫錯誤發生在詞的結尾,而不是詞的開始,使用 prefix_length 就能夠完成優化。注意 prefix_length 必須結合 fuzziness 參數使用。

例如,在查詢 hobbies 中的 football 時,將 prefix_length 參數設置爲 3,這時 foatball 將不能被匹配。

GET matchtest/_search
{
  "query": {
    "match": {
      "hobbies": {
        "query": "foatball",
        "fuzziness": "AUTO",
        "prefix_length": 3
      }
    }
  }
}

TODO(max_expansions 參數對於 match 查詢而言,沒理解表示的意義,若是您知道這個參數的用法,請給我留言告知,不勝感謝! )

Zero terms Query

先看例子, 先建立一個文檔 zero_terms_query_test 其中 message 字段使用 stop 分析器,這個分析器會將 stop words 停用詞在索引時全都去掉。

PUT matchtest1

PUT matchtest1/_mapping/zero_terms_query_test
{
  "properties": {
    "message": {
      "type": "text",
      "analyzer": "stop"
    }
  }
}


PUT matchtest1/zero_terms_query_test/1
{
  "message": "to be or not to be"
}

GET matchtest1/_search
{
  "query": {
    "match": {
      "message": {
        "query": "to be or not to be",
        "operator": "and",
        "zero_terms_query": "none"
      }
    }
  }
}

那麼就像 message 字段中的 to be or not to be 這個短語中所有都是中止詞,一過濾,就什麼也沒有了,得不到任何 tokens, 那搜索時豈不什麼都搜不到。

POST _analyze
{
  "analyzer": "stop",
  "text": "to be or not to be"
}

zero_terms_query 就是爲了解決這個問題而生的。它的默認值是 none ,就是搜不到中止詞(對 stop 分析器字段而言),若是設置成 all ,它的效果就和 match_all 相似,就能夠搜到了。

GET matchtest1/_search
{
  "query": {
    "match": {
      "message": {
        "query": "to be or not to be",
        "operator": "and",
        "zero_terms_query": "all"
      }
    }
  }
}

Cutoff frequency

查詢字符串時的詞項會分紅低頻詞(更重要)和高頻詞(次重要)兩類,像前面所說的停用詞 (stop word)就屬於高頻詞,它雖然出現頻率較高,但在匹配時可能並不太相關。實際上,咱們每每是想要文檔能儘量的匹配那些低頻詞,也就是更重要的詞項

要實現這個需求,只要在查詢時配置 cutoff_frequency 參數就能夠了。假設咱們將 cutoff_frequency 設置成 0.01 就表示

  • 任何詞項在文檔中超過 1%, 被認爲是高頻詞
  • 其餘的詞項會被認爲低頻詞

從而將高頻詞(次重要的詞)挪到可選子查詢中,讓它們只參與評分,而不參與匹配;高頻詞(更重要的詞)參與匹配和評分。

這樣一來,就再也不須要 stopwords 停用詞文件了,從而變成了動態生成停用詞: 高頻詞就會被看作是停用詞。這種配置只是對於詞項比較多的場合如 email body,文章等適用,文字太少, cutoff_frequency 選項設置的意義就不大了。

cutoff_frequency 配置有兩種形式

  • 指定爲一個分數( 0.01 )表示出現頻率
  • 指定爲一個正整數( 5 )則表示出現次數

下面給個例子, 在建立的 3 個文檔中都包含 "be " 的單詞,在查詢時將 cutoff_frequency 參數設置爲 2, 表示 "be" 就是高頻詞,只會參與評分,但在匹配時不作考慮。

此時查詢的內容爲 "to be key" ,因爲 "be" 詞項是高頻詞,由於在文檔中必需要存在 "to" 或者 "key" 才能匹配,所以文檔 3 不能匹配。

PUT /matchtest2

PUT matchtest2/_mapping/cutoff_frequency_test
{
  "properties": {
    "message": {
      "type": "text"
    }
  }
}

PUT matchtest2/cutoff_frequency_test/1
{
  "message": "to be or not to be to be or"
}

PUT matchtest2/cutoff_frequency_test/2
{
  "message": "be key or abc"
}

PUT matchtest2/cutoff_frequency_test/3
{
  "message": "or to be or to to be or"
}

GET matchtest2/_search
{
  "query": {
    "match": {
      "message": {
        "query": "to be key",
        "cutoff_frequency": 2
      }
    }
  }
}

synonyms

synonyms 是指同義詞,只要索引和字段中配置了同義詞過濾器,match 查詢是支持多詞條的同義詞擴展的。在應用過濾器後,解析器會對每一個屢次條同義詞建立一個語句查詢。

例如,同義詞 USA, united states of America 就會構建出 (USA OR ("united states of America"))。看下面例子:

PUT /matchtest4
{
    "settings": {
        "index" : {
            "analysis" : {
                "analyzer" : {
                    "synonym" : {
                        "tokenizer" : "whitespace",
                        "filter" : ["synonym"]
                    }
                },
                "filter" : {
                    "synonym" : {
                        "type" : "synonym",
                        "synonyms" : [
                            "USA, united states of America"
                        ]
                    }
                }
            }
        }
    }
}

PUT /matchtest4/_mapping/synonyms_test
{
  "properties": {
    "message": {
      "type": "text",
      "analyzer": "synonym"
    }
  }
}

PUT /matchtest4/synonyms_test/1
{
  "message": "united states of America people"
}


GET /matchtest4/_search
{
  "query": {
    "match": {
      "message": {
        "query": "USA"
      }
    }
  }
}

小結

本文以代碼實例的方式完整的講解了 Match Query 的各類使用場景和參數意義。下篇會講解 Match Phrase Query 敬請期待。

參考文檔

系列文章列表

Query DSL

  1. Query DSL 概要,MatchAllQuery,全文查詢簡述

Java Rest Client API

  1. Elasticsearch Java Rest Client API 整理總結 (一)——Document API
  2. Elasticsearch Java Rest Client API 整理總結 (二) —— SearchAPI
  3. Elasticsearch Java Rest Client API 整理總結 (三)——Building Queries
相關文章
相關標籤/搜索