如何通过Elasticsearch Watcher遍历聚合桶并为每个桶发送对应邮件
问题分析
你的问题核心在于transform脚本把所有桶的Top Hits数据合并成了一个列表,同时foreach循环里的邮件正文没有正确引用当前迭代的桶数据,导致每封邮件都输出了所有国家的内容。其实不需要提前用transform聚合数据,foreach本身就可以直接遍历每个桶,我们只需要在循环内处理单个桶的hits即可。
修正后的Watcher配置
{ "trigger": { "schedule": { "interval": "30m" } }, "input": { "search": { "request": { "search_type": "query_then_fetch", "indices": [ "my-idx" ], "rest_total_hits_as_int": true, "body": { // 保留你原有的聚合查询逻辑 } } } }, // 移除不必要的transform,直接在foreach中处理单个桶 "actions": { "send_email": { "foreach": "ctx.payload.aggregations.agg1.buckets", "max_iterations": 100, "email": { "profile": "standard", "to": [ "test@gmail.com" ], // 主题加入当前桶的国家名称,更直观 "subject": "Agent Report for {{ctx.payload.key}}", "body": { "html": """ <html> <body> <table style="background-color: #f9f9ff; border-collapse: collapse;"> <thead> <tr> <th style="border: 1px solid #ddd; padding: 8px;">Agent Name</th> <th style="border: 1px solid #ddd; padding: 8px;">Timestamp</th> <th style="border: 1px solid #ddd; padding: 8px;">Country</th> </tr> </thead> <tbody> {% for hit in ctx.payload.agg2.hits.hits %} <tr> <td style="border: 1px solid #ddd; padding: 8px;">{{hit._source.agent.name}}</td> <td style="border: 1px solid #ddd; padding: 8px;">{{hit._source.@timestamp}}</td> <td style="border: 1px solid #ddd; padding: 8px;">{{hit._source.country}}</td> </tr> {% endfor %} </tbody> </table> </body> </html> """ } } } } }
关键修改点说明
- 移除Transform脚本:不需要提前把所有桶的hits合并成一个列表,
foreach会遍历每个桶,每次迭代的ctx.payload就是当前的单个桶对象(比如India或USA的桶)。 - 动态邮件主题:用
{{ctx.payload.key}}获取当前桶的国家名称,让邮件主题更清晰。 - 遍历当前桶的Hits:在邮件正文中使用
{% for hit in ctx.payload.agg2.hits.hits %}循环,逐个输出当前国家下的agent信息,完全符合你需要的格式。 - 表格化展示:把内容整理成表格,比纯文本更易读,你也可以根据需求改回纯文本格式。
纯文本格式的备选正文(如果不需要表格)
如果你更倾向于纯文本格式的邮件正文,可以把body.html换成body.text:
"body": { "text": """ {% for hit in ctx.payload.agg2.hits.hits %} agent.name: "{{hit._source.agent.name}}" @timestamp: {{hit._source.@timestamp}} country: "{{hit._source.country}}" {% endfor %} """ }
内容的提问来源于stack exchange,提问作者Bhavya
相关产品推荐
相关产品推荐

