scripted_metric 聚合

使用脚本执行以提供度量输出的度量聚合。

示例:

POST ledger/_search?size=0
{
    "query" : {
        "match_all" : {}
    },
    "aggs": {
        "profit": {
            "scripted_metric": {
                "init_script" : "state.transactions = []", 
                "map_script" : "state.transactions.add(doc.type.value == 'sale' ? doc.amount.value : -1 * doc.amount.value)",
                "combine_script" : "double profit = 0; for (t in state.transactions) { profit += t } return profit",
                "reduce_script" : "double profit = 0; for (a in states) { profit += a } return profit"
            }
        }
    }
}

init_script 是一个可选的参数,其他的脚本都是必需的。

上面的聚合演示了如何使用脚本聚合来计算销售和成本交易的总利润。

其响应如下:

{
    "took": 218,
    ...
    "aggregations": {
        "profit": {
            "value": 240.0
        }
   }
}

在上面的例子中也可以指定存储的脚本,如下所示:

POST ledger/_search?size=0
{
    "aggs": {
        "profit": {
            "scripted_metric": {
                "init_script" : {
                    "id": "my_init_script"
                },
                "map_script" : {
                    "id": "my_map_script"
                },
                "combine_script" : {
                    "id": "my_combine_script"
                },
                "params": {
                    "field": "amount" 
                },
                "reduce_script" : {
                    "id": "my_reduce_script"
                }
            }
        }
    }
}

initmapcombine 脚本的脚本参数必须在全局 params 对象中指定,以便可以在脚本之间共享。

有关指定脚本的更多详细信息,请参考脚本文档

允许的返回类型

虽然在单个脚本中可以使用任何有效的脚本对象,但是脚本必须在 state 对象中仅返回或存储以下类型:

  • 原始类型(primitive types)
  • 字符串(string)
  • 映射(map,仅包含此处列出的类型的键和值)
  • 数组(array,仅包含此处列出的类型的元素)

脚本的范围

脚本化度量聚合在其执行的 4 个阶段中使用脚本:

init_script

在收集任何文件之前执行。允许聚合设置任何初始状态。

在上面的例子中, init_scriptstate 对象中创建了一个数组 transactions

map_script

每个收集的文档执行一次。这是一个必需的脚本。如果没有指定 combine_script,那么结果状态需要存储在 state 对象中。

在上面的示例中,map_script 检查 type 字段的值。 如果值为sale,则amount字段的值将被添加到transactions数组中。 如果 type 字段的值不是sale,则将 amount 字段取反的值添加到transactions中。

combine_script

文档收集完成后,在每个分片上执行一次。这是一个必需的脚本。允许聚合合并从每个分片返回的状态。

在上面的例子中,combine_script 遍历所有存储的transactions,对profit变量中的值求和,最后返回profit

reduce_script

在所有分片返回结果后,在协调节点上执行一次。 这是一个必需的脚本。 该脚本提供了对变量 states的访问,该变量是每个分片上的 combine_script 结果的数组。

在上面的例子中,reduce_script 遍历每个分片返回的 profit,对这些值求和,然后返回最终的组合的 profit(利润),该利润将在聚合的响应中返回。

工作示例

设想这样一种情况,你将下面的文档索引到一个包含两个分片的索引中:

PUT /transactions/_bulk?refresh
{"index":{"_id":1}}
{"type": "sale","amount": 80}
{"index":{"_id":2}}
{"type": "cost","amount": 10}
{"index":{"_id":3}}
{"type": "cost","amount": 30}
{"index":{"_id":4}}
{"type": "sale","amount": 130}

假设文档1和3最终在分片A上,文档2和4最终在分片B上。 下面是上例中每个阶段的汇总结果。

在 init_script 之前

state 被初始化为一个新的空对象。

"state" : {}

在 init_script 之后

在执行任何文档收集之前,这将在每个分片上运行一次,因此我们将在每个分片上有一个副本:

分片 A
"state" : {
    "transactions" : []
}
分片 B
"state" : {
    "transactions" : []
}

在 map_script 之后

每个分片收集它的文档,并在收集的每个文档上运行 map_script:

分片 A
"state" : {
    "transactions" : [ 80, -30 ]
}
分片 B
"state" : {
    "transactions" : [ -10, 130 ]
}

在 combine_script 之后

在文档收集完成后,在每个分片上执行 combine_script,并将所有 transactions 减少到每个分片的单个利润数字(通过对 transactions 数组中的值求和),该数字被传递回协调节点:

分片 A
50
分片 B
120

在 reduce_script 之后

reduce_script 接收一个 states 数组,其中包含每个分片的组合脚本的结果:

"states" : [
    50,
    120
]

它将分片的响应减少到最终的总利润(profit)数值(通过对这些值求和),并将其作为聚合的结果返回,以生成响应:

{
    ...

    "aggregations": {
        "profit": {
            "value": 170
        }
   }
}

其他参数

params

可选的。 一个对象,其内容将作为变量传递给 init_scriptmap_scriptcombine_script。 这对于允许用户控制聚合行为以及存储脚本之间的状态非常有用。 如果未指定,默认情况下相当于指定了:

"params" : {}

空桶

如果脚本化度量聚合的父桶没有收集任何文档,将从分片返回一个值为 null 的空聚合响应。 在这种情况下,reduce_scriptstates 变量将包含 null 作为来自该分片的响应。 因此,reduce_script 应该期望并处理来自分片的 null 响应。