如今咱們已經瞭解了基礎知識,讓咱們嘗試更真實的數據集,我準備了一份關於客戶銀行帳戶信息的虛構JSON文檔樣本,每一個文檔都有如下模式:html
{ "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "bradshawmckenzie@euron.com", "city": "Hobucken", "state": "CO" }
奇怪的是,這些數據是使用www.json-generator.com/生成的,所以請忽略數據的實際值和語義,由於這些都是隨機生成的。git
你能夠從此處下載示例數據集(accounts.json),將它解壓縮到咱們當前的目錄,而後將它們加載到咱們的集羣中,以下所示:github
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json" curl "localhost:9200/_cat/indices?v"
響應以下:json
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size yellow open bank l7sSYV2cQXmu6_4rJWVIww 5 1 1000 0 128.6kb 128.6kb
這意味着咱們只是成功地將1000個文檔批量索引到bank
索引(在_doc
類型下)。segmentfault
如今讓咱們從一些簡單的搜索開始吧,運行搜索有兩種基本方法:一種是經過REST請求URI發送搜索參數,另外一種是經過REST請求體發送它們。請求體方法容許你更具表現力,並以更易讀的JSON格式定義搜索,咱們將嘗試一個請求URI方法的示例,可是對於本教程的其他部分,咱們將專門使用請求體方法。數組
能夠從_search
端點訪問用於搜索的REST API,此示例返回bank索引中的全部文檔:服務器
GET /bank/_search?q=*&sort=account_number:asc&pretty
讓咱們首先剖析搜索調用,咱們在bank索引中搜索(_search
端點),q=*
參數指示Elasticsearch匹配索引中的全部文檔。sort=account_number:asc
參數指示使用每一個文檔的account_number
字段升序對結果進行排序,pretty
參數再次告訴Elasticsearch返回漂亮的JSON結果。網絡
響應(部分顯示):app
{ "took" : 63, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "skipped" : 0, "failed" : 0 }, "hits" : { "total" : 1000, "max_score" : null, "hits" : [ { "_index" : "bank", "_type" : "_doc", "_id" : "0", "sort": [0], "_score" : null, "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"} }, { "_index" : "bank", "_type" : "_doc", "_id" : "1", "sort": [1], "_score" : null, "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"} }, ... ] } }
至於響應,咱們看如下部分:curl
took
— Elasticsearch執行搜索的時間(以毫秒爲單位)timed_out
— 告訴咱們搜索是否超時_shards
— 告訴咱們搜索了多少個碎片,以及搜索成功/失敗碎片的計數hits
— 搜索結果hits.total
— 符合咱們搜索條件的文檔總數hits.hits
— 實際的搜索結果數組(默認爲前10個文檔)hits.sort
— 對結果進行排序(若是按分數排序則丟失)hits._score
和max_score
— 暫時忽略這些字段如下是使用替代請求體方法的上述徹底相同的搜索:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
這裏的區別在於,咱們不是在URI中傳遞q=*
,而是向_search
API提供JSON樣式的查詢請求體,咱們將在下一節討論這個JSON查詢。
重要的是要理解,一旦你得到了搜索結果,Elasticsearch就徹底完成了請求,而且不會在結果中維護任何類型的服務器端資源或打開遊標,這與SQL等許多其餘平臺造成鮮明對比,其中你可能最初預先得到查詢結果的部分子集,而後若是要獲取(或翻頁)其他的則必須連續返回服務器使用某種有狀態服務器端遊標的結果。
Elasticsearch提供了一種JSON樣式的特定於域的語言,可用於執行查詢,這被稱爲Query DSL,查詢語言很是全面,乍一看可能使人生畏,但實際學習它的最佳方法是從一些基本示例開始。
回到上一個例子,咱們執行了這個查詢:
GET /bank/_search { "query": { "match_all": {} } }
解析上面的內容,query
部分告訴咱們查詢定義是什麼,match_all
部分只是咱們想要運行的查詢類型,match_all
查詢只是搜索指定索引中的全部文檔。
除了query
參數,咱們還能夠傳遞其餘參數來影響搜索結果,在上面的部分示例中,咱們傳遞了sort
,這裏咱們傳入size
:
GET /bank/_search { "query": { "match_all": {} }, "size": 1 }
請注意,若是未指定size
,則默認爲10。
此示例執行match_all
並返回文檔10到19:
GET /bank/_search { "query": { "match_all": {} }, "from": 10, "size": 10 }
from
參數(從0開始)指定從哪一個文檔索引開始,size
參數指定從from
參數開始返回的文檔數,在實現搜索結果的分頁時,此功能很是有用,請注意,若是未指定from
,則默認爲0。
此示例執行match_all
並按賬戶balance降序對結果進行排序,並返回前10個(默認大小)文檔。
GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }
如今咱們已經看到了一些基本的搜索參數,讓咱們再深刻研究一下Query DSL,咱們先來看一下返回的文檔字段。默認狀況下,完整的JSON文檔做爲全部搜索的一部分返回,這被稱爲源(搜索命中中的_source
字段),若是咱們不但願返回整個源文檔,咱們能夠只請求返回源中的幾個字段。
此示例顯示如何從搜索中返回兩個字段,account_number
和balance
(在_source
內部):
GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }
請注意,上面的示例只是簡化了_source
字段,它仍然只返回一個名爲_source
的字段,但在其中只包含字段account_number
和balance
。
若是你有來自SQL背景,則上述內容在概念上與SQL SELECT FROM
字段列表有些類似。
如今讓咱們轉到查詢部分,之前,咱們已經看到match_all
查詢如何用於匹配全部文檔。如今讓咱們介紹一種稱爲match query的新查詢,它能夠被認爲是基本的字段搜索查詢(即針對特定字段或字段集進行的搜索)。
此示例返回帳戶數爲20的帳戶:
GET /bank/_search { "query": { "match": { "account_number": 20 } } }
此示例返回地址中包含「mill」項的全部賬戶:
GET /bank/_search { "query": { "match": { "address": "mill" } } }
此示例返回地址中包含「mill」或「lane」項的全部賬戶:
GET /bank/_search { "query": { "match": { "address": "mill lane" } } }
此示例是match
(match_phrase
)的變體,它返回地址中包含短語「mill lane」的全部賬戶:
GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } }
咱們如今介紹bool query,bool
查詢容許咱們使用布爾邏輯將較小的查詢組成更大的查詢。
此示例組成兩個match
查詢,並返回地址中包含「mill」和「lane」的全部賬戶:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool must
子句指定全部必須爲true的查詢才能將文檔視爲匹配項。
相反,此示例組成兩個match
查詢並返回地址中包含「mill」或「lane」的全部賬戶:
GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool should
子句指定了一個查詢列表,其中的任何一個爲true則將文檔視爲匹配項。
此示例組成兩個匹配查詢,並返回地址中既不包含「mill」也不包含「lane」的全部賬戶:
GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,bool must_not
子句指定了一個查詢列表,對於文檔而言,這些查詢都不能爲true。
咱們能夠在bool
查詢中同時組合must
,should
和must_not
子句,此外,咱們能夠在任何這些bool
子句中組合bool
查詢來模仿任何複雜的多級布爾邏輯。
此示例返回任何40歲但不住在ID(aho)的人的全部賬戶:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }
在上一節中,咱們跳過了一個稱爲文檔分數的小細節(搜索結果中的_score
字段),分數是一個數值,它是文檔與咱們指定的搜索查詢匹配程度的相對度量,分數越高,文檔越相關,分數越低,文檔的相關性越低。
可是查詢並不老是須要產生分數,特別是當它們僅用於「過濾」文檔集時,Elasticsearch會檢測這些狀況並自動優化查詢執行,以便不計算無用的分數。
咱們在上一節中介紹的bool query還支持filter
子句,這些子句容許使用查詢來限制將與其餘子句匹配的文檔,而不會更改計算得分的方式。做爲一個例子,讓咱們介紹range query,它容許咱們按一系列值過濾文檔,這一般用於數字或日期過濾。
此示例使用bool查詢返回全部餘額介於20000和30000之間的賬戶,換句話說,咱們但願找到餘額大於或等於20000且小於或等於30000的賬戶。
GET /bank/_search { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }
解析上面的內容,bool查詢包含match_all
查詢(查詢部分)和range
查詢(過濾部分),咱們能夠將任何其餘查詢替換爲查詢和過濾部分,在上述狀況下,範圍查詢很是有意義,由於落入範圍的文檔都「相同」匹配,即,沒有文檔比另外一文檔更相關。
除了match_all
,match
,bool
和range
查詢以外,還有不少其餘可用的查詢類型,咱們不會在這裏討論它們,因爲咱們已經基本瞭解它們的工做原理,所以將這些知識應用於學習和試驗其餘查詢類型應該不會太困難。
聚合提供了從數據中分組和提取統計信息的能力,考慮聚合的最簡單方法是將其大體等同於SQL GROUP BY和SQL聚合函數。在Elasticsearch中,你能夠執行返回匹配的搜索,同時在一個響應中返回與命中相關的聚合結果。這是很是強大和高效的,由於你能夠運行查詢和多個聚合,並一次性獲取兩個(或任一)操做的結果,避免使用簡潔和簡化的API進行網絡往返。
首先,此示例按state對全部賬戶進行分組,而後返回前10個(默認)state,按計數降序排序(也是默認值):
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" } } } }
在SQL中,上述聚合在概念上相似於:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
響應(部分顯示):
{ "took": 29, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped" : 0, "failed": 0 }, "hits" : { "total" : 1000, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "group_by_state" : { "doc_count_error_upper_bound": 20, "sum_other_doc_count": 770, "buckets" : [ { "key" : "ID", "doc_count" : 27 }, { "key" : "TX", "doc_count" : 27 }, { "key" : "AL", "doc_count" : 25 }, { "key" : "MD", "doc_count" : 25 }, { "key" : "TN", "doc_count" : 23 }, { "key" : "MA", "doc_count" : 21 }, { "key" : "NC", "doc_count" : 21 }, { "key" : "ND", "doc_count" : 21 }, { "key" : "ME", "doc_count" : 20 }, { "key" : "MO", "doc_count" : 20 } ] } } }
咱們能夠看到ID
(Idaho)有27個帳戶,其次是TX
(Texas)的27個帳戶,其次是AL
(Alabama)的25個帳戶,依此類推。
請注意,咱們將size=0
設置爲不顯示搜索命中,由於咱們只想在響應中看到聚合結果。
在前一個聚合的基礎上,此示例按state計算平均賬戶餘額(一樣僅針對按降序排序的前10個state):
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
請注意咱們如何嵌套group_by_state
聚合中的average_balance
聚合,這是全部聚合的常見模式,你能夠在聚合中任意嵌套聚合,以從數據中提取所需的輪轉摘要。
在前一個聚合的基礎上,咱們如今按降序排列平均餘額:
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword", "order": { "average_balance": "desc" } }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
此示例演示了咱們如何按年齡段(20-29歲,30-39歲和40-49歲)進行分組,而後按性別分組,最後獲得每一個年齡段的平均賬戶餘額:
GET /bank/_search { "size": 0, "aggs": { "group_by_age": { "range": { "field": "age", "ranges": [ { "from": 20, "to": 30 }, { "from": 30, "to": 40 }, { "from": 40, "to": 50 } ] }, "aggs": { "group_by_gender": { "terms": { "field": "gender.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } } }
還有許多其餘聚合功能,咱們在此再也不詳述,若是你想進行進一步的實驗,聚合參考指南是一個很好的起點。