Update API能夠根據提供的腳本更新文檔。 該操做從索引獲取文檔,運行腳本(腳本語言和參數是可選的),並返回操做的結果(也容許刪除或忽略該操做)。 使用版本控制來確保在「get」(查詢文檔)和「reindex」(從新索引文檔)期間沒有發生更新。html
值得注意的是,該操做會從新索引文檔(也就是說更新操做會先查文檔,對文檔合併,刪除以前的文檔,從新添加合併的文檔。),它只是減小了網絡往返以及減小了get(獲取文檔)和index(索引文檔)之間版本衝突的可能性。 須要啓用_source字段才能使此特性生效。web
好比,索引一個簡單的文檔:數組
PUT test/_doc/1 { "counter" : 1, "tags" : ["red"] }
如下示例演示瞭如何執行一個增長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 }
若是文檔尚不存在,則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的內容。
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