目錄
探索你的數(shù)據(jù)
現(xiàn)在我們已經(jīng)了解了基礎(chǔ)知識(shí),讓我們嘗試處理一個(gè)更真實(shí)的數(shù)據(jù)集聋庵。
加載示例數(shù)據(jù)集
我們可以從這里下載數(shù)據(jù)集梨树。將它下載保存到我們的當(dāng)前目錄并且加載到我們的集群里瓦呼,如下:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
curl "localhost:9200/_cat/indices?v"
響應(yīng)格式:
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個(gè)文檔到bank索引(在_doc類型下)销斟。
搜索API
現(xiàn)在讓我們從簡(jiǎn)單的搜索開(kāi)始庐椒。這里有兩個(gè)基本的辦法去運(yùn)行搜索:其一是通過(guò)REST request URI發(fā)送搜索參數(shù),另一個(gè)是通過(guò)REST request body發(fā)送搜索參數(shù)蚂踊。請(qǐng)求體方法允許您更有表現(xiàn)力约谈,也可以用更可讀的JSON格式定義搜索。我們將嘗試請(qǐng)求URI方法的一個(gè)示例,但是對(duì)于本教程的其余部分棱诱,我們將只使用請(qǐng)求主體方法泼橘。
搜索的REST API可以從_search端點(diǎn)訪問(wèn)。這個(gè)示例返回銀行索引中的所有文檔:
curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:asc&pretty"
讓我們首先分析搜索調(diào)用迈勋。我們?cè)?strong>bank索引中搜索(_search端點(diǎn))炬灭,q=參數(shù)指示Elasticsearch匹配索引中的所有文檔。sort=account_number:asc參數(shù)指示使用每個(gè)文檔的account_number字段按升序?qū)Y(jié)果進(jìn)行排序靡菇。同樣重归,pretty參數(shù)只是告訴Elasticsearch返回漂亮的JSON結(jié)果。
響應(yīng)(部分顯示):
{
"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"}
}, ...
]
}
}
關(guān)于響應(yīng)镰官,我們看到以下部分:
- took - Elasticsearch執(zhí)行搜索的時(shí)間以毫秒為單位
- time_out - 告訴我們這個(gè)搜索是否超時(shí)
- _shards - 告訴我們搜索了多少碎片提前,以及成功/失敗搜索碎片的計(jì)數(shù)
- hist - 搜索結(jié)果
- hits.total - 符合我們的搜索條件的文檔總數(shù)
- hits.hits - 實(shí)際的搜索結(jié)果數(shù)組(默認(rèn)為前10個(gè)文檔)
- hits.sort - 結(jié)果的排序鍵(如果按分?jǐn)?shù)排序吗货,則會(huì)丟失)
- hits._score and max_score - 暫時(shí)忽略這些字段
下面是使用替代請(qǐng)求主體方法進(jìn)行的相同搜索:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"sort": [
{ "account_number": "asc" }
]
}
'
這里的區(qū)別在于泳唠,我們沒(méi)有在URI中傳遞q=*,而是向_search API提供json風(fēng)格的查詢請(qǐng)求體宙搬。我們將在下一節(jié)討論這個(gè)JSON查詢笨腥。
很重要的一點(diǎn):一旦返回搜索結(jié)果,Elasticsearch就完全完成了對(duì)請(qǐng)求的處理勇垛,不會(huì)在結(jié)果中維護(hù)任何類型的服務(wù)器端資源或打開(kāi)游標(biāo)脖母。這與許多其他平臺(tái)如SQL形成鮮明對(duì)比,你最初可能會(huì)得到查詢結(jié)果的部分子集闲孤,然后如果你想獲取其余的結(jié)果谆级,必須使用某種狀態(tài)的服務(wù)器端游標(biāo)不斷到服務(wù)器去獲取。
介紹查詢語(yǔ)言
Elasticsearch提供了一種json風(fēng)格的域特定語(yǔ)言讼积,您可以使用這種語(yǔ)言執(zhí)行查詢肥照。這稱為DSL查詢語(yǔ)言。查詢語(yǔ)言非常全面勤众,乍一看可能有些嚇人舆绎, 但實(shí)際上最好的學(xué)習(xí)辦法是從幾個(gè)基本示例開(kāi)始。
回到上一個(gè)例子们颜,我們執(zhí)行了這個(gè)查詢:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} }
}
'
分析上面吕朵,query部分告訴我們查詢定義是什么,match_all部分只是我們想要運(yùn)行的查詢類型窥突。match_all查詢只是在指定索引中搜索所有文檔努溃。
除了查詢參數(shù)外,我們還可以傳遞其他參數(shù)來(lái)影響搜索結(jié)果阻问。在上面部分的例子中梧税,我們傳遞了sort,這里我們傳遞了size:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"size": 1
}
'
注意,如果沒(méi)有指定大小贡蓖,默認(rèn)值為10曹鸠。
這個(gè)示例執(zhí)行match_all并返回文檔10到19:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"from": 10,
"size": 10
}
'
from參數(shù)(基于0)指定從哪個(gè)文檔索引開(kāi)始,size參數(shù)指定從from參數(shù)開(kāi)始返回多少文檔斥铺。這個(gè)特性在實(shí)現(xiàn)搜索結(jié)果分頁(yè)時(shí)非常有用彻桃。注意,如果未指定from晾蜘,則默認(rèn)為0邻眷。
這個(gè)示例執(zhí)行match_all,并按照帳戶余額降序?qū)Y(jié)果進(jìn)行排序剔交,并返回前10個(gè)(默認(rèn)大小)文檔肆饶。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"sort": { "balance": { "order": "desc" } }
}
'
執(zhí)行搜索
現(xiàn)在我們已經(jīng)看到了一些基本的搜索參數(shù),讓我們更深入一些到DSL查詢語(yǔ)句中岖常。首先讓我們看一下返回的文檔字段驯镊。默認(rèn)情形下,完整的JSON文檔是返回的所有查詢結(jié)果的一部分竭鞍。這稱為源(搜索命中的_source字段)板惑。如果我們不想整個(gè)的源文檔返回,我們能夠從源文檔中請(qǐng)求只返回幾個(gè)字段偎快。
這個(gè)示例展示了如何從查詢中返回兩個(gè)字段冯乘,account_number和balance(在_source字段中):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_all": {} },
"_source": ["account_number", "balance"]
}
'
注意,上面的示例只是減少了_source字段晒夹。它仍然只返回一個(gè)名為_(kāi)source的字段裆馒,但其中只包含account_number和balance字段。
如果你有SQL的背景丐怯,上面的查詢?cè)诟拍钌鲜呛蚐QL語(yǔ)言喷好,SELECT FROM 字段列表有點(diǎn)相似的。
現(xiàn)在讓我們繼續(xù)查詢部分响逢,之前绒窑,我們已經(jīng)看到match_all是如何被用來(lái)匹配所有文檔的。現(xiàn)在讓我們介紹一個(gè)新的查詢叫做match query舔亭,可以將其視為基本的字段搜索查詢(即針對(duì)特定字段或字段集進(jìn)行的搜索)些膨。
這個(gè)示例返回編號(hào)為20的帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "account_number": 20 } }
}
'
這個(gè)示例返回地址中包含術(shù)語(yǔ)“mill”的所有帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "address": "mill" } }
}
'
這個(gè)示例返回地址中包含術(shù)語(yǔ)“mill”或“l(fā)ane”的所有帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match": { "address": "mill lane" } }
}
'
這個(gè)示例是match (match_phrase)的變體弊予,它返回地址中包含短語(yǔ)“mill lane”的所有帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": { "match_phrase": { "address": "mill lane" } }
}
'
現(xiàn)在讓我們介紹bool query黔漂。bool查詢?cè)试S我們使用布爾邏輯將較小的查詢組合成較大的查詢。
這個(gè)示例組合兩個(gè)match查詢并且返回地址中包含“mill”和“l(fā)ane”的所有賬戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
'
在上面的示例中冕杠,bool must子句指定所有查詢矛洞,這些查詢必須為true洼哎,才能將文檔視為匹配烫映。
與此相反,這個(gè)示例包含兩個(gè)匹配查詢噩峦,并返回地址中包含“mill”或“l(fā)ane”的所有帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"should": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
'
在上面的示例中锭沟,bool should子句指定了一個(gè)查詢列表,其中一個(gè)查詢列表必須為true识补,才能將文檔視為匹配族淮。
這個(gè)示例包含兩個(gè)匹配查詢,并返回地址中既不包含“mill”也不包含“l(fā)ane”的所有帳戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must_not": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
'
在上面的例子中凭涂,bool must_not子句指定了一個(gè)查詢列表祝辣,其中沒(méi)有一個(gè)查詢必須為真,才能將文檔視為匹配切油。
我們可以在bool查詢中同時(shí)組合must蝙斜、should和must_not子句。
此外澎胡,我們可以在任何bool子句中編寫(xiě)bool查詢孕荠,以模擬任何復(fù)雜的多級(jí)布爾邏輯。
這個(gè)例子返回了所有40歲但不生活在ID(aho)中的人的賬戶:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": [
{ "match": { "age": "40" } }
],
"must_not": [
{ "match": { "state": "ID" } }
]
}
}
}
'
執(zhí)行過(guò)濾器
在上一節(jié)中滤馍,我們跳過(guò)了一個(gè)名為文檔分?jǐn)?shù)(搜索結(jié)果中的_score字段)的小細(xì)節(jié)岛琼。分?jǐn)?shù)是一個(gè)數(shù)值底循,它是文檔與我們指定的搜索查詢匹配程度的相對(duì)度量巢株。分?jǐn)?shù)越高,文檔越相關(guān)熙涤,分?jǐn)?shù)越低阁苞,文檔越不相關(guān)。但是查詢并不總是需要產(chǎn)生分?jǐn)?shù)祠挫,特別是當(dāng)它們僅用于“過(guò)濾”文檔集時(shí)那槽。Elasticsearch可以檢測(cè)到這些情況,并自動(dòng)優(yōu)化查詢執(zhí)行等舔,以避免計(jì)算無(wú)用的分?jǐn)?shù)骚灸。我們上一節(jié)介紹的bool query也支持filter子句,它允許我們使用一個(gè)查詢來(lái)限制由其他子句匹配的文檔慌植,不改變?nèi)绾斡?jì)算分?jǐn)?shù)甚牲。作為一個(gè)例子,讓我們引入range query蝶柿,它允許我們通過(guò)一系列值篩選文檔丈钙。這通常用于數(shù)字或日期過(guò)濾。
這個(gè)示例使用bool查詢返回所有余額在20000到30000之間的帳戶(包括在內(nèi))交汤。換句話說(shuō)雏赦,我們希望找到的賬戶余額大于等于20000,小于等于30000。
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"query": {
"bool": {
"must": { "match_all": {} },
"filter": {
"range": {
"balance": {
"gte": 20000,
"lte": 30000
}
}
}
}
}
}
'
分析上面的語(yǔ)句星岗,bool查詢包含一個(gè)match_all子查詢(查詢部分)和一個(gè)range子查詢(過(guò)濾部分)填大。我們可以代替任意其他的查詢到這個(gè)查詢部分和過(guò)濾部分。在上面的例子中俏橘,范圍查詢非常有意義栋盹,因?yàn)閷儆诜秶乃形臋n都“相等”匹配,也就是說(shuō)敷矫, 沒(méi)有哪個(gè)文檔是比其他文檔更相關(guān)的例获。
除了match_all、match曹仗、bool和range查詢之外榨汤,還有許多其他的查詢類型可供使用,我們?cè)谶@里不深入討論怎茫。由于我們已經(jīng)對(duì)它們的工作原理有了基本的了解收壕,因此在學(xué)習(xí)和試驗(yàn)其他查詢類型時(shí)應(yīng)用這些知識(shí)應(yīng)該不會(huì)太難。
執(zhí)行聚合
聚合提供了從你的數(shù)據(jù)中分組和提取統(tǒng)計(jì)信息的能力轨蛤。關(guān)于聚合最簡(jiǎn)單的理解方式就是將其大致等同于SQL GROUP BY和SQL聚合函數(shù)蜜宪。在Elasticsearch中,您可以執(zhí)行返回命中的搜索祥山,同時(shí)在一個(gè)響應(yīng)中返回與命中分離的聚合結(jié)果圃验。這在某種意義上非常強(qiáng)大和高效,因?yàn)槟梢赃\(yùn)行查詢和多個(gè)聚合缝呕,并一次性獲得兩個(gè)(或兩個(gè))操作的結(jié)果澳窑,從而避免使用簡(jiǎn)潔和簡(jiǎn)化的API進(jìn)行網(wǎng)絡(luò)往返。
首先供常,這個(gè)示例按狀態(tài)對(duì)所有帳戶進(jìn)行分組摊聋,然后返回按計(jì)數(shù)遞減排序的前10個(gè)(默認(rèn))狀態(tài):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
}
}
}
}
'
在SQL中,上面的聚合在概念上相當(dāng)于:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
部分響應(yīng)內(nèi)容:
{
"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(愛(ài)達(dá)荷州)有27個(gè)賬戶栈暇,緊跟著TX(德克薩斯州)有27個(gè)賬戶麻裁,緊跟著AL(阿拉巴馬州)有25個(gè)賬戶,等等源祈。
注意:我們?cè)O(shè)置size=0來(lái)不展示搜索結(jié)果因?yàn)槲覀冎幌朐诜祷貎?nèi)容里看到聚合結(jié)果煎源。
在前面的聚合基礎(chǔ)上,本示例計(jì)算了按狀態(tài)排序的平均帳戶余額(同樣新博,僅計(jì)算按計(jì)數(shù)降序排序的前10個(gè)狀態(tài)):
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword"
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
'
注意我們?nèi)绾螌?strong>average_balance聚合嵌套到group_by_state聚合中薪夕。這是所有聚合的常見(jiàn)模式。您可以將聚合嵌套在聚合中赫悄,任意地從數(shù)據(jù)中提取需要的旋轉(zhuǎn)匯總原献。
在前面的聚合的基礎(chǔ)上馏慨,現(xiàn)在讓我們按降序?qū)ζ骄囝~進(jìn)行排序:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "state.keyword",
"order": {
"average_balance": "desc"
}
},
"aggs": {
"average_balance": {
"avg": {
"field": "balance"
}
}
}
}
}
}
'
這個(gè)例子展示了我們?nèi)绾胃鶕?jù)年齡等級(jí)(20-29歲,30-39歲姑隅,40-49歲)來(lái)分組写隶,然后根據(jù)性別分組,最后得到平均賬戶余額讲仰,每個(gè)年齡等級(jí)慕趴,每個(gè)性別:
curl -X GET "localhost:9200/bank/_search" -H 'Content-Type: application/json' -d'
{
"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"
}
}
}
}
}
}
}
}
'
還有很多其他的聚合功能我們?cè)谶@里不會(huì)詳細(xì)介紹。如果您想做進(jìn)一步的實(shí)驗(yàn)鄙陡,聚合參考指南是一個(gè)很好的起點(diǎn)冕房。
結(jié)論
Elasticsearch是一個(gè)簡(jiǎn)單的和復(fù)雜的乘積。到目前為止趁矾,我們已經(jīng)了解了它是什么耙册,如何查看它的內(nèi)部,以及如何使用一些REST api使用它毫捣。希望本教程能讓您更好地理解Elasticsearch是什么详拙,更重要的是,它激發(fā)了您對(duì)其其他強(qiáng)大功能的進(jìn)一步試驗(yàn)!