Elasticsearch 6.5.1學習筆記(四)document更新

全局更新

在 Elasticsearch 中document是不可改變 的,不能修改它們。所以當我們使用更新API時,其實是經歷了:

  1. 查詢舊數據
  2. 標記舊數據爲刪除狀態
  3. 插入新數據
    這裏並不是將舊文檔直接刪除,而是打上刪除標記,是爲了提升ES的性能。但是如果一直不刪除舊文檔則會越堆越多,所以當舊文檔到達一定數量時,ES會做一次清理,物理刪除掉這些被標記刪除的文檔。
    全局更新的API其實就是PUT新增的API:
PUT /employee/_doc/1
{
  "name":"xiaozhangsan",
  "age":2,
  "signature":"I'm a baby",
  "hobby":["sugar","milk"]
}

這裏將員工 zhangsan 全局更新爲他的兒子 xiaozhangsan,返回結果:

{
  "_index" : "employee",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 5,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 9,
  "_primary_term" : 2
}
  • _index 修改文檔所屬的索引
  • _type 修改文檔所屬的類型,7.x版本中只允許爲 _doc
  • _id 修改文檔的id
  • _version 當前版本號,可用於實現樂觀鎖機制
  • _result 當前屬於什麼操作,這裏是做的是全局更新所以是 updated
  • _shards 對於分片的操作結果

部分更新

前面有說過,ES中的文檔是無法修改的,所以局部更新的實現步驟和全局更新類似:

  1. 查詢出舊文檔的 _source 部分
  2. 將更新內容寫入舊的 _source
  3. 刪除舊文檔(標記刪除)
  4. 使用修改後的 _source 來新增一個文檔

API如下:

POST /employee/_doc/1/_update
{
  "doc":{
    "age":3
  }
}

這裏由於 xiaozhangsan 長大了,所以只將其 age 字段加一,返回結果如下:

{
  "_index" : "employee",
  "_type" : "_doc",
  "_id" : "1",
  "_version" : 6,
  "result" : "updated",
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "failed" : 0
  },
  "_seq_no" : 10,
  "_primary_term" : 2
}

與全局更新的返回結果基本一樣。
當然局部更新不僅限於更新字段值,也可以做字段新增

腳本局部更新

簡單類型更新

在ES腳本中可以使用ctx._source來訪問文檔的 _source,這裏使用腳本再講 xiaozhangsan 的年齡加一:

POST /employee/_doc/1/_update
{
  "script":"ctx._source.age+=1"
}

對象類型更新

也可以在腳本中調用對象的方法,比如 hobby 就是一個數組對象,可以像這樣給它添加數據:

POST /employee/_doc/1/_update
{
  "script":"ctx._source.hobby.add('sleep')"
}

使用參數更新

也可以使用參數代替硬編碼,像這樣:

POST /employee/_doc/1/_update
{
  "script": {
    "source": "ctx._source.hobby.add(params.hobby)",
    "params": {
      "hobby": "sleep"
    }
  }
}

新增字段

注意字符串的值需要加單引號:

POST /employee/_doc/1/_update
{
  "script": "ctx._source.sex='male'"
}

刪除字段

POST /employee/_doc/1/_update
{
  "script": "ctx._source.remove('sex')"
}

根據條件更新

使用 if ,如果文檔hobby中包含 milk 則刪除文檔,否則不做任何操作:

POST /employee/_doc/2/_update
{
  "script": {
    "source":"if(ctx._source.hobby.contains('milk')){ctx.op='delete'}else{ctx.op='none'}"
  }
}

更新默認值

使用 upsert 設值字段默認值,當 age 不存在時會新增它並設置爲1:

POST /employee/_doc/1/_update
{
  "script":"ctx._source.age+=1",
  "upsert": {
    "age":1 
  }
}

衝突和重試

由於ES會在更新數據時自動使用 _version 字段來做樂觀鎖,所以更新操作可能由於併發衝突導致失敗,ES也爲我們提供了 retry_on_conflict 實現重試機制,可用於計數器增加、年齡增加這樣的對於併發順序無要求的併發衝突問題:

POST /employee/_doc/1/_update?retry_on_conflict=5
{
  "script":"ctx._source.age+=1",
  "upsert": {
    "age":1 
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章