elasticsearch文档Update API
通过前面两篇文章的阅读,相信读者已经熟练掌握 DeleteByQuery
的用法了,本文则来继续看文档的Update API。
本文是Elasticsearch系列的第十四篇,阅读前面的文章,有助于更好的理解本文:
1.elasticsearch安装与配置
2.初识elasticsearch中的REST接口
3.elasticsearch修改数据
4.elasticsearch文档操作
5.elasticsearch API约定(一)
6.elasticsearch API约定(二)
7.elasticsearch文档读写模型
8.elasticsearch文档索引API(一)
9.elasticsearch文档索引API(二)
10.elasticsearch文档Get API
11.elasticsearch文档Delete API
12.elasticsearch文档Delete By Query API(一)
13.elasticsearch文档Delete By Query API(二)
Update API
Update API允许开发者根据脚本更新文档,这个操作首先会从索引中获取文档(并行的分片)然后来运行更新脚本,并对结果进行索引(这个操作也可以删除或者忽略),它使用版本控制来确保在“get”和“reindex”期间没有update发生。
需要注意的是,这个操作仍然意味着文档完全重新索引,它只是移除了一些网络往返,并减少了get和reindex之间版本冲突的可能性。需要启用_source该字段才能使此功能正常工作。
例如,有如下一个简单的文档:
curl -X PUT "localhost:9200/test/_doc/1?pretty" -H 'Content-Type: application/json' -d'
{
"counter" : 1,
"tags" : ["red"]
}
'
Scripted updates
接下来,可以执行如下请求,给counter增加值,如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
}
}
'
通过ctx可以访问到文档对象,这里的脚本表示给文档的counter字段增加4(即params中定义的值),执行结果如下:
另外,tags是一个集合,也可以像这个集合中动态加入数据,如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
'
执行结果如下:
当然,能上能下,既然可以向集合中添加元素,当然也可以从集合中移除元素,例如如下请求,表示文档的tags集合中包含“blue”元素的话,就去获取“blue”元素的下标,然后将之移除:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
'
执行结果如下:
除了 _source
,ctx也提供了 _index
, _type
, _id
, _version
, _routing
以及 _now
(当前时间戳)。
也可以向文档中添加字段(注意\u0027是单引号'):
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.name = \u0027 江南一点雨 \u0027"
}
'
或者移除字段:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : "ctx._source.remove(\u0027tags\u0027)"
}
'
执行结果如下:
而且我们还可以修改要执行的操作,例如如下请求,如果文档中tags集合中包含red,则删除文档,否则不做任何事(noop):
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = \u0027delete\u0027 } else { ctx.op = \u0027none\u0027 }",
"lang": "painless",
"params" : {
"tag" : "red"
}
}
}
'
执行结果如下:
Updates with a partial document
更新文档API还支持将部分文档合并到现有文档中(简单的递归合并、对象内部合并、替换核心key/value以及数组),要替换整个文档,可以使用前文提到的 index API
,如下请求表示向现有文档添加一个新字段:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc" : {
"gender" : "男"
}
}
'
执行结果如下:
如果同时指定doc和script,则doc被忽略,最好是将部分文档的字段对放在脚本本身中。
Detecting noop updates
如果指定doc,将其值与_source合并,默认情况下,如果未做任何更改,将会返回 "result":"noop"
,如下所示(文档中已经有gender字段了,并且value为男):
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc" : {
"gender" : "男"
}
}
'
执行结果如下(注意result字段的值为noop):
可以通过设置 detect_noop
来禁用此行为,如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc" : {
"gender" : "男"
},
"detect_noop": false
}
'
执行结果如下:
Upserts
如果文档不存在,则upsert中的元素将被作为一个新文档插入,如果文档已经存在,则script脚本将被执行,如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
},
"upsert" : {
"counter" : 1
}
}
'
执行结果如下:
注意,第一次执行时,由于文档不存在,因此响应的result字段值为“created”,第二次执行时,由于文档已经存在,因此响应值为“updated”。
如果开发者希望无论文档是否存在,都是script执行而不是upsert,那么可以将scripted_upsert设置为true,如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"scripted_upsert":true,
"script" : {
"source": "ctx._source.counter += params.count",
"lang": "painless",
"params" : {
"count" : 4
}
},
"upsert" : {
"counter" : 1
}
}
'
也可以使用doc中的值代替upsert中的值(即当文档不存在时,将doc中的值插入),如下:
curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'
{
"doc" : {
"name" : "new_name"
},
"doc_as_upsert" : true
}
'
Parameters
在更新操作中,有如下可选参数:
retryonconflict:在get和index之间,可能有其他操作更新了相同的文档,,默认情况下,这时的更新操作将失败,返回一个版本冲突异常,该参数则用来控制在返回异常前的重试次数。
routing:该参数的用法与前面的类似,将更新操作引入到正确的分片上去,如果相关分片并不存在相关文档,则创建新的文档。
timeout:等待一个分片从不可以用变为可用的时间。
waitforactive_shards:与前文类似,这里不再赘述。
refresh:控制本次的变化是否能够被搜索可见。后文我将详细介绍这个参数。
_source:允许控制是否以及如何在响应中返回更新的source。默认情况下,不会返回更新的source。
version:更新API使用Elasticsearch的版本控制以确保文件在更新过程中不会改变,开发者可以使用version参数指定版本,如果文件匹配那么指定的文件需要更新。
注意:更新API不支持外部(版本类型external&external_gte)或强制(版本类型force)版本控制,因为它会导致Elasticsearch版本号与外部系统不同步。
好了,本文先说到这里,有问题欢迎留言讨论。