查看原文
其他

elasticsearch文档Update API

江南一点雨 江南一点雨 2019-07-25

通过前面两篇文章的阅读,相信读者已经熟练掌握 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该字段才能使此功能正常工作。

例如,有如下一个简单的文档:

  1. curl -X PUT "localhost:9200/test/_doc/1?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "counter" : 1,

  4.    "tags" : ["red"]

  5. }

  6. '

Scripted updates

接下来,可以执行如下请求,给counter增加值,如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : {

  4.        "source": "ctx._source.counter += params.count",

  5.        "lang": "painless",

  6.        "params" : {

  7.            "count" : 4

  8.        }

  9.    }

  10. }

  11. '

通过ctx可以访问到文档对象,这里的脚本表示给文档的counter字段增加4(即params中定义的值),执行结果如下:

另外,tags是一个集合,也可以像这个集合中动态加入数据,如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : {

  4.        "source": "ctx._source.tags.add(params.tag)",

  5.        "lang": "painless",

  6.        "params" : {

  7.            "tag" : "blue"

  8.        }

  9.    }

  10. }

  11. '

执行结果如下:

当然,能上能下,既然可以向集合中添加元素,当然也可以从集合中移除元素,例如如下请求,表示文档的tags集合中包含“blue”元素的话,就去获取“blue”元素的下标,然后将之移除:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : {

  4.        "source": "if (ctx._source.tags.contains(params.tag)) { ctx._source.tags.remove(ctx._source.tags.indexOf(params.tag)) }",

  5.        "lang": "painless",

  6.        "params" : {

  7.            "tag" : "blue"

  8.        }

  9.    }

  10. }

  11. '

执行结果如下:

除了 _source,ctx也提供了 _index, _type, _id, _version, _routing 以及 _now(当前时间戳)。

也可以向文档中添加字段(注意\u0027是单引号'):

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : "ctx._source.name = \u0027 江南一点雨 \u0027"

  4. }

  5. '

或者移除字段:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : "ctx._source.remove(\u0027tags\u0027)"

  4. }

  5. '

执行结果如下:

而且我们还可以修改要执行的操作,例如如下请求,如果文档中tags集合中包含red,则删除文档,否则不做任何事(noop):

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : {

  4.        "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = \u0027delete\u0027 } else { ctx.op = \u0027none\u0027 }",

  5.        "lang": "painless",

  6.        "params" : {

  7.            "tag" : "red"

  8.        }

  9.    }

  10. }

  11. '

执行结果如下:

Updates with a partial document

更新文档API还支持将部分文档合并到现有文档中(简单的递归合并、对象内部合并、替换核心key/value以及数组),要替换整个文档,可以使用前文提到的 index API,如下请求表示向现有文档添加一个新字段:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "doc" : {

  4.        "gender" : "男"

  5.    }

  6. }

  7. '

执行结果如下:

如果同时指定doc和script,则doc被忽略,最好是将部分文档的字段对放在脚本本身中。

Detecting noop updates

如果指定doc,将其值与_source合并,默认情况下,如果未做任何更改,将会返回 "result""noop",如下所示(文档中已经有gender字段了,并且value为男):

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "doc" : {

  4.        "gender" : "男"

  5.    }

  6. }

  7. '

执行结果如下(注意result字段的值为noop):

可以通过设置 detect_noop来禁用此行为,如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "doc" : {

  4.        "gender" : "男"

  5.    },

  6.    "detect_noop": false

  7. }

  8. '

执行结果如下:

Upserts

如果文档不存在,则upsert中的元素将被作为一个新文档插入,如果文档已经存在,则script脚本将被执行,如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "script" : {

  4.        "source": "ctx._source.counter += params.count",

  5.        "lang": "painless",

  6.        "params" : {

  7.            "count" : 4

  8.        }

  9.    },

  10.    "upsert" : {

  11.        "counter" : 1

  12.    }

  13. }

  14. '

执行结果如下:

注意,第一次执行时,由于文档不存在,因此响应的result字段值为“created”,第二次执行时,由于文档已经存在,因此响应值为“updated”。

如果开发者希望无论文档是否存在,都是script执行而不是upsert,那么可以将scripted_upsert设置为true,如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "scripted_upsert":true,

  4.    "script" : {

  5.        "source": "ctx._source.counter += params.count",

  6.        "lang": "painless",

  7.        "params" : {

  8.            "count" : 4

  9.        }

  10.    },

  11.    "upsert" : {

  12.        "counter" : 1

  13.    }

  14. }

  15. '

也可以使用doc中的值代替upsert中的值(即当文档不存在时,将doc中的值插入),如下:

  1. curl -X POST "localhost:9200/test/_doc/1/_update?pretty" -H 'Content-Type: application/json' -d'

  2. {

  3.    "doc" : {

  4.        "name" : "new_name"

  5.    },

  6.    "doc_as_upsert" : true

  7. }

  8. '

Parameters

在更新操作中,有如下可选参数:

  1. retryonconflict:在get和index之间,可能有其他操作更新了相同的文档,,默认情况下,这时的更新操作将失败,返回一个版本冲突异常,该参数则用来控制在返回异常前的重试次数。

  2. routing:该参数的用法与前面的类似,将更新操作引入到正确的分片上去,如果相关分片并不存在相关文档,则创建新的文档。

  3. timeout:等待一个分片从不可以用变为可用的时间。

  4. waitforactive_shards:与前文类似,这里不再赘述。

  5. refresh:控制本次的变化是否能够被搜索可见。后文我将详细介绍这个参数。

  6. _source:允许控制是否以及如何在响应中返回更新的source。默认情况下,不会返回更新的source。

  7. version:更新API使用Elasticsearch的版本控制以确保文件在更新过程中不会改变,开发者可以使用version参数指定版本,如果文件匹配那么指定的文件需要更新。

注意:更新API不支持外部(版本类型external&external_gte)或强制(版本类型force)版本控制,因为它会导致Elasticsearch版本号与外部系统不同步。

好了,本文先说到这里,有问题欢迎留言讨论。

▼往期精彩回顾▼2019新年福利,新书免费送!Docker教程Redis教程SpringCloud教程Git教程MongoDB教程SpringBoot+Vue前后端分离开源项目-微人事SpringBoot+Vue前后端分离开源项目-V部落

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存