updateByQueryRequest.setScript(new Script(
Script.DEFAULT_SCRIPT_TYPE,
Script.DEFAULT_SCRIPT_LANG,
"if(ctx._source['group_id']==null){" +
" ctx._source['group_id']= params.groupIds" +
"} else if(!ctx._source['group_id'].contains(params.groupId)) {" +
" ctx._source['group_id'].add(params.groupId)" +
"}",
params
));
1. 核心概念:ctx 是"上下文"
你可以把 ctx 想象成一个**"快递员"**。
- 当 ES 找到你要修改的那条数据时,它不会直接把数据扔给你。
- 它会把这个数据包装在一个"信封"里,交给快递员
ctx。 - 你想拿数据,就得跟快递员要:
ctx.你要的东西。
所以,ctx 就是**"当前操作的环境"** ,它是你进入当前文档的唯一入口。
2. 第二层:_source 是"原始包裹"
接上上面的比喻:
- 快递员
ctx把信封给你了。 - 信封上写着几个字:
_source。这代表**"原始数据"**。 - ES 里存的文档(Document),本质上就是一个 JSON 对象。在 ES 内部,这个 JSON 对象被存放在
_source这个字段下。
举个例子:
你存进 ES 的是一张卡片(文档):
{
"name": "张三",
"age": 25,
"group_id": ["group_001"]
}
在 ES 的底层存储里,它其实是长这样子的(简化版):
{
"_index": "profile_index",
"_type": "_doc",
"_id": "1",
"_source": { // 👈 看这里!
"name": "张三",
"age": 25,
"group_id": ["group_001"] // 👈 你的数据其实藏在这里面
}
}
所以,_source 就是那个装着你真实数据的"盒子"。
3. 第三层:['group_id'] 是"盒子里的具体东西"
- 既然数据都在
_source这个盒子里,那你想要改group_id,就得打开盒子去找。 ctx._source就是拿到了那个盒子。ctx._source['group_id']就是打开了盒子,拿出了里面叫group_id的那张纸条。
4. 连起来解释
现在我们看这行代码:
ctx._source['group_id'] = params.groupIds
翻译成人话就是:
"快递员(
ctx),请把你的'原始数据盒子'(_source)打开,找到里面叫group_id的那张纸条,把新的值(params.groupIds)写上去。"
5. 为什么不能直接写 group_id?
你可能会问:"既然我在脚本里,为什么不能直接写 group_id = ...?"
原因有两个:
- 作用域问题:脚本运行在一个沙箱里,它不知道你外面的变量叫什么。你必须明确告诉它:"我要改的是文档里的这个字段"。
- 特殊标记 :
_source是 ES 定义的元字段(Meta-field)。它告诉 ES:"我要修改的是文档的主体内容,而不是文档的 ID 或者其他元数据"。