Elasticsearch 資料搜尋篇·【入門級乾貨】
ES即簡單又複雜,你可以快速的實現全文檢索,又需要了解複雜的REST API。本篇就通過一些簡單的搜尋命令,幫助你理解ES的相關應用。雖然不能讓你理解ES的原理設計,但是可以幫助你理解ES,探尋更多的特性。
樣例資料
為了更好的使用和理解ES,沒有點樣例資料還是不好模擬的。這裡提供了一份官網上的資料,accounts.json。如果需要的話,也可以去這個網址玩玩,它可以幫助你自定義寫隨機的JSON資料。
首先開啟你的ES,然後執行下面的命令,windows下需要自己安裝curl、也可以使用cygwin模擬curl命令:
curl -XPOST 'localhost:9200/bank/account/_bulk?pretty' --data-binary @accounts.json
注意:
1 需要在accounts.json所在的目錄執行curl命令。
2 localhost:9200是ES得訪問地址和埠
3 bank是索引的名稱
4 account是型別的名稱
5 索引和型別的名稱在檔案中如果有定義,可以省略;如果沒有則必須要指定
6 _bulk是rest得命令,可以批量執行多個操作(操作是在json檔案中定義的,原理可以參考之前的翻譯)
7 pretty是將返回的資訊以可讀的JSON形式返回。
執行完上述的命令後,可以通過下面的命令查詢:
curl 'localhost:9200/_cat/indices?v' health index pri rep docs.count docs.deleted store.size pri.store.size yellow bank 5 1 1000 0 424.4kb 424.4kb
搜尋API
ES提供了兩種搜尋的方式:請求引數方式 和 請求體方式。
請求引數方式
curl 'localhost:9200/bank/_search?q=*&pretty'
其中bank是查詢的索引名稱,q後面跟著搜尋的條件:q=*表示查詢所有的內容
請求體方式(推薦這種方式)
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_all": {} } }'
這種方式會把查詢的內容放入body中,會造成一定的開銷,但是易於理解。在平時的練習中,推薦這種方式。
返回的內容
{ "took" : 26, "timed_out" : false, "_shards" : { "total" : 5, "successful" : 5, "failed" : 0 }, "hits" : { "total" : 1000, "max_score" : 1.0, "hits" : [ { "_index" : "bank", "_type" : "account", "_id" : "1", "_score" : 1.0, "_source" : {"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"} }, { "_index" : "bank", "_type" : "account", "_id" : "6", "_score" : 1.0, "_source" : {"account_number":6,"balance":5686,"firstname":"Hattie","lastname":"Bond","age":36,"gender":"M","address":"671 Bristol Street","employer":"Netagy","email":"[email protected]","city":"Dante","state":"TN"} }, { "_index" : "bank", "_type" : "account", "_id" : "13",
返回的內容大致可以如下講解:
took:是查詢花費的時間,毫秒單位
time_out:標識查詢是否超時
_shards:描述了查詢分片的資訊,查詢了多少個分片、成功的分片數量、失敗的分片數量等
hits:搜尋的結果,total是全部的滿足的文件數目,hits是返回的實際數目(預設是10)
_score是文件的分數資訊,與排名相關度有關,參考各大搜索引擎的搜尋結果,就容易理解。
由於ES是一次性返回所有的資料,因此理解返回的內容是很必要的。它不像傳統的SQL是先返回資料的一個子集,再通過資料庫端的遊標不斷的返回資料(由於對傳統的資料庫理解的不深,這裡有錯還望指正)。
查詢語言DSL
ES支援一種JSON格式的查詢,叫做DSL,domain specific language。這門語言剛開始比較難理解,因此通過幾個簡單的例子開始:
下面的命令,可以搜尋全部的文件:
{ "query": { "match_all": {} } }
query定義了查詢,match_all聲明瞭查詢的型別。還有其他的引數可以控制返回的結果:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_all": {} }, "size": 1 }'
上面的命令返回了所有文件資料中的第一條文件。如果size不指定,那麼預設返回10條。
下面的命令請求了第10-20的文件。
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_all": {} }, "from": 10, "size": 10 }'
下面的命令指定了文件返回的排序方式:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_all": {} }, "sort": { "balance": { "order": "desc" } } }'
執行搜尋
上面瞭解了基本的搜尋語句,下面就開始深入一些常用的DSL了。
之前的返回資料都是返回文件的所有內容,這種對於網路的開銷肯定是有影響的,下面的例子就指定了返回特定的欄位:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_all": {} }, "_source": ["account_number", "balance"] }'
再回到query,之前的查詢都是查詢所有的文件,並不能稱之為搜尋引擎。下面就通過match方式查詢特定欄位的特定內容,比如查詢餘額為20的賬戶資訊:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match": { "account_number": 20 } } }'
查詢地址為mill的資訊:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match": { "address": "mill" } } }'
查詢地址為mill或者lane的資訊:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match": { "address": "mill lane" } } }'
如果我們想要返回同時包含mill和lane的,可以通過match_phrase查詢:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "match_phrase": { "address": "mill lane" } } }'
ES提供了bool查詢,可以把很多小的查詢組成一個更為複雜的查詢,比如查詢同時包含mill和lane的文件:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }'
修改bool引數,可以改為查詢包含mill或者lane的文件:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }'
也可以改寫為must_not,排除包含mill和lane的文件:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } }'
bool查詢可以同時使用must, should, must_not組成一個複雜的查詢:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } }'
過濾查詢
之前說過score欄位指定了文件的分數,使用查詢會計算文件的分數,最後通過分數確定哪些文件更相關,返回哪些文件。
有的時候我們可能對分數不感興趣,就可以使用filter進行過濾,它不會去計算分值,因此效率也就更高一些。
filter過濾可以巢狀在bool查詢內部使用,比如想要查詢在2000-3000範圍內的所有文件,可以執行下面的命令:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } }'
ES除了上面介紹過的範圍查詢range、match_all、match、bool、filter還有很多其他的查詢方式,這裡就先不一一說明了。
聚合
聚合提供了使用者進行分組和數理統計的能力,可以把聚合理解成SQL中的GROUP BY和分組函式。在ES中,你可以在一次搜尋查詢的時間內,即完成搜尋操作也完成聚合操作,這樣就降低了多次使用REST API造成的網路開銷。
下面就是通過terms聚合的簡單樣例:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state" } } } }'
它類似於SQL中的下面的語句:
SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC
返回的資料:
"hits" : { "total" : 1000, "max_score" : 0.0, "hits" : [ ] }, "aggregations" : { "group_by_state" : { "buckets" : [ { "key" : "al", "doc_count" : 21 }, { "key" : "tx", "doc_count" : 17 }, { "key" : "id", "doc_count" : 15 }, { "key" : "ma", "doc_count" : 15 }, { "key" : "md", "doc_count" : 15 }, { "key" : "pa", "doc_count" : 15 }, { "key" : "dc", "doc_count" : 14 }, { "key" : "me", "doc_count" : 14 }, { "key" : "mo", "doc_count" : 14 }, { "key" : "nd", "doc_count" : 14 } ] } } }
由於size設定為0,它並沒有返回文件的資訊,只是返回了聚合的結果。
比如統計不同賬戶狀態下的平均餘額:
curl -XPOST 'localhost:9200/bank/_search?pretty' -d ' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } }'
聚合支援巢狀,舉個例子,先按範圍分組,在統計不同性別的賬戶餘額:
curl -XPOST 'localhost:9200/bank/_search?pretty' -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" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } } }'
聚合可以實現很多複雜的功能,而且ES也提供了很多複雜的聚合,這裡作為引導篇,也不過多介紹了。
對於基本的資料搜尋大致就是上面講述的樣子,熟悉了一些常用的API,入門還是很簡單的,倒是要熟練使用ES,還是需要掌握各種搜尋查詢的命令,以及ES內部的原理。