如何使用Elasticsearch查询原子交换同索引不同文档的指定字段值
解决方案
你可以通过Elasticsearch的_update_by_query接口结合Painless脚本,原子性完成id交换,全程不会影响其他文档:
执行命令
POST /Employee/_update_by_query { "query": { "terms": { "emp_id": ["1", "2"] } }, "script": { "lang": "painless", "source": """ if (ctx._source.emp_id == '1') { ctx._source.emp_id = '2'; } else if (ctx._source.emp_id == '2') { ctx._source.emp_id = '1'; } """ } }
说明
- 筛选逻辑仅命中
emp_id为1和2的文档,3、4等其他id的文档完全不会被改动 - 每个文档的更新操作本身是原子性的,不会出现中间状态下id重复的问题
- 如果存在并发修改这两个文档的场景,可添加
"conflicts": "proceed"参数跳过冲突,执行后查看响应中的冲突计数,有冲突重新执行即可
提示:如果你的
emp_id字段是数值类型而非字符串,删除脚本中'1'、'2'的单引号即可。
强一致可选方案
如果你需要严格保证两个文档要么同时更新成功要么都不更新,可以使用乐观锁批量更新方案:
- 先查询两个文档的当前版本号:
GET /Employee/_search { "query": { "terms": {"emp_id": ["1","2"]} }, "version": true }
- 携带版本号执行批量更新,只要任意一个文档版本不匹配,整个请求失败不会产生部分更新:
POST /Employee/_bulk {"update": {"_id": "<替换为emp_id=1的文档ID>", "version": <替换为对应版本号>}} {"doc": {"emp_id": "2"}} {"update": {"_id": "<替换为emp_id=2的文档ID>", "version": <替换为对应版本号>}} {"doc": {"emp_id": "1"}}
结果验证
执行完成后执行如下查询确认交换结果:
GET /Employee/_search { "query": { "terms": {"emp_id": ["1","2"]} } }
内容的提问来源于stack exchange,提问作者ARods
相关产品推荐
相关产品推荐

