Elasticsearch從入門到放棄:文檔CRUD要牢記

在Elasticsearch中,文檔(document)是全部可搜索數據的最小單位。它被序列化成JSON存儲在Elasticsearch中。每一個文檔都會有一個惟一ID,這個ID你能夠本身指定或者交給Elasticsearch自動生成。html

若是延續咱們以前不恰當的對比RDMS的話,我認爲文檔能夠類比成關係型數據庫中的表。node

元數據

前面咱們提到,每一個文檔都有一個惟一ID來標識,獲取文檔時,「_id」字段記錄的就是文檔的惟一ID,它是元數據之一。固然,文檔還有一些其餘的元數據,下面咱們來一一介紹數據庫

  • _index:文檔所屬的索引名
  • _type:文檔所屬的type
  • _id:文檔的惟一ID

有了這三個,咱們就能夠惟一肯定一個document了,固然,7.0版本之後咱們已經不須要_type了。接下來咱們再來看看其餘的一些元數據json

  • _source:文檔的原始JSON數據
  • _field_names:該字段用於索引文檔中值不爲null的字段名,主要用於exists請求查找指定字段是否爲空
  • _ignore:這個字段用於索引和存儲文檔中每一個因爲異常(開啓了ignore_malformed)而被忽略的字段的名稱
  • _meta:該字段用於存儲一些自定義的元數據信息
  • _routing:用來指定數據落在哪一個分片上,默認值是Id
  • _version:文檔的版本信息
  • _score:相關性打分

建立文檔

建立文檔有如下4種方法:安全

  • PUT /<index>/_doc/<_id>
  • POST /<index>/_doc/
  • PUT /<index>/_create/<_id>
  • POST /<index>/_create/<_id>

這四種方法的區別是,若是不指定id,則Elasticsearch會自動生成一個id。若是使用_create的方法,則必須保證文檔不存在,而使用_doc方法的話,既能夠建立新的文檔,也能夠更新已存在的文檔。bash

在建立文檔時,還能夠選擇一些參數。併發

請求參數

  • if_seq_no:當文檔的序列號是指定值時才更新
  • if_primary_term:當文檔的primary term是指定值時才更新
  • op_type:若是設置爲create則指定id的文檔必須不存在,不然操做失敗。有效值爲index或create,默認爲index
  • op_type:指定預處理的管道id
  • refresh:若是設置爲true,則當即刷新受影響的分片。若是是wait_for,則會等到刷新分片後,這次操做纔對搜索可見。若是是false,則不會刷新分片。默認值爲false
  • routing:指定路由到的主分片
  • timeout:指定響應時間,默認是30秒
  • master_timeout:鏈接主節點的響應時長,默認是30秒
  • version:顯式的指定版本號
  • version_type:指定版本號類型:internal、 external、external_gte、force
  • wait_for_active_shards:處理操做以前,必須保持活躍的分片副本數量,能夠設置爲all或者任意正整數。默認是1,即只須要主分片活躍。

響應包體

  • _shards:提供分片的信息
  • _shards.total:建立了文檔的總分片數量
  • _shards.successful:成功建立文檔分片的數量
  • _shards.failed:建立文檔失敗的分片數量
  • _index:文檔所屬索引
  • _type:文檔所屬type,目前只支持_doc
  • _id:文檔的id
  • _version:文檔的版本號
  • _seq_no:文檔的序列號
  • _primary_term:文檔的主要術語
  • result:索引的結果,created或者updated

咱們在建立文檔時,若是指定的索引不存在,則ES會自動爲咱們建立索引。這一操做是能夠經過設置中的action.auto_create_index字段來控制的,默認是true。你能夠修改這個字段,實現指定某些索引能夠自動建立或者全部索引都不能自動建立的目的。app

更新文檔

瞭解瞭如何建立文檔以後,咱們再來看看應該如何更新一個已經存在的文檔。其實在建立文檔時咱們就提到過,使用PUT /<index>/_doc/<id>的方法就能夠更新一個已存在的文檔。除此以外,咱們還有另外一種更新文檔的方法:less

POST /<index>/_update/<_id>curl

這兩種更新有所不一樣。_doc方法是先刪除原有的文檔,再建立新的。而_update方法則是增量更新,它的更新過程是先檢索到文檔,而後運行指定腳本,最後從新索引。

還有一個區別就是_update方法支持使用腳本更新,默認的語言是painless,你能夠經過參數lang來進行設置。在請求參數方面,_update相較於_doc多瞭如下幾個參數:

  • lang:指定腳本語言
  • retry_on_conflict:發生衝突時重試次數,默認是0
  • _source:設置爲false,則不返回任何檢索字段
  • _source_excludes:指定要從檢索結果排除的source字段
  • _source_includes:指定要返回的檢索source字段

下面的一個例子是用腳原本更新文檔

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d' { "script" : { "source": "ctx._source.counter += params.count", "lang": "painless", "params" : { "count" : 4 } } } '
複製代碼

Upsert

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d' { "script" : { "source": "ctx._source.counter += params.count", "lang": "painless", "params" : { "count" : 4 } }, "upsert" : { "counter" : 1 } } '
複製代碼

當指定的文檔不存在時,可使用upsert參數,建立一個新的文檔,而當指定的文檔存在時,該請求會執行script中的腳本。若是不想使用腳本,而只想新增/更新文檔的話,可使用doc_as_upsert。

curl -X POST "localhost:9200/test/_update/1?pretty" -H 'Content-Type: application/json' -d' { "doc" : { "name" : "new_name" }, "doc_as_upsert" : true } '
複製代碼

update by query

這個API是用於批量更新檢索出的文檔的,具體能夠經過一個例子來了解。

curl -X POST "localhost:9200/twitter/_update_by_query?pretty" -H 'Content-Type: application/json' -d' { "script": { "source": "ctx._source.likes++", "lang": "painless" }, "query": { "term": { "user": "kimchy" } } } '
複製代碼

獲取文檔

ES獲取文檔用的是GET API,請求的格式是:

GET /<index>/_doc/<_id>

它會返回文檔的數據和一些元數據,若是你只想要文檔的內容而不須要元數據時,可使用

GET /<index>/_source/<_id>

請求參數

獲取文檔的有幾個請求參數以前已經提到過,這裏再也不贅述,它們分別是:

  • refresh
  • routing
  • _source
  • _source_excludes
  • _source_includes
  • version
  • version_type

而還有一些以前沒提到過的參數,咱們來具體看一下

  • preference:用來 指定執行請求的node或shard,若是設置爲_local,則會優先在本地的分片執行
  • realtime:若是設置爲true,則請求是實時的而不是近實時。默認是true
  • stored_fields:返回指定的字段中,store爲true的字段

mget

mget是批量獲取的方法之一,請求的格式有兩種:

  • GET /_mget
  • GET /<index>/_mget

第一種是在請求體中寫index。第二種是把index放到url中,不過這種方式可能會觸發ES的安全檢查。

mget的請求參數和get相同,只是須要在請求體中指定doc的相關檢索條件

request

GET /_mget
{
    "docs" : [
        {
            "_index" : "jackey",
            "_id" : "1"
        },
        {
            "_index" : "jackey",
            "_id" : "2"
        }
    ]
}
複製代碼

response

{
  "docs" : [
    {
      "_index" : "jackey",
      "_type" : "_doc",
      "_id" : "1",
      "_version" : 5,
      "_seq_no" : 6,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user" : "ja",
        "tool" : "ES",
        "message" : "qwer"
      }
    },
    {
      "_index" : "jackey",
      "_type" : "_doc",
      "_id" : "2",
      "_version" : 1,
      "_seq_no" : 2,
      "_primary_term" : 1,
      "found" : true,
      "_source" : {
        "user" : "zhe",
        "post_date" : "2019-11-15T14:12:12",
        "message" : "learning Elasticsearch"
      }
    }
  ]
}
複製代碼

刪除文檔

CURD操做只剩下最後一個D了,下面咱們就一塊兒來看看ES中如何刪除一個文檔。

刪除指定id使用的請求是

DELETE /<index>/_doc/<_id>

在併發量比較大的狀況下,咱們在刪除時一般會指定版本,以肯定刪除的文檔是咱們真正想要刪除的文檔。刪除請求的參數咱們在以前也都介紹過,想要具體瞭解的同窗能夠直接查看官方文檔

delete by query

相似於update,delete也有一個delete by query的API。

POST /<index>/_delete_by_query

它也是要先按照條件來查詢匹配的文檔,而後刪除這些文檔。在執行查詢以前,Elasticsearch會先爲指定索引作一個快照,若是在執行刪除過程當中,要索引起生改變,則會致使操做衝突,同時返回刪除失敗。

若是刪除的文檔比較多,也可使這個請求異步執行,只須要設置wait_for_completion=false便可。

這個API的refresh與delete API的refresh參數有所不一樣,delete中的refresh參數是設置操做是否當即可見,即只刷新一個分片,而這個API中的refresh參數則是須要刷新受影響的全部分片。

Bulk API

最後,咱們再來介紹一種特殊的API,批量操做的API。它支持兩種寫法,能夠將索引名寫到url中,也能夠寫到請求體中。

  • POST /_bulk

  • POST /<index>/_bulk

在這個請求中,你能夠任意使用以前的CRUD請求的組合。

curl -X POST "localhost:9200/_bulk?pretty" -H 'Content-Type: application/json' -d' { "index" : { "_index" : "test", "_id" : "1" } } { "field1" : "value1" } { "delete" : { "_index" : "test", "_id" : "2" } } { "create" : { "_index" : "test", "_id" : "3" } } { "field1" : "value3" } { "update" : {"_id" : "1", "_index" : "test"} } { "doc" : {"field2" : "value2"} } '
複製代碼

請求體中使用的語法是newline delimited JSON(NDJSON)。具體怎麼用呢?其實咱們在上面的例子中已經有所展示了,對於index或create這樣的請求,若是請求自己是有包體的,那麼用換行符來表示下面的內容與子請求分隔,即爲包體的開始。

例如上面例子中的index請求,它的包體就是{ "field1" : "value1" },因此它會在index請求的下一行出現。

對於批量執行操做來講,單條操做失敗並不會影響其餘操做,而最終每條操做的結果也都會返回。

上面的例子執行完以後,咱們獲得的結果應該是

{
   "took": 30,
   "errors": false,
   "items": [
      {
         "index": {
            "_index": "test",
            "_type": "_doc",
            "_id": "1",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 201,
            "_seq_no" : 0,
            "_primary_term": 1
         }
      },
      {
         "delete": {
            "_index": "test",
            "_type": "_doc",
            "_id": "2",
            "_version": 1,
            "result": "not_found",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 404,
            "_seq_no" : 1,
            "_primary_term" : 2
         }
      },
      {
         "create": {
            "_index": "test",
            "_type": "_doc",
            "_id": "3",
            "_version": 1,
            "result": "created",
            "_shards": {
               "total": 2,
               "successful": 1,
               "failed": 0
            },
            "status": 201,
            "_seq_no" : 2,
            "_primary_term" : 3
         }
      },
      {
         "update": {
            "_index": "test",
            "_type": "_doc",
            "_id": "1",
            "_version": 2,
            "result": "updated",
            "_shards": {
                "total": 2,
                "successful": 1,
                "failed": 0
            },
            "status": 200,
            "_seq_no" : 3,
            "_primary_term" : 4
         }
      }
   ]
}
複製代碼

批量操做的執行過程相比屢次單個操做而言,在性能上會有必定的提高。但同時也會有必定的風險,因此咱們在使用的時候要很是的謹慎。

總結

本文咱們先介紹了文檔的基本概念和文檔的元數據。接着又介紹了文檔的CRUD操做和Bulk API。相信看完文章你對Elasticsearch的文檔也會有必定的瞭解。那最後就請你啓動你的Elasticsearch,而後親自動手試一試這些操做,看看各類請求的參數究竟有什麼做用。相信親手實驗過一遍以後你會對這些API有更深的印象。

相關文章
相關標籤/搜索