Elasticsearch DSL 常用語法介紹


課程環境


DSL 介紹

  • 這個才是實際最常用的方式,可以構建復雜的查詢條件。
  • 不用一開始就想着怎樣用 Java Client 端去調用 Elasticsearch 接口。DSL 會了,Client 的也只是用法問題而已。

DSL 語句的校驗以及 score 計算原理

  • 對於復雜的查詢,最好都先校驗下,看有沒有報錯。
GET /product_index/product/_validate/query?explain
{
  "query": { "match": { "product_name": "toothbrush" } } } 

DSL 簡單用法

  • 查詢所有的商品:
GET /product_index/product/_search
{
  "query": { "match_all": {} } } 
  • 查詢商品名稱包含 toothbrush 的商品,同時按照價格降序排序:
GET /product_index/product/_search
{
  "query": { "match": { "product_name": "toothbrush" } }, "sort": [ { "price": "desc" } ] } 
  • 分頁查詢商品:
GET /product_index/product/_search
{
  "query": { "match_all": {} }, "from": 0, ## 從第幾個商品開始查,最開始是 0 "size": 1 ## 要查幾個結果 } 
  • 指定查詢結果字段(field)
GET /product_index/product/_search
{
  "query": { "match_all": {} }, "_source": [ "product_name", "price" ] } 
符號標識 代表含義
gte 大於或等於
gt 大於
lte 小於或等於
lt 小於
  • 搜索商品名稱包含 toothbrush,而且售價大於 400 元,小於 700 的商品
GET /product_index/product/_search
{
  "query": { "bool": { "must": { "match": { "product_name": "toothbrush" } }, "filter": { "range": { "price": { "gt": 400, "lt": 700 } } } } } } 
  • full-text search 全文檢索,倒排索引
  • 索引中只要有任意一個匹配拆分后詞就可以出現在結果中,只是匹配度越高的排越前面
  • 比如查詢:PHILIPS toothbrush,會被拆分成兩個單詞:PHILIPS 和 toothbrush。只要索引中 product_name 中只要含有任意對應單詞,都會在搜索結果中,只是如果有數據同時含有這兩個單詞,則排序在前面。
GET /product_index/product/_search
{
  "query": { "match": { "product_name": "PHILIPS toothbrush" } } } 
  • phrase search 短語搜索
  • 索引中必須同時匹配拆分后詞就可以出現在結果中
  • 比如查詢:PHILIPS toothbrush,會被拆分成兩個單詞:PHILIPS 和 toothbrush。索引中必須有同時有這兩個單詞的才會在結果中。
GET /product_index/product/_search
{
  "query": { "match_phrase": { "product_name": "PHILIPS toothbrush" } } } 
  • Highlight Search 高亮搜索
  • 給匹配拆分后的查詢詞增加高亮的 html 標簽,比如這樣的結果:"<em>PHILIPS</em> <em>toothbrush</em> HX6730/02"
GET /product_index/product/_search
{
  "query": { "match": { "product_name": "PHILIPS toothbrush" } }, "highlight": { "fields": { "product_name": {} } } } 
  • range 用法,查詢數值、時間區間:
GET /product_index/product/_search
{
  "query": { "range": { "price": { "gte": 30.00 } } } } 
  • match 用法(與 term 進行對比):
  • 查詢的字段內容是進行分詞處理的,只要分詞的單詞結果中,在數據中有滿足任意的分詞結果都會被查詢出來
GET /product_index/product/_search
{
  "query": { "match": { "product_name": "PHILIPS toothbrush" } } } 
  • match 還有一種情況,就是必須滿足分詞結果中所有的詞,而不是像上面,任意一個就可以的。(這個常見,所以很重要
  • 看下面的 JSON 其實你也可以猜出來,其實上面的 JSON 和下面的 JSON 本質是:operator 的差別,上面是 or,下面是 and 關系。
GET /product_index/product/_search
{
  "query": { "match": { "product_name": { "query": "PHILIPS toothbrush", "operator": "and" } } } } 
  • match 還還有一種情況,就是必須滿足分詞結果中百分比的詞,比如搜索詞被分成這樣子:java 程序員 書 推薦,這里就有 4 個詞,假如要求 50% 命中其中兩個詞就返回,我們可以這樣:
  • 當然,這種需求也可以用 must、must_not、should 匹配同一個字段進行組合來查詢
GET /product_index/product/_search
{
  "query": { "match": { "product_name": { "query": "java 程序員 書 推薦", "minimum_should_match": "50%" } } } } 
  • multi_match 用法:
  • 查詢 product_name 和 product_desc 字段中,只要有:toothbrush 關鍵字的就查詢出來。
GET /product_index/product/_search
{
  "query": { "multi_match": { "query": "toothbrush", "fields": [ "product_name", "product_desc" ] } } } 
  • multi_match 跨多個 field 查詢,表示查詢分詞必須出現在相同字段中。
GET /product_index/product/_search
{
  "query": { "multi_match": { "query": "PHILIPS toothbrush", "type": "cross_fields", "operator": "and", "fields": [ "product_name", "product_desc" ] } } } 
  • match_phrase 用法(短語搜索)(與 match 進行對比):
  • 對這個查詢詞不進行分詞,必須完全匹配查詢詞才可以作為結果顯示。
GET /product_index/product/_search
{
  "query": { "match_phrase": { "product_name": "PHILIPS toothbrush" } } } 
  • match_phrase + slop(與 match_phrase 進行對比):
  • 在說 slop 的用法之前,需要先說明原數據是:PHILIPS toothbrush HX6730/02,被分詞后至少有:PHILIPS,toothbrush,HX6730 三個 term。
  • match_phrase 的用法我們上面說了,按理說查詢的詞必須完全匹配才能查詢到,PHILIPS HX6730 很明顯是不完全匹配的。
  • 但是有時候我們就是要這種不完全匹配,只要求他們盡可能靠譜,中間有幾個單詞是沒啥問題的,那就可以用到 slop。slop = 2 表示中間如果間隔 2 個單詞以內也算是匹配的結果()。
  • 其實也不能稱作間隔,應該說是移位,查詢的關鍵字分詞后移動多少位可以跟 doc 內容匹配,移動的次數就是 slop。所以 HX6730 PHILIPS 其實也是可以匹配到 doc 的,只是 slop = 5 才行。
GET /product_index/product/_search
{
  "query": { "match_phrase": { "product_name" : { "query" : "PHILIPS HX6730", "slop" : 1 } } } } 
  • match + match_phrase + slop 組合查詢,使查詢結果更加精准和結果更多
  • 但是 match_phrase 性能沒有 match 好,所以一般需要先用 match 第一步進行過濾,然后在用 match_phrase 進行進一步匹配,並且重新打分,這里又用到了:rescore,window_size 表示對前 10 個進行重新打分
  • 下面第一個是未重新打分的,第二個是重新打分的
GET /product_index/product/_search
{
  "query": { "bool": { "must": { "match": { "product_name": { "query": "PHILIPS HX6730" } } }, "should": { "match_phrase": { "product_name": { "query": "PHILIPS HX6730", "slop": 10 } } } } } } GET /product_index/product/_search { "query": { "match": { "product_name": "PHILIPS HX6730" } }, "rescore": { "window_size": 10, "query": { "rescore_query": { "match_phrase": { "product_name": { "query": "PHILIPS HX6730", "slop": 10 } } } } } } 
  • match_phrase_prefix 用法(不常用),一般用於類似 Google 搜索框,關鍵字輸入推薦
  • max_expansions 用來限定最多匹配多少個 term,優化性能
  • 但是總體來說性能還是很差,因為還是會掃描整個倒排索引。推薦用 edge_ngram 做該功能
GET /product_index/product/_search
{
  "query": { "match_phrase_prefix": { "product_name": "PHILIPS HX", "slop": 5, "max_expansions": 20 } } } 
  • term 用法(與 match 進行對比)(term 一般用在不分詞字段上的,因為它是完全匹配查詢,如果要查詢的字段是分詞字段就會被拆分成各種分詞結果,和完全查詢的內容就對應不上了。):
  • 所以自己設置 mapping 的時候有些不分詞的時候就最好設置不分詞。
  • 其實 Elasticsearch 5.X 之后給 text 類型的分詞字段,又默認新增了一個子字段 keyword,這個字段的類型就是 keyword,是不分詞的,默認保留 256 個字符。假設 product_name 是分詞字段,那有一個 product_name.keyword 是不分詞的字段,也可以用這個子字段來做完全匹配查詢。
GET /product_index/product/_search
{
  "query": { "term": { "product_name": "PHILIPS toothbrush" } } } GET /product_index/product/_search { "query": { "constant_score": { "filter":{ "term": { "product_name": "PHILIPS toothbrush" } } } } } 
  • terms 用法,類似於數據庫的 in
GET /product_index/product/_search
{
  "query": { "constant_score": { "filter": { "terms": { "product_name": [ "toothbrush", "shell" ] } } } } } 

query 和 filter 差異

  • 只用 query:
GET /product_index/_search
{
  "query": { "bool": { "must": [ { "terms": { "product_name": [ "PHILIPS", "toothbrush" ] } }, { "range": { "price": { "gt": 12.00 } } } ] } } } 
GET /product_index/product/_search
{
  "query": { "constant_score": { "filter": { "range": { "price": { "gte": 30.00 } } } } } } 

多搜索條件組合查詢(最常用)

  • bool 下包括:must(必須匹配,類似於數據庫的 =),must_not(必須不匹配,類似於數據庫的 !=),should(沒有強制匹配,類似於數據庫的 or),filter(過濾)
GET /product_index/product/_search
{
  "query": { "bool": { "must": [ { "match": { "product_name": "PHILIPS toothbrush" } } ], "should": [ { "match": { "product_desc": "刷頭" } } ], "must_not": [ { "match": { "product_name": "HX6730" } } ], "filter": { "range": { "price": { "gte": 33.00 } } } } } } GET /product_index/product/_search { "query": { "bool": { "should": [ { "term": { "product_name": "飛利浦" } }, { "bool": { "must": [ { "term": { "product_desc": "刷頭" }, "term": { "price": 30 } } ] } } ] } } } 
  • should 有一個特殊性,如果組合查詢中沒有 must 條件,那么 should 中必須至少匹配一個。我們也還可以通過 minimum_should_match 來限制它匹配更多個。
GET /product_index/product/_search
{
  "query": { "bool": { "should": [ { "match": { "product_name": "java" } }, { "match": { "product_name": "程序員" } }, { "match": { "product_name": "書" } }, { "match": { "product_name": "推薦" } } ], "minimum_should_match": 3 } } } 
  • 下面還用到自定義排序。
  • 排序最好別用到字符串字段上。因為字符串字段會進行分詞,Elasticsearch 默認是拿分詞后的某個詞去進行排序,排序結果往往跟我們想象的不一樣。解決這個辦法是在設置 mapping 的時候,多個這個字段設置一個 fields raw,讓這個不進行分詞,然后查詢排序的時候使用這個 raw,具體看這里:https://www.elastic.co/guide/cn/elasticsearch/guide/current/multi-fields.html
GET /product_index/product/_search
{
  "query": { "bool": { "must": [ { "match": { "product_name": "PHILIPS toothbrush" } } ], "should": [ { "match": { "product_desc": "刷頭" } } ], "filter": { "bool": { "must": [ { "range": { "price": { "gte": 33.00 } } }, { "range": { "price": { "lte": 555.55 } } } ], "must_not": [ { "term": { "product_name": "HX6730" } } ] } } } }, "sort": [ { "price": { "order": "desc" } } ] } 
  • boost 用法(默認是 1)。在搜索精准度的控制上,還有一種需求,比如搜索:PHILIPS toothbrush,要比:Braun toothbrush 更加優先,我們可以這樣:
GET /product_index/product/_search
{
  "query": { "bool": { "must": [ { "match": { "product_name": "toothbrush" } } ], "should": [ { "match": { "product_name": { "query": "PHILIPS", "boost": 4 } } }, { "match": { "product_name": { "query": "Braun", "boost": 3 } } } ] } } } 
  • dis_max 用法,也稱作:best fields 策略。
  • 由於查詢關鍵字是會被分詞的,默認 query bool 查詢多個字段的語法時候,每個字段匹配到一個或多個的時候分數比:一個字段匹配到查詢分詞的所有結果的分數來的大。但是對於我們來講這樣的不夠精准的。所以我們希望查詢字段中,匹配的關鍵字越多排序越靠前,而不是每個字段查詢了一個分詞就排前,我們可以使用 dis_max。
  • 但是使用 dis_max,一般還不夠,建議再加上 tie_breaker。
  • tie_breaker 是一個小數值,在 0~1 之間用來將其他查詢結果分數,乘以 tie_breaker 的值,然后再綜合與 dis_max 最高分數的的分數一起進行計算。除了取 dis_max 的最高分以外,還會考慮其他的查詢結果的分數。
  • 在 dis_max 基礎上,為了增加精准,我們還可以加上:boost、minimum_should_match 等相關參數。其中 minimum_should_match 比較常用,因為查詢字段的分詞中如果只有一個分詞查詢上了這種結果基本是沒啥用的。
  • 官網資料:https://www.elastic.co/guide/en/elasticsearch/guide/current/_best_fields.html
GET /product_index/product/_search
{
  "query": { "dis_max": { "queries": [ { "match": { "product_name": "PHILIPS toothbrush" } }, { "match": { "product_desc": "iphone shell" } } ], "tie_breaker": 0.2 } } } GET /product_index/product/_search { "query": { "dis_max": { "queries": [ { "match": { "product_name": { "query": "PHILIPS toothbrush", "minimum_should_match": "50%", "boost": 3 } } }, { "match": { "product_desc": { "query": "iphone shell", "minimum_should_match": "50%", "boost": 2 } } } ], "tie_breaker": 0.3 } } } 
  • prefix 前綴搜索(性能較差,掃描所有倒排索引)
  • 比如有一個不分詞字段 product_name,分別有兩個 doc 是:iphone-6,iphone-7。我們搜索 iphone 這個前綴關鍵字就可以搜索到結果
GET /product_index/product/_search
{
  "query": { "prefix": { "product_name": { "value": "iphone" } } } } 
  • 通配符搜索(性能較差,掃描所有倒排索引)
GET /product_index/product/_search
{
  "query": { "wildcard": { "product_name": { "value": "ipho*" } } } } 
  • 正則搜索(性能較差,掃描所有倒排索引)
GET /product_index/product/_search
{
  "query": { "regexp": { "product_name": "iphone[0-9].+" } } } 
  • fuzzy 糾錯查詢
  • 參數 fuzziness 默認是 2,表示最多可以糾錯兩次,但是這個值不能很大,不然沒效果。一般 AUTO 是自動糾錯。
  • 下面的關鍵字漏了一個字母 o。
GET /product_index/product/_search
{
  "query": { "match": { "product_name": { "query": "PHILIPS tothbrush", "fuzziness": "AUTO", "operator": "and" } } } } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM