上篇文章向讀者介紹了Elasticsearch中修改數據的操作,使用了Elasticsearch提供的一整套強大的REST API,本文繼續來看通過這一套API如何完成文檔的基本操作。
本文是Elasticsearch系列的第四篇,閱讀前面的文章,有助於更好的理解本文:
1.elasticsearch安裝與配置 2.初識elasticsearch中的REST接口 3.elasticsearch修改數據
加載樣本數據
爲了完成後面的操作,我們首先需要一些樣本數據,這個樣本數據可以在http://www.json-generator.com/網站上自動生成,也可以直接下載已經生成好的,下載地址https://github.com/elastic/elasticsearch/blob/master/docs/src/test/resources/accounts.json?raw=true。
將下載下來的JSON數據放到當前用戶目錄下,然後執行如下命令,將數據導入到Elasticsearch中,如下:
curl -H "Content-Type: application/json" -XPOST "localhost:9200/bank/_doc/_bulk?pretty&refresh" --data-binary "@accounts.json"
小貼士:
這裏實際上就是使用了上文提到的批量操作,只不過數據變爲了一個本地的JSON文件而已。
注意,上面這行命令在執行過程中,可能會報如下錯誤:
The bulk request must be terminated by a newline [\n]
這是因爲下載的accounts.json文件少了一個換行符,在下載的accounts.json文件最末尾,按下一個回車即可。
上面的命令執行完後,可以通過如下命令查看數據是否導入成功:
curl "localhost:9200/_cat/indices?v"
查看結果如下:
可以看到,成功的添加了1000個文檔到bank索引中。
搜索API
整體來說,搜索條件既可以放在URL中,也可以放在REST請求體中,一般來說建議採用第二種方案,但是爲了知識的完整性,這裏對兩種方案都予以介紹。
搜索條件在地址欄中
請求如下:
curl -X GET "localhost:9200/bank/_search?q=*&sort=account_number:desc&pretty"
請求解釋:
- q=* 表示搜索所有文檔。
- sort=accountnumber:desc表示請求結果按照accountnumber字段的值倒敘排序。
- pretty則表示將響應的JSON格式化,方便閱讀。
執行結果如下(部分):
響應字段解釋:
- took表示搜索耗時,單位爲毫秒。
- timed_out表示搜索是否超時。
- _shards表示有多少個分片被搜索了,成功搜索的分片數量、跳過的分片數量以及失敗的分片數量。
- hits表示搜索結果。
- hits.total表示搜索到的文檔總數量。
- hits.hits表示搜索到的文檔數組,默認顯示搜索到的前十個文檔。
搜索條件在REST請求體中
上面介紹的這種搜索條件在URL中,搜索條件也可以放在REST請求體中,下面這個請求等同於上面的請求:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "desc" } ] } '
這裏通過一個JSON請求體來描述查詢條件,接下來向讀者詳細介紹這個JSON格式的查詢條件。
查詢語言介紹
Elasticsearch提供了一套JSON風格的查詢條件格式,這稱爲DSL。DSL非常全面,雖然看起來可能有點難以理解,不過我們可以先從幾個簡單的案例來開始這個東西的學習。
回顧我們上文的查詢條件:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "desc" } ] } '
在上面這個請求中, query
表示查詢的定義, match_all
表示查詢指定索引下的所有文檔。 sort
則是一個排序字段,表示根據 account_number
字段的值降序排列。
除了這些參數外,我們還可以指定返回的文檔個數:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "desc" } ], "size": 1 } '
這個表示返回的文檔個數爲1,執行結果如下:
此時就只返回了一個文檔,如果不指定的話,size默認爲10。
也可以指定從第幾個文檔開始查詢,類似於SQL中的分頁,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "desc" } ], "size": 1, "from": 10 } '
這個表示從第10個開始查詢,查詢1一個出來,如下:
如果不指定from,則默認值爲0。
執行搜索
通過上面一小節,讀者對基本的查詢已經有所瞭解,接下來再來看看查詢中其他的一些細節。
自定義返回字段
默認情況下,查詢結果中會返回查詢文檔的所有字段,如果不需要返回所有字段,則可以自定義返回字段,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_all": {} }, "sort": [ { "account_number": "desc" } ], "size": 2, "from": 10, "_source": ["account_number","balance"] } '
此時執行結果如下:
可以看到,查詢結果中的 _source
字段中的值已經發生了變化。
match
前面我們已經用過 match_all
表示查詢指定索引下的所有文檔,我們也可以使用 match
來指定查詢條件,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match": { "account_number": 20 } } } '
這個表示查詢 account_number
的值爲20的文檔。
his example returns all accounts containing the term "mill" in the address:
下面這個表示查詢address中包含詞語(term)“mill”的所有賬戶:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill" } } } '
如下則表示查詢address中包含詞語(term)“mill”或者“lane”的所有賬戶:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match": { "address": "mill lane" } } } '
match_phrase
match_phrase表示短語查詢,如下查詢表示查詢address中包含“mill lane”的所有文檔,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "match_phrase": { "address": "mill lane" } } } '
bool查詢
bool查詢允許用戶將幾個較小的查詢條件,通過bool中的邏輯運算,組合成一個較大的查詢條件,如下查詢表示查詢address中包含“mill”和“lane”的所有文檔:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
上面案例中的must相當於and,而下面的should則相當於or(即查詢的文檔,只要包含的兩個條件中一個符合,則該文檔會被認爲是符合條件的),如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "bool": { "should": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
如下查詢則表示查詢address中既不包含“mill”,又不包含“lane”的所有文檔,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must_not": [ { "match": { "address": "mill" } }, { "match": { "address": "lane" } } ] } } } '
也可以在一個查詢中一起使用must、must_not、should,如下查詢表示查詢age爲40,並且state不爲ID的所有賬戶:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": [ { "match": { "age": "40" } } ], "must_not": [ { "match": { "state": "ID" } } ] } } } '
執行過濾器
在前面的小節中,我們跳過了搜索結果中的_score字段,這個字段用來描述搜索結果的匹配度,得分越高,文檔匹配度越高,得分越低,文檔的匹配度越低。在Elasticsearch中,所有的查詢操作都會觸發匹配度得分的計算,但是並非所有的查詢都需要獲取匹配度這個參數,對於那些我們不需要匹配度得分的搜索中(例如僅僅只是想過濾文檔集),可以使用Elasticsearch提供的另一種查詢功能---過濾器。過濾器在概念上類似於查詢,但是執行速度高於查詢,之所以查詢速度高,有如下兩個原因:
- 過濾器不會計算相關度的得分
- 過濾器可以被緩存到內存中,在重複搜索時,速度會比較快
如下案例表示查詢賬戶餘額介於[20000,30000]之間的所有賬戶(查詢條件和過濾條件也都可以自己定義):
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "query": { "bool": { "must": { "match_all": {} }, "filter": { "range": { "balance": { "gte": 20000, "lte": 30000 } } } } } } '
在查詢時,究竟是使用過濾器還是使用查詢,要考慮是否關注結果中的 _score
字段。
執行聚合
聚合操作有點類似於我們在SQL中的聚合函數,開發者可以通過聚合操作,在一個查詢結果中同時返回查詢到的數據和聚合之後的結果,例如,按照state中的關鍵字對用戶進行分組,然後按照分組後state的關鍵字數量進行排序,並展示出排名最高的前十個,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "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;
請求執行結果如下:
注意,在這個查詢中,因爲我們將size設置爲0,因此只能看到聚合的結果,而沒有查詢結果。
另外,這種聚合操作還可以互相嵌套,如下表示計算每個state賬戶的平均存款並列出前10個:
curl -X GET "localhost:9200/bank/_search?pretty" -H 'Content-Type: application/json' -d' { "size": 0, "aggs": { "group_by_state": { "terms": { "field": "state.keyword" }, "aggs": { "average_balance": { "avg": { "field": "balance" } } } } } } '
還可以按照平均餘額進行排序,如下:
curl -X GET "localhost:9200/bank/_search?pretty" -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" } } } } } } '
如下請求則表示使用首先使用年齡段進行分組 (ages 20-29, 30-39, and 40-49),然後在此基礎上再使用性別進行分組,最後再計算不同年齡段的不同性別用戶的賬戶餘額平均數:
curl -X GET "localhost:9200/bank/_search?pretty" -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" } } } } } } } } '
本文介紹的只是一些非常簡單的常規操作,在後面的文章中,還會就這裏的每一個話題和小夥伴們詳細分享其細節。
好了,本文就先到這裏,有問題歡迎留言討論。