ElasticSearch實踐系列(三):探索資料
前言
經過前兩篇文章得實踐,我們已經瞭解了ElasticSearch的基礎知識,本篇文章讓我來操作一些更真實的資料集。我們可以利用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叢集
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索引下了。
[[email protected] cusD]# curl "localhost:9200/_cat/indices?v"
health status index uuid pri rep docs.count docs.deleted store.size pri.store.size
yellow open index 3BGZ895tTNa8qtM_nA3YmA 5 1 1 0 4.4kb 4.4kb
green open .kibana qCbYeswVT2WCogz_E9Y3Ag 1 0 2 0 13.7kb 13.7kb
yellow open customer x57uWBR3Rg-w2_Dz7Djduw 5 1 1 0 4.5kb 4.5kb
yellow open customerb 80DoY8e3RtinVNV4VGU4Cg 5 1 1 0 4.5kb 4.5kb
yellow open best3 DPh-_bOLQBimS9jqWVyyjw 5 1 3 0 10.9kb 10.9kb
yellow open best1 oD5uUlCbSnqevbRfLvl2Iw 5 1 1 0 5.5kb 5.5kb
yellow open customer2 VyIXSBK6R9yHNYNDlsni3A 5 1 0 0 1.2kb 1.2kb
yellow open customerc Nbglz5hbRO28jyt_XyPNTA 5 1 1 0 4.5kb 4.5kb
yellow open cust xuYth97RShixNtgNpbyxBA 5 1 1 0 4.4kb 4.4kb
yellow open customerf osKgtSLxTPKblJW7mrmO0Q 5 1 1 0 5.1kb 5.1kb
yellow open bank Wrk49iM6TjGItiZKWdnzJA 5 1 1000 0 474.7kb 474.7kb
yellow open customer3 101ZzeNmRuCn9d_NOx5oZg 5 1 0 0 1.2kb 1.2kb
yellow open customere p2BWLci9Qz-1VnOh0vSSQA 5 1 2 0 7.6kb 7.6kb
搜尋API
讓我們開始執行一些簡單的搜尋api,有兩種方式:
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 演示上述命令。