Update API

Update API能夠根據提供的腳本更新文檔。 該操做從索引獲取文檔,運行腳本(腳本語言和參數是可選的),並返回操做的結果(也容許刪除或忽略該操做)。 使用版本控制來確保在「get」(查詢文檔)和「reindex」(從新索引文檔)期間沒有發生更新。html

值得注意的是,該操做會從新索引文檔(也就是說更新操做會先查文檔,對文檔合併,刪除以前的文檔,從新添加合併的文檔。),它只是減小了網絡往返以及減小了get(獲取文檔)和index(索引文檔)之間版本衝突的可能性。 須要啓用_source字段才能使此特性生效。web

好比,索引一個簡單的文檔:數組

PUT test/_doc/1
{
    "counter" : 1,
    "tags" : ["red"]
}

Scripted updates

如下示例演示瞭如何執行一個增長counter的腳本:網絡

POST test/_doc/1/_update
{
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    }
}

如今咱們就能夠往tags列表裏添加一個tag(注意,若是tag存在,仍會添加,由於它是一個list)session

POST test/_doc/1/_update
{
    "script" : {
        "source": "ctx._source.tags.add(params.tag)",
        "lang": "painless",
        "params" : {
            "tag" : "blue"
        }
    }
}

不止_source,如下變量也能夠經過ctx來取得: _index, _type, _id, _version, _routing and _now(當前的時間戳)less

如下示例演示瞭如何獲取_id,好比:elasticsearch

POST test/_doc/1/_update
{
    "script" : "ctx._source.tags.add(ctx._id)"
}

也能夠向文檔添加新字段:ide

POST test/_doc/1/_update
{
    "script" : "ctx._source.new_field = 'value_of_new_field'"
}

從文檔移除某個字段:oop

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

甚至能夠改變已執行的操做。 如下示例:若是標籤字段包含green,將刪除doc,不然它不執行任何操做(即該操做會被忽略,返回noop):ui

POST test/_doc/1/_update
{
    "script" : {
        "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
        "lang": "painless",
        "params" : {
            "tag" : "green"
        }
    }
}

更新部分文檔

update API還支持傳遞部分文檔,該部分文檔將合併到現有文檔中(簡單的遞歸合併,對象的內部合併,替換核心"keys/values"和數組)。 要徹底替換現有文檔,應使用index API。 如下示例演示瞭如何使用部分更新向現有文檔添加新字段:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    }
}

若是同時指定了doc和script,會報錯。 最好是將部分文檔的字段對放在腳本自己中(目前我還不知道該怎麼操做)。

POST test/_doc/1/_update
{
  "doc" : {
        "age" : "18"
    },
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    }
}

返回結果以下:

{
  "error": {
    "root_cause": [
      {
        "type": "action_request_validation_exception",
        "reason": "Validation Failed: 1: can't provide both script and doc;"
      }
    ],
    "type": "action_request_validation_exception",
    "reason": "Validation Failed: 1: can't provide both script and doc;"
  },
  "status": 400
}

檢測noop更新
若是指定了doc,則其值將與現有_source合併。 默認狀況下,不更改任何內容的更新,會檢測到並會返回「result」:「noop」,以下所示:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    }
}

若是在發送請求以前name是new_name,則忽略整個更新請求。 若是請求被忽略,響應中的result元素將返回noop。

{
  "_index": "test",
  "_type": "_doc",
  "_id": "1",
  "_version": 2,
  "result": "noop",
  "_shards": {
    "total": 0,
    "successful": 0,
    "failed": 0
  }
}

設置"detect_noop": false能夠禁用這種默認行爲:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    },
    "detect_noop": false
}

Upserts

 若是文檔尚不存在,則upsert元素的內容將做爲新文檔插入。 若是文檔確實存在,則執行腳本:

POST test/_doc/1/_update
{
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    },
    "upsert" : {
        "counter" : 1
    }
}

固然,不必定非得腳本,下面這樣也是能夠的,文檔不存在的時候執行upsert內容,文檔存在的時候執行doc的內容:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    },
    "upsert" : {
        "counter" : 10
    }
}

scripted_upsert
若是但願不管文檔是否存在,都運行腳本(即便用腳本處理初始化文檔而不是upsert元素)能夠將scripted_upsert設置爲true:

POST sessions/session/dh3sgudg8gsrgl/_update
{
    "scripted_upsert":true,
    "script" : {
        "id": "my_web_session_summariser",
        "params" : {
            "pageViewEvent" : {
                "url":"foo.com/bar",
                "response":404,
                "time":"2014-01-01 12:32"
            }
        }
    },
    "upsert" : {}
}

下面來看看和直接寫腳本不用upsert的區別,當文檔不存在時,直接下面這樣寫會報錯。

POST test/_doc/1/_update
{
    "scripted_upsert":true,
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    }
}

返回錯誤消息以下:

{
  "error": {
    "root_cause": [
      {
        "type": "document_missing_exception",
        "reason": "[_doc][1]: document missing",
        "index_uuid": "YgmlkeEERGm20yUBDJHKtQ",
        "shard": "3",
        "index": "test"
      }
    ],
    "type": "document_missing_exception",
    "reason": "[_doc][1]: document missing",
    "index_uuid": "YgmlkeEERGm20yUBDJHKtQ",
    "shard": "3",
    "index": "test"
  },
  "status": 404
}

設置scripted_upsert:true,當文檔不存在時,執行下面的代碼:

POST test/_doc/1/_update
{
    "scripted_upsert":true,
    "script" : {
        "source": "ctx._source.counter += params.count",
        "lang": "painless",
        "params" : {
            "count" : 4
        }
    },
    "upsert" : {
        "counter" : 10
    }
}

返回的結果以下:

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

可見,執行成功了,下面來看看文檔:

{
  "_index": "test",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "counter": 14
  }
}

counter的值爲14,可見是先執行了upsert的內容,而後執行了腳本。

doc_as_upsert
將doc_as_upsert設置爲true將使用doc的內容做爲upsert值,而不是發送部分doc加上upsert文檔:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    },
    "doc_as_upsert" : true
}

下面來看看和直接寫doc的區別:

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    }
}

當文檔不存在時,設置doc_as_upsert爲true,能夠成功執行。而上面這種狀況會報錯,提示文檔不存在。若是向下面這樣寫會出現什麼狀況呢?

POST test/_doc/1/_update
{
    "doc" : {
        "name" : "new_name"
    },
    "upsert" : {
        "counter" : 10
    },
    "doc_as_upsert" : true
}

結果是upsert永遠不會被執行,無論文檔存在不存在,始終執行的是doc的內容。

Parameters

 update操做支持如下query-string(跟在請求url後面)參數:

retry_on_conflict:在更新的get和indexing階段之間,另外一個進程可能已經更新了同一文檔。 默認狀況下,會更新失敗,由於版本衝突異常。 retry_on_conflict參數控制在最終拋出異常以前重試更新的次數。

routing:路由用於將更新請求路由到正確的分片,以及在將要更新的文檔不存在時爲upsert請求設置路由。 不能用於更新現有文檔的路由。

timeout:設置等待分片變爲可用的時間。

wait_for_active_shards:在繼續更新操做以前須要處於活動狀態的分片副本數。 詳情請見此處

refresh:控制什麼時候該請求所作的更改對搜索可見。 看refresh

_source:容許控制是否返回以及如何在響應中返回更新的源。 默認狀況下,不會返回更新的源。 請參閱源過濾瞭解詳細信息

version:update API在內部使用Elasticsearch的versioning支持,以確保在更新期間文檔不會更改。 能夠使用version參數指定僅在文檔版本與指定版本匹配時才更新文檔。

update API不支持internal之外的版本,也就是說update API不支持外部(版本類型external&external_gte)或強制(版本類型force)版本,由於它會致使Elasticsearch版本號與外部系統不一樣步。 請改用index API

 

官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html

相關文章
相關標籤/搜索