最清晰易懂的Elasticsearch操作手冊|收藏夾必備

寫在前面:我是「且聽風吟」,目前是一名大數據開發工程師,熱愛大數據開源技術,喜歡分享自己的所學所悟,現階段正在從頭梳理大數據體系的知識,以後將會把時間重點放在Spark和Flink上面。

如果你也對大數據感興趣,希望在這個行業一展拳腳。歡迎關注我,我們一起努力,一起學習。博客地址:https://ropledata.blog.csdn.net

博客的名字來源於:且聽風吟,靜待花開。也符合我對技術的看法,想要真正掌握一門技術就需要厚積薄發的毅力,同時保持樂觀的心態。

你只管努力,剩下的交給時間!

在這裏插入圖片描述

一、前言

本文版本說明:

  1. ElasticSearch版本:7.7 (目前最新版)
  2. Kibana版本:7.7(目前最新版)

前面兩篇文章咱們已經對Elasticsearch進行了精細的講解,第一篇圍繞Elasticsearch最新版進行了上萬字的詳細解析,相信看過的朋友對Elasticsearch及kibana等工具的極速安裝配置印象深刻,也至少會對Elasticsearch有一個入門的掌握。第二篇主要圍繞Elasticsearch的分詞器進行講解,並重點分析了ik中文分詞器。

前文鏈接

  1. ElasticSearch最新版快速入門詳解
  2. ElasticSearch中文分詞,看這一篇就夠了

本文咱們將對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 的。

  1. 不指定id

    POST /ropledata/_doc/
    {
      "id":1,
      "name":"且聽_風吟",
      "page":"https://ropledata.blog.csdn.net",
      "say":"歡迎點贊,收藏,關注,一起學習" 
    }
    
  2. 指定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對數據的修改分爲全局更新局部更新,下面咱們進行對比說明:

  1. 全局更新

    PUT /ropledata/_doc/101
    { 
      "id":1,
      "name":"且聽_風吟",
      "page":"https://ropledata.blog.csdn.net",
      "say":"再次歡迎點贊,收藏,關注,一起學習" 
    }
    

    然後大家可以多全局更新幾次,會發現每次全局更新之後這個文檔的_version都會發生改變!
    在這裏插入圖片描述

  2. 局部更新

    POST /ropledata/_update/101 
    {
      "doc":
      {
        "say":"奧力給"
      } 
    }
    

    這時候我們可以多次去執行上面的局部更新代碼,會發現除了第一次執行,後續不管又執行了多少次,_version都不再變化!
    在這裏插入圖片描述
    疑問二:局部更新的時候ES底層的流程是怎樣的?和全局更新相比性能怎麼樣?

    局部更新的底層流程:

    1. 內部先獲取到對應的文檔;
    2. 將傳遞過來的字段更新到文檔的json中(這一步實質上也是一樣的);
    3. 將老的文檔標記爲deleted(到一定時候纔會物理刪除);
    4. 將修改後的新的文檔創建出來。

    性能對比:

    1. 全局更新本質上是替換操作,即使內容一樣也會去替換;
    2. 局部更新本質上是更新操作,只有遇到新的東西才更新,沒有新的修改就不更新;
    3. 局部更新比全局更新的性能好,因此推薦使用局部更新。

3.4、基礎查詢數據

3.4.1、搜索全部數據(默認展示10條數據)
  1. GET全局搜索數據:

    GET /ropledata/_search
    
  2. match_all全局搜索數據,可以加各種條件,比如排序:

    POST /ropledata/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "id": {
            "order": "asc"
          }
        }
      ]
    }
    

    以match_all爲例,查詢後的結果如下:
    在這裏插入圖片描述

    疑問三:查詢出來的字段都是什麼含義呢?

    1. took:Elasticsearch運行查詢需要多長時間(以毫秒爲單位);
    2. timed_out :搜索請求是否超時 ;
    3. _shards 搜索了多少碎片,並對多少碎片成功、失敗或跳過進行了細分;
    4. _max_score 找到最相關的文檔的得分;
    5. hits.total.value :找到了多少匹配的文檔;
    6. hits.sort :文檔排序後的位置(比如上面查詢的1,2,3…) ;
    7. 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格式的,所以更加的靈活,而且可以同時包含查詢和過濾器,咱們可以很輕鬆的構造出複雜的查詢功能。

好,廢話不多說,咱們把常用的關鍵點逐一展開,最後再來個複雜的查詢玩玩:

  1. term查詢

    term需要完全匹配,不會對詞彙進行分詞器分析。主要用於查詢精確匹配的值,比如數字,日期,布爾值或未經分析的文本數據類型的字符串 (not_analyzed)。

    比如咱們查詢id字段爲9的數據(注意:這裏的id不是文檔id,是文檔裏的咱們自己命名爲id的字段):

    POST /ropledata/_search
    {
      "query": {
        "term": {
          "id": 9
        }
      }
    }
    
  2. terms查詢

    terms和term 有點類似,但 terms 允許指定多個匹配條件。 如果某個字段指定了多個值,那麼文檔需要一起去做匹配。

    比如咱們查詢id字段爲9和5的數據:

    POST /ropledata/_search
    {
      "query": {
        "terms": {
          "id": [5,9]
        }
      }
    }
    
  3. range查詢

    range 主要用於過濾,通常用於按照指定範圍查找一批數據,咱們需要記憶如下四個關鍵字的含義:

    1. gt :大於
    2. gte : 大於等於
    3. lt :小於
    4. lte : 小於等於

    記憶小技巧:gt可以理解爲高to,也就是高於,大於的意思。lt可以理解爲little to,也就是小於的意思。然後加上e就附加一個等於。

    比如咱們想要查詢id字段大於等於5且小於10的數據:

    POST /ropledata/_search
    {
      "query": {
        "range": {
          "id": {
            "gte": 5,
            "lt": 10
          }
        }
      }
    }
    
  4. exists查詢

    exists查詢類似sql裏的is null條件,通常用於查找文檔中是否包含指定字段,包含這個字段就返回返回這條數據。

    比如咱們想查詢ropledata這個索引下,包含hobby這個字段的數據:

    POST /ropledata/_search
    {
      "query": {
        "exists": {
          "field": "hobby"
        }
      }
    }
    
  5. match查詢

    match 查詢是一個標準查詢,不管是全文本查詢還是精確查詢基本上都要用到它。所以非常非常重要,一定要掌握。

    在使用 match 查詢一個全文本字段時,它會在真正查詢之前用分析器先分析match一下查詢字符;如果用 match 下指定了一個確切值, 在遇到數字,日期,布爾值或者 not_analyzed 的字符串時,它將爲你搜索你給定的值。

    比如咱們查詢hobbyrun的數據:

    POST /ropledata/_search
    {
      "query": {
        "match": {
          "hobby": "run"
        }
      }
    }
    
  6. match_phrase查詢

    疑問五:match_phrase和match有啥區別呢?

    match_phrase和match類似,在查詢時都會對查詢詞進行分詞,但是match會忽略查詢詞的空格,而match_phrase不會。因此需要注意的是:查詢包含空格的字符串,要用match_phrase!

    比如咱們查詢hobbymusic and movie的數據(注意:這裏要查詢的數據包含空格):

    POST /ropledata/_search
    {
      "query": {
        "match_phrase": {
          "hobby": "music and movie"
        }
      }
    }
    
  7. bool查詢

    bool 查詢可以用來合併多個條件查詢結果的布爾邏輯,咱們需要記憶如下操作符關鍵字:

    1. must:多個查詢條件的完全匹配,相當 於 and;
    2. must_not:多個查詢條件的相反匹配,相當於 not;
    3. should:至少有一個查詢條件匹配, 相當於 or。

    注意:這些參數可以分別繼承一個查詢條件或者一個查詢條件的數組。

    比如咱們想查詢hobby必須是runid必須不是4name可以是一起學習或者且聽風吟的數據:

    POST /ropledata/_search
    {
      "query": {
        "bool": {
          "must": {
            "term": {
              "hobby": "run"
            }
          },
          "must_not": {
            "term": {
              "id": 4
            }
          },
          "should": [
            {
              "term": {
                "name": "且聽風吟"
              }
            },
            {
              "term": {
                "name": "一起學習"
              }
            }
          ]
        }
      }
    
  8. filter查詢

    filter用於過濾查詢,通常和bool連用,就像編程語言一樣,用於過濾數據。

    比如咱們想查詢hobbymusic的用戶:

    POST /ropledata/_search
    {
      "query": {
        "bool": {
          "filter": {
            "term": {
              "hobby": "music"
            }
          }
        }
      }
    }
    
  9. 融合在一起使用

    其實常用的就是上面這些,它們一般是融合在一起使用的。比如咱們要查詢id必須不是4name包含且聽風吟,同時id大於等於2且小於10的數據:

    POST /ropledata/_search
    {
      "query": {
        "bool": {
          "filter": {
            "range": {
              "id": {
                "gte": 2,
                "lt": 10
              }
            }
          },
          "must_not": {
            "term": {
              "id": 4
            }
          },
          "must": {
            "match": {
              "name": "且聽風吟"
            }
          }
        }
      }
    }
    


五、聚合查詢

學會了非常實用的DSL查詢,下面咱們再看看聚合查詢。

  1. 常用數學統計函數

    首先咱們需要了解幾個非常常用的數學統計函數:

    1. avg:平均值
    2. max:最大值
    3. min:最小值
    4. 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條數據。

  2. cardinality去查

    涉及到聚合查詢的場景,當然少不了去重了,ES提供了cardinality去重統計函數來解決這個問題。

    比如咱們想根據id字段去重統計:

    POST /ropledata/_search
    {
      "aggs": {
        "ropledata": {
          "cardinality": {
            "field": "id"
          }
        }
      },
      "size": 0
    }
    
  3. value_count計數統計

    有時候咱們會遇到讓統計個數的場景,這時候就可以使用value_count來解決了。

    比如咱們要統計有多少條數據:

    POST /ropledata/_search
    {
      "aggs": {
        "ropledata": {
          "value_count": {
            "field": "id"
          }
        }
      },
      "size": 0
    }
    
  4. terms詞聚合

    terms詞聚合可以基於給定的字段,並按照這個字段對應的每一個數據爲一個桶,然後計算每個桶裏的文檔個數。默認會按照文檔的個數排序。

    比如咱們要根據id字段進行詞聚合:

    POST /ropledata/_search
    {
      "aggs": {
        "ropledata": {
          "terms": {
            "field": "id"
          }
        }
      }
    }
    
  5. 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
    }
    
  6. 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也提供了批量的操作,具體的用法如下。

  1. 批量插入

    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"}
    
  2. 批量查詢

    比如咱們想批量查詢ropledata這個索引下文檔id爲1010,1011,1012的文檔數據,可以這樣寫:

    POST /ropledata/_mget
    {
      "ids": [
        "1010",
        "1011",
        "1012"
      ]
    }
    
  3. 批量更新

    如果咱們想批量修改1011和1012的文檔裏的name字段的值,可以這樣寫:

    POST _bulk
    { "update" : {"_id" : "1011", "_index" : "ropledata"} } { "doc" : {"name" : "批量修改"} }
    { "update" : {"_id" : "1012", "_index" : "ropledata"} } { "doc" : {"name" : "大家好"}}
    
  4. 批量刪除

    如果咱們想批量刪除文檔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提供了sizefrom兩個參數,size代表每頁的個數,默認是10個,from代表從第幾個獲取。咱們只要在代碼裏寫清楚這兩個參數,就可以實現翻譯了。

http://127.0.0.1:9200/ropledata/_search?size=1&from=2&pretty

在這裏插入圖片描述

八、總結

本文圍繞ES最新版本,從常用基礎操作、DSL查詢、聚合查詢、批量操作,實用技巧等方面進行了詳細的舉例解析,並對讀者可能出現的疑惑進行了標紅疑問總結(本文共總結了六處容易產生的疑問點),對需要注意的地方也進行了單獨的聲明,希望對大家學習ES提供幫助!

注意:如果我的文章對您有所幫助,歡迎關注點贊收藏,如果您有疑惑或發現文中有不對的地方,還請不吝賜教,非常感謝!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章