Update a document
Stack
The Update API enables you to script document updates, allowing you to update, delete, or skip modifying a document.
The following examples show common use cases, such as incrementing a counter and adding or removing elements from a list, as well as how to:
- Update part of a document
- Detect noop updates
- Insert or update documents with upsert
- Simplify upsert with doc_as_upsert
First, let's index a simple doc:
PUT test/_doc/1
{
"counter" : 1,
"tags" : ["red"]
}
To increment the counter, you can submit an update request with the following script:
POST test/_update/1
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}
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):
POST test/_update/1
{
"script": {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params": {
"tag": "blue"
}
}
}
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.
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"
}
}
}
You can also add and remove fields from a document. For example, this script
adds the field new_field
:
POST test/_update/1
{
"script" : "ctx._source.new_field = 'value_of_new_field'"
}
Conversely, this script removes the field new_field
:
POST test/_update/1
{
"script" : "ctx._source.remove('new_field')"
}
The following script removes a subfield from an object field:
PUT test/_doc/1?refresh
{
"my-object": {
"my-subfield": true
}
}
POST test/_update/1
{
"script": "ctx._source['my-object'].remove('my-subfield')"
}
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
):
POST test/_update/1
{
"script": {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'noop' }",
"lang": "painless",
"params": {
"tag": "green"
}
}
}
The following partial update adds a new field to the existing document:
POST test/_update/1
{
"doc": {
"name": "new_name"
}
}
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.
By default updates that don't change anything detect that they don't change
anything and return "result": "noop"
:
POST test/_update/1
{
"doc": {
"name": "new_name"
}
}
If the value of name
is already new_name
, the update
request is ignored and the result
element in the response returns noop
:
{
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_index": "test",
"_id": "1",
"_version": 2,
"_primary_term": 1,
"_seq_no": 1,
"result": "noop"
}
You can disable this behavior by setting "detect_noop": false
:
POST test/_update/1
{
"doc": {
"name": "new_name"
},
"detect_noop": false
}
An upsert operation lets you update an existing document or insert a new one if it doesn't exist, in a single request.
In this example, if the product with ID 1
exists, its price will be updated to 100
. If the product does not exist, a new document with ID 1
and a price of 50
will be inserted.
POST /test/_update/1
{
"doc": {
"product_price": 100
},
"upsert": {
"product_price": 50
}
}
To run the script whether or not the document exists, set scripted_upsert
to
true
:
POST test/_update/1
{
"scripted_upsert": true,
"script": {
"source": """
if ( ctx.op == 'create' ) {
ctx._source.counter = params.count
} else {
ctx._source.counter += params.count
}
""",
"params": {
"count": 4
}
},
"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
value:
POST test/_update/1
{
"doc": {
"name": "new_name"
},
"doc_as_upsert": true
}
Using ingest pipelines with doc_as_upsert
is not supported.