首页 > 解决方案 > Elasticsearch:将每个嵌套元素乘以聚合

问题描述

让我们想象一个由 2 个文档组成的索引,如下所示:

doc1 = {
       "x":1,
       "y":[{brand:b1, value:1},
            {brand:b2, value:2}]
       },

doc2 = {
       "x":2,
       "y":[{brand:b1, value:0},
            {brand:b2, value:3}]
       }

是否可以将每个文档的 y 的每个值乘以 x,然后根据品牌术语进行总和聚合以获得此结果:

b1: 1
b2: 8

如果没有,是否可以使用任何其他映射类型来完成?

标签: elasticsearch

解决方案


这是一个高度自定义的用例,所以我认为没有某种预先优化的映射。

我建议如下:

建立一个索引 w y/ nested

PUT xy/
{"mappings":{"properties":{"y":{"type":"nested"}}}}

从您的示例中提取文档:

POST xy/_doc
{"x":1,"y":[{"brand":"b1","value":1},{"brand":"b2","value":2}]}

POST xy/_doc
{"x":2,"y":[{"brand":"b1","value":0},{"brand":"b2","value":3}]}

使用scripted_metric聚合来计算产品并将它们添加到 shared 中HashMap

GET xy/_search
{
  "size": 0,
  "aggs": {
    "multiply_and_add": {
      "scripted_metric": {
        "init_script": "state.by_brands = [:]",
        "map_script": """
          def x = params._source['x'];
          for (def brand_pair : params._source['y']) {
            def brand = brand_pair['brand'];
            def product = x * brand_pair['value'];
            if (state.by_brands.containsKey(brand)) {
              state.by_brands[brand] += product;
            } else {
              state.by_brands[brand] = product;
            }
          }
        """,
        "combine_script": "return state",
        "reduce_script": "return states"
      }
    }
  }
}

这将产生一些类似的东西

{
  ... 
  "aggregations":{
    "multiply_and_add":{
      "value":[
        {
          "by_brands":{   <----
            "b2":8,            
            "b1":1
          }
        }
      ]
    }
  }
}

更新

combine_script可能看起来像这样:

def combined_states = [:];
for (def state : states) {
  for (def brand_pair : state['by_brands'].entrySet()) {
    def key = brand_pair.getKey();
    def value = brand_pair.getValue();
    
    if (combined_states.containsKey(key)) {
      combined_states[key] += (float)value;
      break;
    } 
    
    combined_states[key] = (float)value
  }
}

推荐阅读