1. 程式人生 > 其它 >elasticsearch中的請求體查詢詳解

elasticsearch中的請求體查詢詳解

空查詢

  1. 空查詢將會返回所有索引庫(indices)中的所有文件:
    GET /_search
  2. 只用一個查詢字串你就可以在一個或者多個索引庫中進行查詢
    GET /test,drivers/_search
  3. 同時可以使用from和size引數來分頁
    GET /_search
{
    "from": 1,
    "size": 1
}

相對於使用晦澀難懂的查詢字串的方式,一個帶請求體的查詢允許我們使用
查詢領域特定語言Query DSL來寫查詢語句

查詢表示式

  1. 查詢表示式Query DSL是一種非常靈活且富有表現力的查詢語言,es可以使用json介面來展現lucene的強大功能
    在應用中,應該用查詢表示式Query DSL來編寫查詢語句,他可以使你的查詢語句更靈活、更精確、易讀和易除錯
    要使用這樣查詢表示式,只需要將查詢語句傳遞給query引數
  2. 空查詢在功能上等價於match_all查詢
  • 空查詢
    GET /_search
  • match_all查詢
    GET /search
{
  "query": {
    "match_all": {}
  }
}
  1. 查詢語句的結構
    可以使用match查詢語句來查詢name欄位包含zhang的文件
    GET /_search
{
    "query": {
        "match": {
            "name": "zhang"
        }
    }
}
  1. 合併查詢請求
  • 篩選date域在大於2014-09-20,小於2014-09-24的文件
{
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "date": {
                            "gt": "2014-09-20"
                        }
                    }
                },
                {
                    "range": {
                        "date": {
                            "lt": "2014-09-24"
                        }
                    }
                }
            ]
        }
    }
}

查詢語句query clauses就像一些簡單的組合塊,這些組合塊可以彼此之間合併形成更復雜的查詢
語句形式如下;
葉子語句(leaf clauses), (就像match語句),被用於將查詢字串和一個欄位(或多個欄位)對比
複合(compound)語句主要用於合併其它查詢語句,比如一個bool語句,允許你在需要的時候組合其它語句
無論是must匹配,mast_not匹配還是should匹配,還有filter匹配
案例:查詢us索引庫中date域大於2014-09-14,tweet域包含elasticsearch,date域不等於2014-09-16,
date域等於2014-09-22

{
    "query": {
        "bool": {
            "must": [
                {
                    "range": {
                        "date": {
                            "gt": "2014-09-14"
                        }
                    }
                },
                {
                    "match": {
                        "tweet": "elasticsearch"
                    }
                }
            ],
            "must_not": {
                "term": {
                    "date": "2014-09-16"
                }
            },
            "filter": {
                "term": {
                    "date": "2014-09-22"
                }
            }
        }
    }
}

一條複合語句可以合併任何其它查詢語句,包括複合語句,瞭解這一點是很重要的
這就意味著,複合語句之間可以互相巢狀,可以表達非常複雜的邏輯

  • should可以進行or查詢
    查詢tweet域包含@mary或者date域大於2014-09-22
{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "tweet": "@mary"
                    }
                },
                {
                    "range": {
                        "date": {
                            "gt": "2014-09-22"
                        }
                    }
                }
            ]
        }
    }
}
  • should可以進行or查詢,在加上must聯合查詢呢
    查詢(tweet域包含"@mary"或者date域大於"2014-09-22") 並且date域等於"2014-09-20"
    GET /us/_search
{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "tweet": "@mary"
                    }
                },
                {
                    "range": {
                        "date": {
                            "gt": "2014-09-22"
                        }
                    }
                }
            ],
            "must": {
                "term": {
                    "date": "2014-09-20"
                }
            }
        }
    }
}

查詢結果,只查詢到了date域等於"2014-09-20"的文件,也就是說must查詢表示式起作用了,should沒有起作用
其實should在於must或者filter同級時,預設是不需要滿足should中的任何條件的,此時我們可以加上minimum_should_match引數
來達到我們的目的,程式碼改為

{
    "query": {
        "bool": {
            "should": [
                {
                    "match": {
                        "tweet": "@mary"
                    }
                },
                {
                    "range": {
                        "date": {
                            "gt": "2014-09-20"
                        }
                    }
                }
            ],
            "must": {
                "terms": {
                    "date": ["2014-09-24", "2014-09-22"]
                }
            },
            "minimum_should_match": 1
        }
    }
}

上述程式碼表示必須滿足must中的所有條件,並且至少滿足should中的一個條件,這樣就得到了預期的結果,
當然我們也可以使用另外一種方式實現,即將should放到must中的一個bool條件中,即用分層的方式讓should和must不同時出現

{
    "query": {
        "bool": {
            "must": [
                {
                    "terms": {
                        "date": ["2014-09-22", "2014-09-24"]
                    }
                },
                {
                    "bool": {
                        "should": [
                            {
                                "match": {
                                    "tweet": "@mary"
                                }
                            },
                            {
                                "range": {
                                    "date": {
                                        "gt": "2014-09-22"
                                    }
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}

上述兩種方法都可以解決should與must或者filter共存的問題,

  • 核心需要知道的是:
  • 一條複合語句可以將多條語句,葉子語句和其它複合語句,合併成一個單一的查詢語句

查詢與過濾

  1. es使用的查詢語言DSL擁有一套查詢元件,這些元件可以無限組合的方式進行搭配
    這套元件可以在一下兩種情況使用過濾情況filtering context, 查詢情況query context
  2. 當使用過濾情況時,查詢被設定成不評分或者過濾查詢,
  3. 當使用查詢情況下,查詢就被設定成了評分查詢,和不評分的查詢類似,也要去看這個文件是否匹配
    同時也要看這個文件匹配的有多好(匹配程度如何),查詢的典型用法是查詢一下文件
  • 查詢與full text context這個詞語最佳匹配的文件
  • 包含 run 這個詞,也能匹配 runs 、 running 、 jog 或者 sprint
  • 包含 quick 、 brown 和 fox 這幾個詞 — 詞之間離的越近,文件相關性越高
  • 標有 lucene 、 search 或者 java 標籤 — 標籤越多,相關性越高
  1. 一個評分查詢計算每一個文件與此查詢的相關程度,同時將這個相關程度分配給表示相關性的欄位_score
    並且按照相關性對匹配的文件進行排序,這種相關性的概念非常適合全文搜尋情況,因為全文搜尋幾乎沒有
    "完全"正確的答案
  2. es自問世以來,查詢與過濾(queries filters)就獨自成為了es的元件,但從2.0開始,過濾已經從技術上被排除了,
    同時所有的查詢(queries)擁有了變成不評分查詢的能力,然而為了明確和簡單,我們用filter這個詞表示不評分,
    只過濾情況下的查詢,filter和should聯合使用案例,需要配合minimum_should_match: 1
{
    "query": {
        "bool": {
            "filter": [
                {
                    "match": {
                        "tweet": "is"
                    }
                }
            ],
            "should": [
                {
                    "match": {
                        "name": "jone"
                    }
                },
                {
                    "match": {
                        "tweet": "elasticsearch"
                    }
                }
            ],
            "minimum_should_match": 1
        }
    }
}

filter只查詢不評分,而must和should即查詢又評分
當只使用filter查詢時你會發現,_score欄位是0.0,因為filter過濾查詢不評分
6. 效能差異
過濾查詢(filter queries)只是簡單的查詢包含或者排除,這就使得計算起來非常快,同時結果會被快取起來
評分查詢(scoring queries)不僅要找出匹配的文件,還要計算每個匹配文件的相關性,計算相關性使得它們比不評分查詢費力的多
同時,查詢結果並不快取
多虧倒排索引(inverted index),一個簡單的評分查詢在匹配少量的文件時可能與一個涵蓋百萬文件的filter表現的一樣好
甚至會更好,但是一般情況下, filter會比評分的query效能更好,並且每次的表現都很穩定

  • 過濾filter的目標是減少那些需要通過評分查詢進行檢查的文件
  1. 如何選擇查詢與過濾
    通常的規則是,使用查詢語句來進行全文搜尋,或者其它任何需要影響相關性得分的搜尋
    除此之外的情況都需要考慮用filter

最重要的查詢

  1. match_all查詢
    簡單的匹配所有文件,在沒有指定查詢方式時,它是預設的查詢
    GET /us/_search
{
    "query": {
        "match_all": {}
    }
}
  1. match查詢
    無論你在任何欄位上進行的全文搜尋還是精確查詢,match查詢是你可用的標準查詢
    如果你在一個全文欄位上使用match查詢,在執行查詢前,它將用正確的分析器去分析查詢字串,例如:
    GET /us/_search
{
    "query": {
        "match": {
            "tweet": "@mary API"
        }
    }
}

分析器會先分析查詢引數"@mary API"成兩個token詞條mary api,然後再去索引匹配

  • 如果在一個精確值的欄位上使用match, 例如數字、日期、布林或者一個keyword字串欄位,
    那麼它會精確匹配給定的值
{ "match": { "age":    26           }}
{ "match": { "date":   "2014-09-01" }}
{ "match": { "public": true         }}
{ "match": { "tag":    "full_text"  }}

對於精確值的查詢,我們需要使用filter過濾查詢,因為filter將會被快取

  1. multi_match查詢
    multi_match查詢可以在多個欄位上執行相同的match查詢
    GET /us/_search
{
    "query": {
        "multi_match": {
            "query": "mayanan API",
            "fields": ["name", "tweet"]
        }
    }
}
  1. range查詢
    range查詢找出那些落在指定區間內的數字或日期
    GET /us/_search
{
    "query": {
        "range": {
            "date": {
                "gt": "2014-09-16",
                "lt": "2014-09-20"
            }
        }
    }
}
  • 被允許的操作符如下:gt gte lt lte
  1. term查詢
    term查詢用於精確值匹配,這些精確值可能是數字、日期、布林或者keyword型別的字串
{
    "query": {
        "term": {
            "date": "2014-09-16"
        }
    }
}

term查詢對於輸入的文字不分析,所以它將給定的值進行精確查詢

  1. terms查詢
    terms查詢和term查詢一樣,但它允許你指定多值進行匹配,如果指定欄位中包含了指定值的任何一個值
    那麼這個文件就滿足條件
    GET /us/_search
{
    "query": {
        "terms": {
            "date": ["2014-09-16", "2014-09-22"]
        }
    }
}
  • 和term查詢一樣,terms查詢對於輸入的文字也不分析,它查詢那些精確匹配的值(包括在大小寫,重音,空格等方面的差異)
  1. exists查詢
    用於查詢那些指定欄位有值的文件,這與sql中的not is_null有共性
    GET /us/_search
{
    "query": {
        "exists": {
            "field": "username"
        }
    }
}

上面的查詢將會查詢username欄位有值的文件
該查詢經常用於查某個欄位有值的情況

組合多查詢

實際應用中,我們需要在多個欄位中查詢多種多樣的文字,並且根據一系列的標準來過濾,
為了構建類似的高階查詢,你需要一種將多查詢組合成單一查詢的查詢方法

  1. 你可以使用bool查詢來實現查詢需求,這種查詢將多查詢組合在一起,成為使用者自己想要的布林查詢
    它接收一下引數:
  • must: 文件必須匹配這些條件才能被包含進來,相當於mysql中的and
  • must_not: 文件必須不匹配這些條件才能被包含進來, 相當於mysql中的not and
  • should: 文件只要匹配這些條件中的任意一個,將會被包含進來, 相當於mysql中的or
  • filter: 必須匹配,但它不以評分模式,而是以過濾模式來進行,這些語句對評分沒有貢獻
    只是根據過濾標準來包含或者排除文件
    filter案例, 查詢date欄位等於2014-09-22的文件
{
    "query": {
        "bool": {
            "filter": {
                "term": {
                    "date": "2014-09-22"
                }
            }
        }
    }
}
  1. 由於這是第一個我們看到包含多個查詢的查詢,所以必須要討論一下相關性得分是如何組合的,
    每一個子查詢都獨自計算文件的相關性得分,一旦它們的得分被計算出來,bool查詢就將這些得分進行合併
    並且返回一個代表整個布林操作的得分,經典案例:
{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ]
    }
}

用於查詢title欄位匹配how to make millions, 並且tag欄位不為spam的文件,
如果tag欄位等於starred或者date>=2014-01-01,將比另外那些文件擁有更高的排名,
如果should中兩者都滿足,他麼它的排名將更高
注意:如果沒有must語句,那麼至少要匹配其中的一條should語句,但是如果存在至少一條must語句
則對should語句的匹配沒有要求

  • 還有一個注意事項,如果must_not和should都使用,那麼兩條查詢語句都需要匹配
    GET /us/_search
{
    "query": {
        "bool": {
            "must_not": [
                {
                    "match": {
                        "tweet": "elasticsearch"
                    }
                }
            ],
            "should": [
                {
                    "term": {
                        "date": "2014-09-24"
                    }
                }
            ]
        }
    }
}
  1. 增加帶過濾器filtering的查詢
    如果我們不想因為文件的date時間而影響得分,可以使用filter語句來重寫前面的例子
{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
            "range": { "date": { "gte": "2014-01-01"}}
        }
    }
}

range查詢已經從should語句中移動到filter語句中
通過將range查詢從should語句中移動到了filter語句中,我們將它轉成了不評分的查詢,
將不在影響文件的相關性排名,由於它是一個不評分的查詢,可以使用各種filter查詢有效的優化手段來提升效能

所有的查詢都可以借鑑這種方式,將查詢移動到bool的filter語句中,那麼他就自動轉成了不評分的filter了

如果你需要通過多個不同的標準來過濾文件,bool查詢本身亦可以用作不評分的查詢,簡單的將它放到filter語句內部
構建布林邏輯

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }}
        ],
        "filter": {
          "bool": { 
              "must": [
                  { "range": { "date": { "gte": "2014-01-01" }}},
                  { "range": { "price": { "lte": 29.99 }}}
              ],
              "must_not": [
                  { "term": { "category": "ebooks" }}
              ]
          }
        }
    }
}

將布林查詢包裹在filter語句中,我們可以在過濾標準中增加布爾邏輯

  • 通過混合布林查詢,我們可以在我們的查詢請求中靈活的編寫scoring和filtering查詢邏輯
    案例:
    GET /us/_search
    查詢tweet欄位等於elasticsearch的文件(參與評分和匹配),
    而且查詢date欄位不等2014-09-20的文件而且date欄位大於I=2014-09-16的文件(參與匹配,不參與評分)
{
    "query": {
        "bool": {
            "must": [
                {
                    "match": {
                        "tweet": "elasticsearch"
                    }
                }
            ],
            "filter": {
                "bool": {
                    "must_not": {
                        "term": {
                            "date": "2014-09-20"
                        }
                    },
                    "must": {
                        "range": {
                            "date": {
                                "gt": "2014-09-16"
                            }
                        }
                    }
                }
            }
        }
    }
}
  1. constant_score查詢
    它被經常用於你只執行一個filter而沒有其它查詢,可以使用它來取代只有filter語句的bool查詢,
    在效能上是完全相同的,但是對於查詢簡潔性和清晰度有很大幫助
{
    "query": {
        "constant_score": {
            "filter": {
                "terms": {
                    "date": ["2014-09-16", "2014-09-22"]
                }
            }
        }
    }
}
  • constant_score可以代替只有filter的bool查詢

驗證查詢

  1. 查詢可以變得非常複雜,尤其和不同的分析器與不同的欄位對映結合時,理解起來就有點困難了
    不過validate-query API可以用來驗證查詢是否合法
    GET /us/_validate/query
{
    "query": {
        "constant_score": {
            "filter": {
                "terms": {
                    "date": ["2014-09-16", "2014-09-22"]
                }
            }
        }
    }
}

如果合法,valid返回true, 否則返回false

  1. 理解錯誤資訊
    為了找出不合法的原因,可以將explain引數加到查詢字串中
    GET /us/_validate/query?explain
{
    "query": {
        "constant_score": {
            "filter": {
                "must": {
                    "date": ["2014-09-16", "2014-09-22"]
                }
            }
        }
    }
}

返回錯誤原因,因為must應該寫成match

{
    "valid": false,
    "error": "org.elasticsearch.common.ParsingException: Failed to parse; java.lang.IllegalStateException: Can't get text on a START_ARRAY at 6:29"
}

explain引數可以提供更多關於查詢不合法的原因

  1. 理解查詢語句
    對於合法查詢,explain將會返回可讀的描述,這對準確理解es是如何解析你的query是非常有用的

參考文件