寫在前面:我是「且聽風吟」,目前是一名大數據開發工程師,熱愛大數據開源技術,喜歡分享自己的所學所悟,現階段正在從頭梳理大數據體系的知識,以後將會把時間重點放在Spark和Flink上面。
如果你也對大數據感興趣,希望在這個行業一展拳腳。歡迎關注我,我們一起努力,一起學習。博客地址:https://ropledata.blog.csdn.net
博客的名字來源於:且聽風吟,靜待花開。也符合我對技術的看法,想要真正掌握一門技術就需要厚積薄發的毅力,同時保持樂觀的心態。
你只管努力,剩下的交給時間!
文章目錄
一、前言
本文版本說明:
- ElasticSearch版本:7.7 (目前最新版)
- Kibana版本:7.7(目前最新版)
前面兩篇文章咱們已經對Elasticsearch進行了精細的講解,第一篇圍繞Elasticsearch最新版進行了上萬字的詳細解析,相信看過的朋友對Elasticsearch及kibana等工具的極速安裝配置印象深刻,也至少會對Elasticsearch有一個入門的掌握。第二篇主要圍繞Elasticsearch的分詞器進行講解,並重點分析了ik中文分詞器。
前文鏈接:
本文咱們將對Elasticsearch原生的RESTful API操作進行詳盡的歸納分析,並會對複雜的常用查詢知識點進行一一舉例展開,重點會對DSL查詢,聚合查詢,批量操作等進行舉例解析,並會提供一些Elasticsearch的使用技巧。相信學會了這些知識和技巧之後,以後在工作中不管應對多麼複雜的場景,都可以得心應手,迅速的根據RESTful API寫出完美的代碼。好了,廢話不多說,讓我們開始吧!
注意:下文咱們把ElasticSearch簡稱爲ES,對大家可能出現的疑問進行標紅並解釋,並會對容易混淆的地方加以聲明。
二、索引操作
2.1、創建索引
比如咱們創建一個3副本2分片的名爲ropledata的索引:
PUT /ropledata
{
"settings": {
"number_of_shards": "2",
"number_of_replicas": "3"
}
}
2.2、刪除索引
和刪除數據庫一樣,索引也是可以刪除的,只需要執行如下命令就可以刪除名爲ropledata的索引:
DELETE /ropledata
2.3、修改索引副本數
這裏要注意,索引的分片是不允許修改的,咱們只能修改索引的副本數量,比如想把副本數量修改爲2個,只需要執行:
PUT ropledata/_settings
{
"number_of_replicas" : "2"
}
三、基礎增刪改查
3.1、插入數據
咱們平時進行基礎的數據插入時,可以分爲兩種情況。一種是指定文檔的id,一種是不指定。
注意:不指定的時候,ES會幫我們自動生成,不過不容易記憶,因此推薦指定id的方式插入數據。
疑問一:這裏說的id是大括號裏面的id嗎?
不是的,這點容易混淆,這裏包括後面查詢或者刪除時候用到的ID是創建文檔時候指定或者ES自動生成的那個id,那個是唯一id,也就是下面示例裏的101,而不是文檔裏面大括號的那個叫
id
字段!文檔裏面的文檔字段是可以沒有id
的。
-
不指定id
POST /ropledata/_doc/ { "id":1, "name":"且聽_風吟", "page":"https://ropledata.blog.csdn.net", "say":"歡迎點贊,收藏,關注,一起學習" }
-
指定id
POST /ropledata/_doc/101 { "id":1, "name":"且聽_風吟", "page":"https://ropledata.blog.csdn.net", "say":"歡迎點贊,收藏,關注,一起學習" }
3.2、刪除數據
如果咱們想刪除剛纔創建的ropledata索引下的id爲101的文檔,可以使用如下命令:
DELETE /ropledata/_doc/101
3.3、更新數據
這裏大家要特別注意,ES裏的文檔是不可以修改的,但是可以覆蓋,所以ES修改數據本質上是對文檔的覆蓋。
ES對數據的修改分爲全局更新和局部更新,下面咱們進行對比說明:
-
全局更新
PUT /ropledata/_doc/101 { "id":1, "name":"且聽_風吟", "page":"https://ropledata.blog.csdn.net", "say":"再次歡迎點贊,收藏,關注,一起學習" }
然後大家可以多全局更新幾次,會發現每次全局更新之後這個文檔的
_version
都會發生改變!
-
局部更新
POST /ropledata/_update/101 { "doc": { "say":"奧力給" } }
這時候我們可以多次去執行上面的局部更新代碼,會發現除了第一次執行,後續不管又執行了多少次,
_version
都不再變化!
疑問二:局部更新的時候ES底層的流程是怎樣的?和全局更新相比性能怎麼樣?局部更新的底層流程:
- 內部先獲取到對應的文檔;
- 將傳遞過來的字段更新到文檔的json中(這一步實質上也是一樣的);
- 將老的文檔標記爲deleted(到一定時候纔會物理刪除);
- 將修改後的新的文檔創建出來。
性能對比:
- 全局更新本質上是替換操作,即使內容一樣也會去替換;
- 局部更新本質上是更新操作,只有遇到新的東西才更新,沒有新的修改就不更新;
- 局部更新比全局更新的性能好,因此推薦使用局部更新。
3.4、基礎查詢數據
3.4.1、搜索全部數據(默認展示10條數據)
-
GET全局搜索數據:
GET /ropledata/_search
-
match_all全局搜索數據,可以加各種條件,比如排序:
POST /ropledata/_search { "query": { "match_all": {} }, "sort": [ { "id": { "order": "asc" } } ] }
以match_all爲例,查詢後的結果如下:
疑問三:查詢出來的字段都是什麼含義呢?
- took:Elasticsearch運行查詢需要多長時間(以毫秒爲單位);
- timed_out :搜索請求是否超時 ;
- _shards 搜索了多少碎片,並對多少碎片成功、失敗或跳過進行了細分;
- _max_score 找到最相關的文檔的得分;
- hits.total.value :找到了多少匹配的文檔;
- hits.sort :文檔排序後的位置(比如上面查詢的1,2,3…) ;
- hits._score:文檔的相關性評分(在使用match_all時不適用)
3.4.2、指定文檔id搜索數據:
GET /ropledata/_doc/101
3.4.3、根據關鍵字搜索數據
比如咱們想查找ropledata這個索引下,name字段爲**“且聽風吟,靜待花開”**的數據:
GET /ropledata/_search?q=name:"且聽風吟,靜待花開"
3.4.4、高亮顯示數據
注意:使用高亮查詢,會對要查詢的數據進行分詞搜索!
有時候我們想把查詢到的數據進行高亮顯示,比如查到“且聽風吟”後,想把name這個字段的數據高亮顯示,可以用如下的方式:
POST /ropledata/_search
{
"query": {
"match": {
"name": "且聽風吟"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
查詢後,可以得到如下結果:
我們可以發現,會查詢到分詞後的“且聽風吟”,同時會把name個字段分詞後的數據進行高亮強調顯示。注:這裏的"<em></em>"標籤在html裏有強調文本的作用,支持所有的瀏覽器!
四、DSL查詢
疑問四:DSL是啥意思呢?怎麼通俗點解釋呢?
簡單來說,DSL就是ES的一種查詢方式,DSL基於JSON實現了直觀簡單的結構化查詢功能。由於DSL查詢是JSON格式的,所以更加的靈活,而且可以同時包含查詢和過濾器,咱們可以很輕鬆的構造出複雜的查詢功能。
好,廢話不多說,咱們把常用的關鍵點逐一展開,最後再來個複雜的查詢玩玩:
-
term查詢
term需要完全匹配,不會對詞彙進行分詞器分析。主要用於查詢精確匹配的值,比如數字,日期,布爾值或未經分析的文本數據類型的字符串 (not_analyzed)。
比如咱們查詢
id
字段爲9的數據(注意:這裏的id不是文檔id,是文檔裏的咱們自己命名爲id的字段):POST /ropledata/_search { "query": { "term": { "id": 9 } } }
-
terms查詢
terms和term 有點類似,但 terms 允許指定多個匹配條件。 如果某個字段指定了多個值,那麼文檔需要一起去做匹配。
比如咱們查詢
id
字段爲9和5的數據:POST /ropledata/_search { "query": { "terms": { "id": [5,9] } } }
-
range查詢
range 主要用於過濾,通常用於按照指定範圍查找一批數據,咱們需要記憶如下四個關鍵字的含義:
- gt :大於
- gte : 大於等於
- lt :小於
- lte : 小於等於
記憶小技巧:gt可以理解爲高to,也就是高於,大於的意思。lt可以理解爲little to,也就是小於的意思。然後加上e就附加一個等於。
比如咱們想要查詢
id
字段大於等於5且小於10的數據:POST /ropledata/_search { "query": { "range": { "id": { "gte": 5, "lt": 10 } } } }
-
exists查詢
exists查詢類似sql裏的is null條件,通常用於查找文檔中是否包含指定字段,包含這個字段就返回返回這條數據。
比如咱們想查詢ropledata這個索引下,包含
hobby
這個字段的數據:POST /ropledata/_search { "query": { "exists": { "field": "hobby" } } }
-
match查詢
match 查詢是一個標準查詢,不管是全文本查詢還是精確查詢基本上都要用到它。所以非常非常重要,一定要掌握。
在使用 match 查詢一個全文本字段時,它會在真正查詢之前用分析器先分析match一下查詢字符;如果用 match 下指定了一個確切值, 在遇到數字,日期,布爾值或者 not_analyzed 的字符串時,它將爲你搜索你給定的值。
比如咱們查詢
hobby
是run的數據:POST /ropledata/_search { "query": { "match": { "hobby": "run" } } }
-
match_phrase查詢
疑問五:match_phrase和match有啥區別呢?
match_phrase和match類似,在查詢時都會對查詢詞進行分詞,但是match會忽略查詢詞的空格,而match_phrase不會。因此需要注意的是:查詢包含空格的字符串,要用match_phrase!
比如咱們查詢
hobby
是music and movie的數據(注意:這裏要查詢的數據包含空格):POST /ropledata/_search { "query": { "match_phrase": { "hobby": "music and movie" } } }
-
bool查詢
bool 查詢可以用來合併多個條件查詢結果的布爾邏輯,咱們需要記憶如下操作符關鍵字:
- must:多個查詢條件的完全匹配,相當 於 and;
- must_not:多個查詢條件的相反匹配,相當於 not;
- should:至少有一個查詢條件匹配, 相當於 or。
注意:這些參數可以分別繼承一個查詢條件或者一個查詢條件的數組。
比如咱們想查詢
hobby
必須是run,id
必須不是4,name
可以是一起學習或者且聽風吟的數據:POST /ropledata/_search { "query": { "bool": { "must": { "term": { "hobby": "run" } }, "must_not": { "term": { "id": 4 } }, "should": [ { "term": { "name": "且聽風吟" } }, { "term": { "name": "一起學習" } } ] } }
-
filter查詢
filter用於過濾查詢,通常和bool連用,就像編程語言一樣,用於過濾數據。
比如咱們想查詢
hobby
爲music的用戶:POST /ropledata/_search { "query": { "bool": { "filter": { "term": { "hobby": "music" } } } } }
-
融合在一起使用
其實常用的就是上面這些,它們一般是融合在一起使用的。比如咱們要查詢
id
必須不是4,name
包含且聽風吟,同時id
大於等於2且小於10的數據:POST /ropledata/_search { "query": { "bool": { "filter": { "range": { "id": { "gte": 2, "lt": 10 } } }, "must_not": { "term": { "id": 4 } }, "must": { "match": { "name": "且聽風吟" } } } } }
五、聚合查詢
學會了非常實用的DSL查詢,下面咱們再看看聚合查詢。
-
常用數學統計函數
首先咱們需要了解幾個非常常用的數學統計函數:
- avg:平均值
- max:最大值
- min:最小值
- sum:求和
比如咱們求
id
的平均值,有兩種寫法,基礎寫法和腳本寫法:基礎寫法:
POST /ropledata/_search { "aggs": { "ropledata": { "avg": { "field": "id" } } }, "size": 0 }
腳本寫法:
POST /ropledata/_search { "aggs": { "ropledata": { "avg": { "script": { "source": "doc.id.value" } } } }, "size": 0 }
疑問六:求平均值或求和時,爲什麼要加(“size”: 0)呢?
size用來控制返回多少數據,由於咱們是想要在所有文檔裏求平均值和求和,所以要用size來控制返回一個數據即可,不然ES還會默認返回10條數據。
-
cardinality去查
涉及到聚合查詢的場景,當然少不了去重了,ES提供了cardinality去重統計函數來解決這個問題。
比如咱們想根據
id
字段去重統計:POST /ropledata/_search { "aggs": { "ropledata": { "cardinality": { "field": "id" } } }, "size": 0 }
-
value_count計數統計
有時候咱們會遇到讓統計個數的場景,這時候就可以使用value_count來解決了。
比如咱們要統計有多少條數據:
POST /ropledata/_search { "aggs": { "ropledata": { "value_count": { "field": "id" } } }, "size": 0 }
-
terms詞聚合
terms詞聚合可以基於給定的字段,並按照這個字段對應的每一個數據爲一個桶,然後計算每個桶裏的文檔個數。默認會按照文檔的個數排序。
比如咱們要根據
id
字段進行詞聚合:POST /ropledata/_search { "aggs": { "ropledata": { "terms": { "field": "id" } } } }
-
top_hits聚合
咱們使用sql時可以很方便的處理top問題,ES也提供了對應的支持,top_hits就是這樣的函數,一般和terms連用,可以獲取到每組前n條數據。
比如咱們想根據
id
分組,然後拿到前6條數據:POST /ropledata/_search { "aggs": { "ropledata": { "terms": { "field": "id" }, "aggs": { "count": { "top_hits": { "size": 6 } } } } }, "size": 0 }
-
range範圍查詢
在咱們日常進行數據統計時,控制數據的範圍是必不可少的,除了前面DSL查詢時介紹的gt,lt函數,其實在聚合查詢裏還提供了range用來進行範圍查詢。
比如咱們想查詢
id
字段的值在6-9之間和10-20之間的文檔有多少:POST /ropledata/_search { "aggs": { "group_by_id": { "range": { "field": "id", "ranges": [ { "from": 6, "to": 9 }, { "from": 10, "to": 20 } ] } } }, "size": 0 }
六、批量操作
作爲一個存儲系統,對數據的批量增刪改查自然也是必不可少的!ES也提供了批量的操作,具體的用法如下。
-
批量插入
POST _bulk { "create" : { "_index" : "ropledata", "_id" : "1009" } } {"id":9,"name": "且聽風吟,靜待花開","hobby": "music and movie"} { "create" : { "_index" : "ropledata", "_id" : "1010" } } {"id":10,"name": "且聽_風吟","hobby": "music"} { "create" : { "_index" : "ropledata", "_id" : "1011" } } {"id":11,"name": "大數據領域","hobby": "movie"} { "create" : { "_index" : "ropledata", "_id" : "1012" } } {"id":12,"name": "一起學習","hobby": "run"}
-
批量查詢
比如咱們想批量查詢ropledata這個索引下文檔id爲1010,1011,1012的文檔數據,可以這樣寫:
POST /ropledata/_mget { "ids": [ "1010", "1011", "1012" ] }
-
批量更新
如果咱們想批量修改1011和1012的文檔裏的name字段的值,可以這樣寫:
POST _bulk { "update" : {"_id" : "1011", "_index" : "ropledata"} } { "doc" : {"name" : "批量修改"} } { "update" : {"_id" : "1012", "_index" : "ropledata"} } { "doc" : {"name" : "大家好"}}
-
批量刪除
如果咱們想批量刪除文檔id爲1011和1012的文檔,可以這樣寫:
POST _bulk { "delete" : { "_index" : "ropledata", "_id" : "1011" } } { "delete" : { "_index" : "ropledata", "_id" : "1012" } }
七、實用騷操作
7.1、瀏覽器查詢結果美化
咱們之前介紹的所有的操作,都是在kibana裏輸入的,裏面可以對輸入的命令和輸出的結果進行格式化。但是如果咱們直接使用瀏覽器進行查詢時,輸出的查詢結果會亂成一團,那麼怎麼去美化呢?
別急,只要在查詢的最後加上pretty參數,就可以了:
http://127.0.0.1:9200/ropledata/_doc/1001?pretty
查詢出來的結果就會被美化了:
7.2、指定返回的字段
咱們有時候不需要返回整個文檔所有的字段,只想要查看其中的一個或者多個字段,這時候ES也提供的有方法,只需要在最後使用**_source**參數,並傳遞想要返回的字段就可以了。
http://127.0.0.1:9200/ropledata/_doc/1001?pretty&_source=id,name
7.3、不顯示元數據
咱們查詢的時候,會返回一大堆數據,上面那些稱爲元數據,那不需要的時候,怎麼去掉呢?別急,只需要把_doc
換成_source
就可以了。
http://127.0.0.1:9200/ropledata/_source/1001?pretty&_source=id,name
7.4、查看文檔是否存在
有時候咱們爲了防止報錯,在查詢之前,需要查看這個文檔是否存在,這時候只需要用到HEAD
關鍵字就可以了。
HEAD /ropledata/_doc/1001
7.5、分頁查詢
對於海量存儲的數據,有時候咱們需要分頁查看。ES提供了size和from兩個參數,size代表每頁的個數,默認是10個,from代表從第幾個獲取。咱們只要在代碼裏寫清楚這兩個參數,就可以實現翻譯了。
http://127.0.0.1:9200/ropledata/_search?size=1&from=2&pretty
八、總結
本文圍繞ES最新版本,從常用基礎操作、DSL查詢、聚合查詢、批量操作,實用技巧等方面進行了詳細的舉例解析,並對讀者可能出現的疑惑進行了標紅疑問總結(本文共總結了六處容易產生的疑問點),對需要注意的地方也進行了單獨的聲明,希望對大家學習ES提供幫助!
注意:如果我的文章對您有所幫助,歡迎關注點贊收藏,如果您有疑惑或發現文中有不對的地方,還請不吝賜教,非常感謝!!