目录
- 前言
- Update API
- 使用部分文档进行更新
- Updata by Query API
前言
Elasticsearch操作中,最常用的就是搜索和更新操作了。之前介绍了Elasticsearch的搜索API,所以,今天我们来介绍下Elasticsearch的更新操作。
Update API
Elasticsearch的更新API为Update,在url中为_update方法,Update方法支持脚本更新和内容更新
更新API允许基于提供的脚本更新文档。 该操作从索引获取文档(与分片并置),运行脚本(使用可选的脚本语言和参数),并对结果进行索引(也允许删除或忽略操作)。 它使用版本控制来确保在“get”和“reindex”期间没有发生更新。
请注意,此操作仍然意味着文档的完全重新索引,它只是删除了一些网络往返,并减少了get和index之间版本冲突的可能性。 需要启用_source字段才能使此功能正常工作。
例如,让我们索引一个简单的文档:
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)中添加一个标签(注意,如果标签存在,它仍会添加它,因为它是一个列表):
POST test/_doc/1/_update
{
"script" : {
"source": "ctx._source.tags.add(params.tag)",
"lang": "painless",
"params" : {
"tag" : "blue"
}
}
}
注:ctx._source用于表示当前文档,后面接文档属性,即可进行文档属性的修改,如上面的将counter增加,添加tags等操作
除了_source ,下面的参数也存在在 cxt map中:_index,_type,_id,_version,_routing和_now(当前时间戳)
在文档中添加新的字段:
POST test/_doc/1/_update
{
"script" : "ctx._source.new_field = 'value_of_new_field'"
}
new_field为新字段的名称
value_of_new_field为字段的初始值
删除一个字段:
POST test/_doc/1/_update
{
"script" : "ctx._source.remove('new_field')"
}
除了简单的更新,我们还可以使用脚本进行一些复杂的更新操作,如下面的例子,如果tags字段包含green,此示例将删除doc,否则它不执行任何操作(noop)
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"
}
}
}
ctx.op=‘delete‘表示删除文档中的数据
ctx.op = 'none’表示不执行任何操作
使用部分文档进行更新
除了使用脚本进行更新,update还支持使用部分文档内容进行更新。
更新API支持传递部分文档,该部分文档将合并到现有文档中。 要完全替换现有文档,应使用index API。 以下为更新文档中的某个字段值:
POST test/_doc/1/_update
{
"doc" : {
"name" : "new_name"
}
}
注:如果文档没有name这个字段,那么会先添加name字段,然后将new_name赋值进去
多个字段更新
{
"doc" : {
"new_field " : "new",
"name":"m"
}
doc相当于重新定义文档,执行了doc之后,文档内的数据就会变成doc中定义的数据,所以我们可以直接在doc更新和添加数据
注:如果同时指定了doc和script,则会忽略doc。
默认情况下,不更改任何内容的更新会检测到它们没有更改任何内容并返回“result”:“noop”,如下所示:
POST test/_doc/1/_update
{
"doc" : {
"name" : "new_name"
}
}
如果在发送请求之前name是new_name,则忽略整个更新请求。 如果忽略请求,响应中的result元素将返回noop。
{
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
},
"_index": "test",
"_type": "_doc",
"_id": "1",
"_version": 6,
"result": "noop"
}
参数
更新操作支持以下查询字符串参数:
retry_on_conflict 在更新的get和indexing阶段之间,另一个进程可能已经更新了同一文档。 默认情况下,更新将因版本冲突异常而失败。 retry_on_conflict 参数控制在最终抛出异常之前重试更新的次数。
routing 路由用于将更新请求路由到正确的分片,并在更新的文档不存在时为upsert请求设置路由。 不能用于更新现有文档的路由。
timeout 超时等待碎片变为可用。
wait_for_active_shards 在继续更新操作之前需要处于活动状态的分片副本数。 详情请见此处。
refresh 控制何时此请求所做的更改对搜索可见。 看?refresh。
_source 允许控制是否以及如何在响应中返回更新的源。 默认情况下,不会返回更新的源。 请参阅源过滤了解详细信息
version 更新API在内部使用Elasticsearch的版本控制支持,以确保在更新期间文档不会更改。 您可以使用
version参数指定仅在文档版本与指定版本匹配时才更新文档。
Updata by Query API
Updata API是对单个文档进行更新操作,很多时候,我们需要对多个文档进行更新操作,这时候就要使用 Updata by Query API了。 Updata by Query API会对符合条件的所有文档都进行更新
Updata by Query API对应的方法为_update_by_query
_update_by_query的最简单用法只是对索引中的每个文档执行更新而不更改源。 这对于获取新属性或其他一些在线映射更改很有用。
例:
POST twitter/_update_by_query?conflicts=proceed
类似结果:
{
"took" : 147,
"timed_out": false,
"updated": 120,
"deleted": 0,
"batches": 1,
"version_conflicts": 0,
"noops": 0,
"retries": {
"bulk": 0,
"search": 0
},
"throttled_millis": 0,
"requests_per_second": -1.0,
"throttled_until_millis": 0,
"total": 120,
"failures" : [ ]
}
_update_by_query在索引启动时获取索引的快照,并使用内部版本控制索引它的内容。 这意味着如果文档在拍摄快照的时间和处理索引请求之间发生更改,则会出现版本冲突。 当版本匹配时,文档会更新,版本号会递增。
备注:由于内部版本控制不支持将值0作为有效版本号,因此无法使用_update_by_query更新版本等于零的文档,并且将使请求失败。
所有更新和查询失败都会导致_update_by_query中止,并在响应失败时返回。 已执行的更新仍然存在。 换句话说,该过程不会回滚,只会中止。 当第一个故障导致中止时,失败的批量请求返回的所有故障都将在failure元素中返回; 因此,可能存在相当多的失败实体。
如果您只想简单地计算版本冲突,不要导致_update_by_query中止,您可以设置在URL中设置 conflicts=proceed 或在请求体中设置"conflicts": “proceed”。,上面例子中就使用了该参数。
我们可以使用查询DSL限制_update_by_query的更新范围
如下,_update_by_query只会更新user为kimchy的文档,而不是所有文档
POST twitter/_update_by_query?conflicts=proceed
{
"query": { // 1
"term": {
"user": "kimchy"
}
}
}
_update_by_query同样支持脚本来更新文档
如下,我们将user为kimchy的所有文档的likes都加1
POST twitter/_update_by_query
{
"script": {
"source": "ctx._source.likes++",
"lang": "painless"
},
"query": {
"term": {
"user": "kimchy"
}
}
}
_update_by_query 和_update使用差不多,只是_update_by_query可以通过DSL查询来指定我们更新的范围
例:
通过 script 的方法来为我们的 某个索引下的所有文档增加一个新的字段,比如:
POST twitter/_update_by_query
{
"script": {
"source": "ctx._source['contact'] = \"139111111111\""
}
}
通过上面的方法,我们twitter索引中的所有的文档都添加一个新的字段 contact,并赋予它一个同样的值