Query DSL是一種通過request body提交搜索參數的請求方式。就是將請求頭參數(?xxx=xxx)轉換為請求體參數。
語法格式:
GET [/index_name/type_name]/_search
{
"query_name" : {
"argument" : "value" [, ....]
},
"query_name" : {
"field_name" : {
"argument" : "value" [, ....]
}
}
}
4.1 測試數據
PUT /test_index
{
"mappings": {
"test_type" : {
"properties": {
"dept_name" : {
"type" : "keyword"
},
"emps.name" : {
"type" : "text",
"analyzer": "standard"
},
"emps.age" : {
"type": "long"
},
"num_of_emps" : {
"type": "long"
},
"tags" : {
"type" : "keyword"
}
}
}
}
}
PUT /test_index/test_type/1
{
"dept_name" : "Sales Department",
"emps" : [
{ "name" : "zhangsan", "age" : 20, "join_date" : "2018-07-10" },
{ "name" : "wangwu", "age" : 22, "join_date" : "2018-07-10" }
],
"num_of_emps" : 2,
"tags" : [ "Sales tag1", "Sales tag2", "Sales tag3" ]
}
PUT /test_index/test_type/2
{
"dept_name" : "Development Department",
"emps" : [
{ "name" : "Rod Johnson", "age" : 40, "join_date" : "2016-07-10" },
{ "name" : "James Gosling", "age" : 41, "join_date" : "2017-07-10" },
{ "name" : "Patrick Lencioni", "age" : 42, "join_date" : "2018-01-10" }
],
"num_of_emps" : 3,
"tags" : [ "Development tags1", "Development tags2", "Development tags3" ]
}
4.2 搜索案例
全查詢
GET /test_index/test_type/_search
{
"query" : {
"match_all" : {}
}
}
單字段匹配
GET /test_index/test_type/_search
{
"query" : {
"match" : { "emps.name" : "zhangsan" }
}
}
組合條件:
用java來描述 : ( a > 3 && ( b < 10 || c == 6 ) )
bool - 用於組合多條件,相當於java中的布爾表達式。相當於最外層的括號,也就是完整的布爾表達式。
must - 必須符合要求, 相當於java中的邏輯運算符 ==或&&。在must范圍內的條件都必須滿足。
must_not - 必須不符合要求, 相當於java中的邏輯運算符 !
should - 有任意條件符合要求即可,相當於java中的邏輯運算符 ||,在should范圍內的條件,只要有任意一個滿足即可。
range - 數學比較條件。計算數學范圍的。
常見的搜索方式在后續案例中演示。
GET /test_index/test_type/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"dept_name": "Sales Department"
}
}
],
"must_not": [
{
"range": {
"num_of_emps": {
"gt": 10
}
}
}
],
"should": [
{
"match": {
"emps.name": "wangwu"
}
},
{
"range": {
"emps.age": {
"lte": 10
}
}
}
]
}
}
}
4.2.1 match
根據某條件搜索document。match會根據field對應的analyzer對搜索條件做分詞。並在倒排索引中搜索數據。
GET /_search
{
"query" : {
"match" : { "dept_name" : "Sales Department" }
}
}
4.2.2 multi match
使用搜索條件,在多個字段中進行全文檢索,搜索document數據。也會將搜索條件使用字段的analyzer做分詞。
GET /_search
{
"query" : {
"multi_match": {
"query": "rod",
"fields": ["dept_name", "emps.name"]
}
}
}
4.2.3 range
數學比較搜索
GET /_search
{
"query" : {
"range" : {
"emps.age" : {
"gt" : 21, "lte" : 45
}
}
}
}
在ES中range也可以實現日期的比較,如:查詢40天以內入職的員工。其語法為:
GET /test_index/_search
{
"query": {
"range": {
"emps.join_date": {
"gte": "2018-08-10||-40d"
}
}
}
}
4.2.4 term
詞組比較,詞組搜索。
忽略搜索條件分詞,使用搜索條件進行精確匹配。進行匹配搜索的字段mapping建議手工創建,且type值為keyword。ES在處理keyword類型的字符串時,不進行分詞操作。term搜索可執行。ES中對數字,日期,boolean類型數據也不會做分詞操作。
在ES5.x版本后,為text類型的field默認會創建一個子字段,子字段命名為field_name.keyword,這個字段是不做分詞的,默認字段長度最大256個字符。
GET /_search
{
"query" : {
"term" : {
"dept_name" : "Sales Department"
}
}
}
4.2.5 terms
與term含義相同,就是可以比對多個搜索條件數據
GET /_search
{
"query" : {
"terms" : {
"tags" : [ "Sales tag1", "Development tags3" ]
}
}
}
4.2.6 復合搜索
在一個請求體中,有多個搜索條件,就是復合搜索。如:搜索數據,條件為部門名稱是Sales Department,部門員工數量少於10人,部門員工入職時間晚於2017-01-01,部門員工中一定有人叫zhangsan。上述條件中,部門名稱可選,部門員工數量必須滿足要求,部門員工入職時間和人名有任意一個條件滿足即可。
在執行復合條件搜索的時候,命令也是有內外順序的。總體來說就是:"bool" : { "must" : { "action" : {} }, "should" : { "action" : {} }, "must_not" : { "action" : {} }, bool: {}}。 bool命令是可以嵌套的。而"action"代表的就是具體的比對方式,如:match,range等。
GET /test_index/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"num_of_emps": {
"lt": 10
}
}
}
],
"should": [
{
"match": {
"dept_name": "Sales Department"
}
},
{
"match": {
"emps.name": "zhangsan"
}
},
{
"range": {
"emps.join_date": {
"gte": "2017-01-01"
}
}
}
]
}
}
}
4.2.7 排序
在ES的搜索中,默認是使用相關度分數實現排序的。可以通過搜索語法實現定制化排序。
GET test_index/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"num_of_emps": {
"order": "asc"
}
},
{
"dept_name": {
"order": "desc"
}
}
],
"_source": ["dept_name", "num_of_emps"]
}
注意:在ES中,如果使用字符串類型的字段作為排序依據,可能會有問題,尤其是字段類型為text時,ES需要對字段數據做分詞,建立倒排索引。如果使用text類型字段做排序,ES給出的排序結果未必友好,畢竟分詞后,先使用哪一個單詞做排序是不固定的。如:
PUT /test_sort/sort_type/1
{
"content" : "first content",
"order" : 1
}
PUT /test_sort/sort_type/2
{
"content" : "second content",
"order" : 2
}
GET /test_sort/sort_type/_search
{
"query" : {
"match" : {
"content" : "content"
}
},
"sort" : [
{
"content" : { "order" : "asc" }
}
]
}
此時需要對字段content進行特殊處理,處理如下:為字符串字段增加子字段sf(sort field),這個字段類型為keyword類型(不進行分詞),排序時使用這個子字段content.sf作為排序字段即可。在ES5.x版本之后,ES默認為每一個text類型的字段建立一個子字段。子字段類型為keyword,可以實現排序機制。
DELETE /test_sort
PUT /test_sort
{
"mappings": {
"sort_type": {
"properties": {
"content" : {
"type": "text",
"analyzer": "standard",
"fields": {
"sf" : {
"type": "keyword"
}
}
},
"order" : {
"type": "long"
}
}
}
}
}
PUT /test_sort/sort_type/1
{
"content" : "first content",
"order" : 1
}
PUT /test_sort/sort_type/2
{
"content" : "second content",
"order" : 2
}
GET /test_sort/sort_type/_search
{
"query" : {
"match_all" : {}
},
"sort" : [
{
"content.sf" : { "order" : "desc" }
}
]
}
4.2.8 scroll搜索
如果需要一次性搜索出10萬數據,那么執行效率一定不會很高,這個時候可以使用scroll滾動搜索的方式實現搜索。scroll滾動搜索類似分頁,是在搜索的時候,先查詢一部分,之后再查詢下一部分,分批查詢總體數據。可以實現一個高效的響應。
scroll搜索會在第一次搜索的時候保存一個快照,這個快照保存的時長由請求指定,后續的查詢會依據這個快照執行再次查詢。如果這個過程中,ES中的document發生了變化,是不會影響到原搜索結果的。
scroll搜索時,需要指定一個排序,可以使用_doc實現排序,這種排序性能更高一些。
scroll搜索的2次以后的請求,必須在指定的快照保存時長內發起,且每次請求都需要指定這個快照保存時長。
scroll搜索的返回結果一定會包含一個特殊的結果數據_scroll_id。這個數據就是scroll搜索的快照ID。scroll搜索的2次以后的請求,必須攜帶上這個ID,否則無法依據ES保存的快照執行后續的搜索。
測試:
GET /t_index/_search?scroll=1m
{
"query": {
"match_all": {}
},
"sort": [ "_doc" ],
"size" : 1
}
GET /_search/scroll
{
"scroll" : "1m",
"scroll_id" : "根據具體返回結果替換"
}
scroll不是分頁,不是用來替換分頁搜索的技術。一般來說,分頁是為用戶提供數據的,scroll是為系統內部處理分批提供數據的。如:reindex 重建索引。