Elasticsearch 6.8.13自定义Upsert:Painless脚本控制索引决策问题
问题分析与解决办法
问题原因
你设置ctx.op = 'index'后,Elasticsearch的Update API并不会自动使用upsert参数中的文档内容:
- 当目标文档存在时,
ctx._source有值,但你的脚本没有对ctx._source做任何字段赋值操作,执行index操作时只是重新索引原文档,字段自然不会更新; - 当目标文档不存在时,
ctx._source为null,此时设置ctx.op = 'index'会触发索引操作,但因为没有指定要索引的文档内容,最终不会生成有效文档。
而你最初的脚本能正常工作,是因为明确对ctx._source的字段做了赋值,ES能拿到更新后的内容执行操作。
解决方案
方案1:脚本中直接指定文档内容
将需要插入/更新的文档作为参数传入脚本,在脚本中明确赋值给ctx._source:
修改后的Painless脚本
if (condition) { ctx.op = 'noop'; } else { // 将传入的文档内容赋值给ctx._source,覆盖或创建文档 ctx._source = params.doc; ctx.op = 'index'; }
修改后的Java代码
private UpdateRequest createUpsertRequest(Map<String, Object> params) { // 封装脚本参数,把要操作的文档单独放入doc字段 Map<String, Object> scriptParams = new HashMap<>(params); scriptParams.put("doc", params); return new UpdateRequest(elasticProperties.getIndex(), "_doc", params.get(DOCUMENT_ID_KEY).toString()) .script(new Script(ScriptType.INLINE, "painless", UPSERT_SCRIPT, scriptParams)) // 保留upsert作为兜底,避免极端情况脚本执行失败 .upsert(params); }
方案2:结合doc与doc_as_upsert参数
利用Update API的doc参数存储要更新的内容,开启doc_as_upsert让ES自动处理文档不存在的情况,脚本只负责控制是否执行操作:
修改后的Painless脚本
if (condition) { ctx.op = 'noop'; } // 不需要设置ctx.op,默认执行update操作;开启doc_as_upsert后,文档不存在时自动插入doc内容
修改后的Java代码
private UpdateRequest createUpsertRequest(Map<String, Object> params) { return new UpdateRequest(elasticProperties.getIndex(), "_doc", params.get(DOCUMENT_ID_KEY).toString()) .script(new Script(ScriptType.INLINE, "painless", UPSERT_SCRIPT, params)) .doc(params) // 设置要更新的文档内容 .docAsUpsert(true); // 开启自动upsert,文档不存在时插入doc内容 }
如果需要强制替换整个文档(而非增量更新),可以在脚本的else分支中添加ctx.op = 'index',配合doc参数同样生效。
内容的提问来源于stack exchange,提问作者Frofike
相关产品推荐
相关产品推荐

