Elasticsearch:Script aggregation (2)

在本教程中,我们将继续进行更高级的操作:重新定义 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 聚合类型。