本篇介紹Query DSL的語法案例,查詢語句的調試,以及排序的相關內容。java
最簡單的搜索命令,不指定索引和類型的空搜索,它將返回集羣下全部索引的全部文檔(默認顯示10條):mysql
GET /_search {}
GET /index1,index2/_doc/_search {}
GET /_search { "from": 0, "size": 10 }
HTTP協議,GET請求帶body是不規範的作法,但因爲ES搜索的複雜性,加上HTTP協議GET/POST方法表述的語義,GET更適合用來表述查詢的動做,雖然不規範,但仍是這麼用了。如今大多數瀏覽器也支持GET+request body,若是遇到不支持的,換成POST便可。瞭解一下就行,不用太慌張。sql
Query DSL是一種很是靈活、可讀性高的查詢語言,body爲JSON格式,絕大部分功能均可以用它來展示,而且這種查詢語句更純粹,讓學習者更專一於自己的功能,避免Client API的干擾。瀏覽器
上一節的空查詢,等價於這個:緩存
GET /_search { "query": { "match_all": {} } }
# 查詢語句結構 { QUERY_NAME: { ARGUMENT: VALUE, ARGUMENT: VALUE,... } } # 針對某個字段的查詢 { QUERY_NAME: { FIELD_NAME: { ARGUMENT: VALUE, ARGUMENT: VALUE,... } } }
再複雜的查詢語句,也是由一個一個的查詢條件疊加而成的,查詢語句有兩種形式:架構
舉個例子:併發
{ "bool": { "must": { "match": { "status": 1 }}, "must_not": { "match": { "language": "french" }}, "should": { "match": { "author": "John Tom" }}, "filter": { "range": { "length" : { "gt" : 30 }} } } }
複合語句能夠嵌套,來實現更復雜的查詢需求,在上面的例子上簡單延伸一下:app
"bool": { "must": { "match": { "status": 1 }}, "must_not": { "match": { "language": "french" }}, "should": [ {"match": { "author": "John Tom" }}, {"bool": { "must": { "match": { "name": "friend" }}, "must_not": { "match": { "content": "star" }} }} ], "filter": { "range": { "length" : { "gt" : 30 }} } }
每個子查詢都獨自地計算文檔的相關性得分。一旦他們的得分被計算出來,bool 查詢就將這些得分進行合併而且返回一個表明整個布爾操做的得分,得分高的顯示在前面,filter內的條件不參與分數計算。elasticsearch
咱們仍是以英文兒歌的索引爲案例,看一個搜索需求:歌詞內容包含friend,同時歌長大於30秒的記錄分佈式
GET /music/children/_search { "query": { "bool": { "must": [ { "match": { "content": "friend" } } ], "filter": { "range": { "length": { "gte": 30 } } } } } }
僅按照搜索條件把須要的數據篩選出來,不計算相關度分數。
匹配條件的數據,會根據搜索條件的相關度,計算每一個document的分數,而後按照分數進行排序,這個纔是全文搜索的狀況。
filter只作過濾,不做排序,而且會緩存結果到內存中,性能很是高。
query匹配條件,要作評分,沒有緩存,性能要低一些。
filter一個很是重要的做用就是減小不相關數據對query的影響,提高query的性能,兩者經常搭配在一塊兒使用。
組合使用的時候,把指望符合條件的document的搜索條件放在query裏,把要濾掉的條件放在filter裏。
若是一個查詢只有filter過濾條件,能夠用constant_score來替代bool查詢,這樣的查詢語句更簡潔、更清晰,只是沒有評分,示例以下:
GET /music/children/_search { "query": { "constant_score": { "filter": { "term": { "content": "gymbo"} } } } }
filter內不支持terms語法,注意一下。
再複雜的查詢語句,也是由最基礎的查詢變化而來的,而最經常使用的查詢其實也就那麼幾個。
查詢簡單的匹配全部文檔
GET /_search { "query": { "match_all": {} } }
不管是全文搜索仍是精確查詢,match查詢是最基本的標準
# 全文搜索例子 { "match": { "content": "loves smile" }} # 精確搜索 { "match": { "likes": 15 }} { "match": { "date": "2019-12-05" }} { "match": { "isOwner": true }} { "match": { "keyword": "love you" }}
對於精確值的查詢,咱們可使用filter來替代,filter有緩存的效果。
能夠在多個字段上執行相同的match查詢
{ "multi_match": { "query": "my sunshine", "fields": [ "name", "content" ] } }
查詢指定區間內的數字或時間,query和filter都支持,通常是filter用得多,容許的操做符以下:
{ "range": { "length": { "gte": 45, "lt": 60 } } }
用於精確值匹配,精確值能夠是數字,日期,boolean或keyword類型的字符串
{ "term": { "likes": 15 }} { "term": { "date": "2019-12-05" }} { "term": { "isOwner": true }} { "term": { "keyword": "love you" }}
創建索引時mapping設置爲not_analyzed時,match等同於term,用得多的是match和range。
跟term相似,只是容許一次指定多個值進行匹配,只要有任何一個匹配上,都知足條件
{ "terms": { "content": [ "love", "gymbo", "sunshine" ] }}
複雜的查詢語句,可能會有幾百行,能夠先使用調試工具檢測一下查詢語句,定位不合法的搜索及緣由,完整語法以下:
GET /index/type/_validate/query?explain { "query": { ... } }
explain參數能夠提供更詳細的查詢不合法的信息,便於問題定位。寫一個錯誤的例子,好比使用中文標點符號:
GET /music/children/_validate/query?explain { "query": { "terms": { "content「: [ "love", "gymbo", "sunshine" ] } } }
錯誤提示以下:
{ "valid": false, "error": """ ParsingException[Failed to parse]; nested: JsonParseException[Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33]];; com.fasterxml.jackson.core.JsonParseException: Unexpected character ('l' (code 108)): was expecting a colon to separate field name and value at [Source: org.elasticsearch.transport.netty4.ByteBufStreamInput@5e57280e; line: 3, column: 33] """ }
valid關鍵字,true爲驗證經過,false爲不經過,如上提示信息,會指明3行33列錯誤,緣由是使用了中文的引號。將語法修正後,獲得的正確響應以下:
{ "_shards": { "total": 1, "successful": 1, "failed": 0 }, "valid": true, "explanations": [ { "index": "music", "valid": true, "explanation": "+content:(gymbo love sunshine) #*:*" } ] }
查詢請求獲得的結果,默認排序是相關性得分降序。若是咱們只使用filter過濾,符合filter條件的文檔,評分都是同樣的(bool的filter得分是null,constant_score得分是1),結果文檔仍是隨機返回,顯然這樣的排序不符合咱們的預期。
爲此,咱們可使用sort屬性,對文檔進行排序,sort的用法與mysql一模一樣,示例以下:
GET /music/children/_search { "query": { "bool": { "filter": { "range": { "length" : { "gt" : 30 }} } } }, "sort": [ { "length": { "order": "desc" } } ] }
sort內能夠同時指定多個排序字段,一旦使用sort排序後,_score得分將變成null,由於咱們指定了排序規則,_score沒有實際意義了,就不用耗費精力再去計算它。
咱們知道text類型的字段,會有關鍵詞分詞處理,對這樣的字段進行排序,結果每每都不許確,6.x版本之後的text類型,會再自動創建一個keyword類型的字段,這個字段是不分詞的,因此這樣就有了分工,text類型的負責搜索,keyword類型則負責排序。
咱們回顧一下music索引的mapping信息(節選):
{ "music": { "mappings": { "children": { "properties": { "content": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } }, "name": { "type": "text", "fields": { "keyword": { "type": "keyword", "ignore_above": 256 } } } } } } } }
例如name字段,有一個text類型的,裏面fields還有一個類型爲keyword,名稱也爲keyword的字段,因此在排序的場景中,咱們應該使用name.keyword,示例以下:
GET /music/children/_search { "sort": [ { "name.keyword": { "order": "asc" } } ] }
本篇介紹Query DSL的語法及基礎實戰內容,順帶點了一下filter與query的區別,面對複雜查詢語句時,建議先用驗證工具進行排查,最後介紹了一下排序方面的知識,基礎語法、上機案例多實踐便可。
專一Java高併發、分佈式架構,更多技術乾貨分享與心得,請關注公衆號:Java架構社區