OpenSearch/docs/reference/docs/update.asciidoc

336 lines
8.6 KiB
Plaintext
Raw Normal View History

[[docs-update]]
=== Update API
++++
<titleabbrev>Update</titleabbrev>
++++
Updates a document using the specified script.
[[docs-update-api-request]]
==== {api-request-title}
`POST /<index>/_update/<_id>`
[[update-api-desc]]
==== {api-description-title}
Enables you script document updates. The script can update, delete, or skip
modifying the document. The update API also supports passing a partial document,
which is merged into the existing document. To fully replace an existing
document, use the <<docs-index_,`index` API>>.
This operation:
. Gets the document (collocated with the shard) from the index.
. Runs the specified script.
. Indexes the result.
The document must still be reindexed, but using `update` removes some network
roundtrips and reduces chances of version conflicts between the GET and the
index operation.
The `_source` field must be enabled to use `update`. In addition to `_source`,
you can access the following variables through the `ctx` map: `_index`,
`_type`, `_id`, `_version`, `_routing`, and `_now` (the current timestamp).
[[docs-update-api-path-params]]
==== {api-path-parms-title}
`<index>`::
(Required, string) Name of the target index. By default, the index is created
automatically if it doesn't exist. For more information, see <<index-creation>>.
`<_id>`::
(Required, string) Unique identifier for the document to be updated.
[[docs-update-api-query-params]]
==== {api-query-parms-title}
include::{docdir}/rest-api/common-parms.asciidoc[tag=if_seq_no]
include::{docdir}/rest-api/common-parms.asciidoc[tag=if_primary_term]
`lang`::
(Optional, string) The script language. Default: `painless`.
include::{docdir}/rest-api/common-parms.asciidoc[tag=refresh]
`retry_on_conflict`::
(Optional, integer) Specify how many times should the operation be retried when
a conflict occurs. Default: 0.
include::{docdir}/rest-api/common-parms.asciidoc[tag=routing]
`_source`::
(Optional, list) Set to `false` to disable source retrieval (default: `true`).
You can also specify a comma-separated list of the fields you want to retrieve.
`_source_excludes`::
(Optional, list) Specify the source fields you want to exclude.
`_source_includes`::
(Optional, list) Specify the source fields you want to retrieve.
include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
include::{docdir}/rest-api/common-parms.asciidoc[tag=wait_for_active_shards]
[[update-api-example]]
==== {api-examples-title}
First, let's index a simple doc:
[source,console]
--------------------------------------------------
PUT test/_doc/1
{
"counter" : 1,
"tags" : ["red"]
}
--------------------------------------------------
To increment the counter, you can submit an update request with the
following script:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : {
"source": "ctx._source.counter += params.count",
2016-06-27 09:55:16 -04:00
"lang": "painless",
"params" : {
"count" : 4
}
}
}
--------------------------------------------------
// TEST[continued]
Similarly, you could use and update script to add a tag to the list of tags
(this is just a list, so the tag is added even it exists):
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : {
"source": "ctx._source.tags.add(params.tag)",
2016-06-27 09:55:16 -04:00
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
--------------------------------------------------
// TEST[continued]
You could also remove a tag from the list of tags. The Painless
function to `remove` a tag takes the array index of the element
you want to remove. To avoid a possible runtime error, you first need to
make sure the tag exists. If the list contains duplicates of the tag, this
script just removes one occurrence.
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
--------------------------------------------------
// TEST[continued]
You can also add and remove fields from a document. For example, this script
adds the field `new_field`:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : "ctx._source.new_field = 'value_of_new_field'"
}
--------------------------------------------------
// TEST[continued]
Conversely, this script removes the field `new_field`:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : "ctx._source.remove('new_field')"
}
--------------------------------------------------
// TEST[continued]
Instead of updating the document, you can also change the operation that is
executed from within the script. For example, this request deletes the doc if
the `tags` field contains `green`, otherwise it does nothing (`noop`):
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",
2016-06-27 09:55:16 -04:00
"lang": "painless",
"params" : {
"tag" : "green"
}
}
}
--------------------------------------------------
// TEST[continued]
2015-06-19 17:29:02 +02:00
[float]
===== Update part of a document
The following partial update adds a new field to the
existing document:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"doc" : {
"name" : "new_name"
}
}
--------------------------------------------------
// TEST[continued]
If both `doc` and `script` are specified, then `doc` is ignored. If you
specify a scripted update, include the fields you want to update in the script.
2015-06-19 17:29:02 +02:00
[float]
===== Detect noop updates
By default updates that don't change anything detect that they don't change
anything and return `"result": "noop"`:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"doc" : {
"name" : "new_name"
}
}
--------------------------------------------------
// TEST[continued]
If the value of `name` is already `new_name`, the update
request is ignored and the `result` element in the response returns `noop`:
[source,console-result]
--------------------------------------------------
{
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 7,
"_primary_term": 1,
"_seq_no": 6,
Enforce that responses in docs are valid json (#26249) All of the snippets in our docs marked with `// TESTRESPONSE` are checked against the response from Elasticsearch but, due to the way they are implemented they are actually parsed as YAML instead of JSON. Luckilly, all valid JSON is valid YAML! Unfurtunately that means that invalid JSON has snuck into the exmples! This adds a step during the build to parse them as JSON and fail the build if they don't parse. But no! It isn't quite that simple. The displayed text of some of these responses looks like: ``` { ... "aggregations": { "range": { "buckets": [ { "to": 1.4436576E12, "to_as_string": "10-2015", "doc_count": 7, "key": "*-10-2015" }, { "from": 1.4436576E12, "from_as_string": "10-2015", "doc_count": 0, "key": "10-2015-*" } ] } } } ``` Note the `...` which isn't valid json but we like it anyway and want it in the output. We use substitution rules to convert the `...` into the response we expect. That yields a response that looks like: ``` { "took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits, "aggregations": { "range": { "buckets": [ { "to": 1.4436576E12, "to_as_string": "10-2015", "doc_count": 7, "key": "*-10-2015" }, { "from": 1.4436576E12, "from_as_string": "10-2015", "doc_count": 0, "key": "10-2015-*" } ] } } } ``` That is what the tests consume but it isn't valid JSON! Oh no! We don't want to go update all the substitution rules because that'd be huge and, ultimately, wouldn't buy much. So we quote the `$body.took` bits before parsing the JSON. Note the responses that we use for the `_cat` APIs are all converted into regexes and there is no expectation that they are valid JSON. Closes #26233
2017-08-17 09:02:10 -04:00
"result": "noop"
}
--------------------------------------------------
You can disable this behavior by setting `"detect_noop": false`:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"doc" : {
"name" : "new_name"
},
"detect_noop": false
}
--------------------------------------------------
// TEST[continued]
[[upserts]]
[float]
===== Upsert
2015-06-19 17:29:02 +02:00
If the document does not already exist, the contents of the `upsert` element
are inserted as a new document. If the document exists, the
`script` is executed:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"script" : {
"source": "ctx._source.counter += params.count",
2016-06-27 09:55:16 -04:00
"lang": "painless",
"params" : {
"count" : 4
}
},
"upsert" : {
"counter" : 1
}
}
--------------------------------------------------
// TEST[continued]
2015-06-19 17:29:02 +02:00
[float]
[[scripted_upsert]]
===== Scripted upsert
2015-06-19 17:29:02 +02:00
To run the script whether or not the document exists, set `scripted_upsert` to
`true`:
[source,console]
--------------------------------------------------
POST sessions/_update/dh3sgudg8gsrgl
{
"scripted_upsert":true,
"script" : {
"id": "my_web_session_summariser",
"params" : {
"pageViewEvent" : {
"url":"foo.com/bar",
"response":404,
"time":"2014-01-01 12:32"
}
}
},
2015-06-19 17:29:02 +02:00
"upsert" : {}
}
--------------------------------------------------
// TEST[s/"id": "my_web_session_summariser"/"source": "ctx._source.page_view_event = params.pageViewEvent"/]
// TEST[continued]
2015-06-19 17:29:02 +02:00
[float]
[[doc_as_upsert]]
===== Doc as upsert
Instead of sending a partial `doc` plus an `upsert` doc, you can set
`doc_as_upsert` to `true` to use the contents of `doc` as the `upsert`
2015-06-19 17:29:02 +02:00
value:
[source,console]
--------------------------------------------------
POST test/_update/1
{
"doc" : {
"name" : "new_name"
},
"doc_as_upsert" : true
}
--------------------------------------------------
// TEST[continued]