ElasticSearch實踐系列(三):探索資料
前言
經過前兩篇文章得實踐,我們已經瞭解了ElasticSearch的基礎知識,本篇文章讓我來操作一些更真實的資料集。
我們可以利用ofollow,noindex" target="_blank">
www.json-generator.com/
生成如下的文件結構:
{ "account_number": 1, "balance": 39225, "firstname": "Amber", "lastname": "Duke", "age": 32, "gender": "M", "address": "880 Holmes Lane", "employer": "Pyrami", "email": "[email protected]", "city": "Brogan", "state": "IL" }
載入簡單資料集
我們可以下載es提供的資料集accounts.json ,然後推送到ES叢集
wgethttps://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
curl "localhost:9200/_cat/indices?v"
我們可以看到1000個文件已經索引到bank索引下了。
[root@XXXXX cusD]# curl "localhost:9200/_cat/indices?v" health status indexuuidpri rep docs.count docs.deleted store.size pri.store.size yellow openindex3BGZ895tTNa8qtM_nA3YmA51104.4kb4.4kb greenopen.kibanaqCbYeswVT2WCogz_E9Y3Ag102013.7kb13.7kb yellow opencustomerx57uWBR3Rg-w2_Dz7Djduw51104.5kb4.5kb yellow opencustomerb 80DoY8e3RtinVNV4VGU4Cg51104.5kb4.5kb yellow openbest3DPh-_bOLQBimS9jqWVyyjw513010.9kb10.9kb yellow openbest1oD5uUlCbSnqevbRfLvl2Iw51105.5kb5.5kb yellow opencustomer2 VyIXSBK6R9yHNYNDlsni3A51001.2kb1.2kb yellow opencustomerc Nbglz5hbRO28jyt_XyPNTA51104.5kb4.5kb yellow opencustxuYth97RShixNtgNpbyxBA51104.4kb4.4kb yellow opencustomerf osKgtSLxTPKblJW7mrmO0Q51105.1kb5.1kb yellow openbankWrk49iM6TjGItiZKWdnzJA5110000474.7kb474.7kb yellow opencustomer3 101ZzeNmRuCn9d_NOx5oZg51001.2kb1.2kb yellow opencustomere p2BWLci9Qz-1VnOh0vSSQA51207.6kb7.6kb
搜尋API
讓我們開始執行一些簡單的搜尋api,有兩種方式:
- 傳送引數到REST request URI
- 傳送請求到REST request body ,請求body支援json格式,易讀易使用。
GET /bank/_search?q=*&sort=account_number:asc&pretty
讓我們分析下這個搜尋請求。我們正在用_search搜尋 bank索引。q=代表Es會匹配索引內的全部文件。 sort=account_number:asc 代表每個文件的欄位以account_number升序對結果進行排序。 pretty *代表結果以漂亮的json格式輸出。
這裡摘選部分結果
{ "took": 53, "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", "_score": null, "_source": { "account_number": 0, "balance": 16623, "firstname": "Bradshaw", "lastname": "Mckenzie", "age": 29, "gender": "F", "address": "244 Columbus Place", "employer": "Euron", "email": "[email protected]", "city": "Hobucken", "state": "CO" }, "sort": [ 0 ] }, }
- took - Elasticsearch執行搜尋的時間(以毫秒為單位)
- timed_out - 告訴我們搜尋是否超時
- _shards - 告訴我們搜尋了多少個分片,以及搜尋成功/失敗分片的計數
- hits - 搜尋結果
- hits.total - 符合我們搜尋條件的文件總數
- hits.hits - 實際的搜尋結果陣列(預設為前10個文件)
- hits.sort - 對結果進行排序(如果按分數排序則丟失)
-
hits._score並max_score- 暫時忽略這些欄位
也可以用Request Body方式執行搜尋,格式如下:
GET /bank/_search { "query": { "match_all": {} }, "sort": [ { "account_number": "asc" } ] }
介紹查詢語言【Query Language】
Elasticsearch提供了一種JSON樣式的特定於域的語言,可用於執行查詢。這被稱為查詢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規定文件開始的索引,size指定了查詢文件的大小。在實現分頁時,這兩個引數非常有用。from如果不傳,預設為0。
下面的示例執行一個 match_all並按帳戶餘額降序對結果進行排序,返回前10個(預設大小)文件。
GET /bank/_search { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }
執行搜尋
上面我們已經看到了一些基本的查詢示例,讓我們再深入瞭解下QueryDSL 。讓我們來看下返回的json文件的欄位。預設情況下會返回命中文件的所有欄位。這被稱為源(_source代表命中的欄位)。有些情況下,我們只需要部分欄位,如下:
GET /bank/_search { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }
下面我們來說說查詢部分。之前我們講過match_all是匹配所有文件,現在讓我們瞭解一個
match
query
,它能針對特定欄位或欄位集進行搜尋。
下面這個示例能搜尋account_number為20的資料:
GET /bank/_search { "query": { "match": { "account_number": 20 } } }
此示例返回地址中包含術語“mill”或“lane”的所有帳戶,這裡格外注意【空格隔開的兩個單詞是or查詢】:
GET /bank/_search { "query": { "match": { "address": "mill lane" } } }
此示例演示地址種包含“mill lane”的所有賬戶,【用match_phrase查詢時,空格隔開的依然是一個單詞】
GET /bank/_search { "query": { "match_phrase": { "address": "mill lane" } } }
然後我們繼續介紹下
bool
query
,它允許我們使用布林查詢將更小的查詢組合成更大的查詢。
must 同時滿足條件此示例組成兩個match查詢並返回地址中包含“mill”和“lane”的所有帳戶:
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,該bool must子句指定必須為true才能將文件視為匹配的所有查詢。
should或滿足一個即可 此示例組成兩個match查詢並返回地址中包含“mill”或“lane”的所有帳戶:
GET /bank/_search { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
在上面的示例中,該bool should子句指定了一個查詢列表,其中任何一個查詢都必須為true,才能將文件視為匹配項。
must_not都不包含 此示例組成兩個match查詢並返回地址中既不包含“mill”也不包含“lane”的所有帳戶:
GET /bank/_search { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }
我們可以在查詢中同時組合must,should和must_not子句bool。此外,我們可以bool在任何這些bool子句中組合查詢來模仿任何複雜的多級布林邏輯。
此示例返回任何40歲但不住在ID(aho)的人的所有帳戶
GET /bank/_search { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }
執行過濾器
上面的示例中,我們跳過了一個稱為文件分數的小細節(_score搜尋結果中的欄位)。分數是一個數值,它是文件與我們指定的搜尋查詢匹配程度的相對度量。分數越高,文件越相關,分數越低,文件的相關性越低。
但是查詢並不總是需要產生分數,特別是當它們僅用於“過濾”文件集時。Elasticsearch會檢測這些情況並自動優化查詢執行,以便不計算無用的分數。
我們在上面示例介紹的
bool
查詢
還支援filter
允許使用查詢來限制將與其他子句匹配的文件的子句,而不會更改計算得分的方式。作為示例,讓我們介紹一下
range
查詢
,它允許我們按一系列值過濾文件。一般數字或日期會用到range。
此示例使用bool查詢返回餘額大於或等於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個(預設)states(也是預設值),(這裡的group_by_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": 0, "timed_out": false, "_shards": { "total": 5, "successful": 5, "skipped": 0, "failed": 0 }, "hits": { "total": 1000, "max_score": 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 } ] } } }
我們可以看到key為ID的有27個賬戶,TX也是27個賬戶,AL的是25個賬戶,以此類推。
請注意,我們設定size=0為不顯示搜尋匹配,因為我們只希望在響應中看到聚合結果。
在前一個聚合的基礎上,此示例按州計算平均帳戶餘額(同樣僅針對按降序排序的前10個州):
GET /bank/_search { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }
請注意我們如何巢狀average_balance聚合內的group_by_state聚合。這是所有聚合的常見模式。您可以在聚合中任意巢狀聚合,以從資料中提取所需的輪轉摘要。
在前一個聚合的基礎上,我們現在按降序排列平均餘額:
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" } } } } } } } }
還有許多其他聚合功能,我們在此不再詳述。如果您想進行進一步的實驗,聚合參考指南 是一個很好的起點。
總結
本篇文章依據官方文件,實踐了查詢和聚合命令,前面查詢的部分還是很簡單的,聚合這塊有些複雜。
本篇到此結束,感謝觀看。有興趣的可以通過 http://www.weixinhe.cn:5601 演示上述命令。