在本教程中,我们将继续进行更高级的操作:重新定义 extended_stats 聚合的结果以及实现 scripted_metric 聚合。
准备数据
本文中的示例使用下面提供的文档,其中包含不同公司出售的单个产品类型的详细信息。 数据包括公司名称,产品名称,产品价格,产品销售市场,可销售的单位以及运输指示器。 我们首先在名为 sales 的索引:
PUT sales
{
"mappings": {
"properties": {
"companyName": {
"type": "keyword"
},
"markets": {
"type": "keyword"
},
"price": {
"type": "long"
},
"productType": {
"type": "keyword"
},
"shipped": {
"type": "boolean"
},
"units": {
"type": "long"
}
}
}
}
PUT sales/_doc/1
{
"companyName": "Nestle",
"productType": " milk",
"markets": [
"US",
"India",
"China"
],
"price": 20,
"units": 1400,
"shipped": true
}
PUT sales/_doc/2
{
"companyName": "Knor",
"productType": "milk",
"markets": [
"US",
"Korea",
"France"
],
"price": 15,
"units": 1200,
"shipped": true
}
PUT /sales/_doc/3
{
"companyName": "Britannia",
"productType": "milk",
"markets": [
"Portugal",
"India",
"Spain"
],
"price": 15,
"units": 1000,
"shipped": true
}
使用脚本修改 “extended_stats” 指标值
Elasticsearch extended_stats 聚合是其 stats 聚合的扩展。它为我们提供了许多其他指标,包括特定字段的值数计数,最大值,最小值,方差和标准偏差。
为简单起见,我们的索引仅包含代表三个公司的三个文件。但是,你可以轻松想象一个现实世界的场景,在该场景中,我们拥有众多公司,并且每个公司都有许多类型的产品。将有许多文档具有与 companyName 相同的价值,但产品种类繁多。
在这种情况下,可能有必要修改聚合中冒出的一个或多个值。假设我们需要集中精力在 extended_stats 指标中找到的特定价格值。我们如何做到这一点?
这是一个查询,它将在更改 extended_stats 指标中的价格字段时,根据 companyName 对数据进行分类:
GET sales/_search
{
"size": 0,
"aggs": {
"company_aggs": {
"terms": {
"field": "companyName",
"order": {
"_count": "desc"
}
},
"aggs": {
"price_modify": {
"extended_stats": {
"field": "price",
"script": "_value == 20 ? 1 : 0"
}
}
}
}
}
}
您是否注意到这是一个嵌套聚合? company_aggs 聚集将根据 doc_counts 的降序,根据字段 companyName 聚合文档。 在company_aggs 内部的 price_modify 聚合将对 price 字段执行 extended_stats 聚合。
作为 price_modify 聚合的一部分的脚本将检查 price 字段的值,以查看其值是否为 20。如果是,它将为 extended_stats 指标内的 price 字段分配值1; 否则,它将分配值为0。
Scripted_metric Aggregations
Scripted_metric 是使用脚本提供度量标准输出的度量标准聚合。 最重要的是,这使我们可以自由定义自己的聚合。 我们在这里如何在上下文中使用它?
再次查看上面的文档,我们现在将重点转移到这些文档中的三个字段:productType,price 和 units。 我们还注意到,只有一种类型的产品 “milk”。 假设我们需要计算此产品类型的总收入。 你会怎么做?
我们的方法是将每个文档的单位价格字段与单位字段中的值相乘,然后将所有这些结果相加。 使用脚本的方法如下:
GET sales/_search
{
"size": 0,
"aggs": {
"expected_revenue": {
"scripted_metric": {
"init_script": "state.tempArray = [];",
"map_script": "if (doc.productType.value == 'milk') { state.tempArray.add(doc.price.value*doc.units.value); }",
"combine_script": "double exRevenue = 0; for (i in state.tempArray) { exRevenue += i } return exRevenue",
"reduce_script": "double exRevenue = 0; for (j in states) { exRevenue += j } return exRevenue;"
}
}
}
}
遍历上面的查询,我们看到聚合的名称为 expected_income,并向 Elasticsearch 指定这是一个 scripted_metric 聚合。还要注意,实际上有四个脚本参数。
- init_script —如 state 构造所示,我们在聚合对象中初始化一个名为 tempArray 的数组。
- map_script —在这里我们检查特定条件或匹配项。在这里,我们检查 productType 字段值是否为 “milk”,如果满足此条件,则将 price 和 units 字段中相乘后的值的结果推送到 tempArray 中。
- combine_script —文档收集完成后,对每个分片执行一次。 这是必需的脚本。 允许聚合合并从每个分片返回的状态。运行 map_script 之后,我们得到一个包含 tempArrays 集合的聚合结构,然后将所有这些数组中的所有值迭代地合并为一个 exRevenue 数组。
- reduce_script — 所有分片均返回其结果后,在协调节点上执行一次。 这是必需的脚本。 该脚本可以访问变量状态,该状态是每个分片上 combine_script 结果的数组。 在此次要迭代中,我们将 exRevenue 数组中的所有元素加起来以获得单个字段中元素的总和。
在结果中,我们发现奶类产品销售的总收入(expected_revenue)为61000。它的计算是这样的:
20 x 1400 + 15 x 1200 + 15 x 1000 = 61000
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"expected_revenue" : {
"value" : 61000.0
}
}
}
我们鼓励你进行实验,省略 combine_script 和 reduce_script 参数并检查那些结果。然后将它们(一对一)添加回查询中,并检查这些结果又如何不同。这将帮助你扎实地了解 scripted_metrics 聚合类型。