Carson
Carson

Reputation: 17751

ElasticSearch aggregation using array items as keys

Is it possible to create an aggregation by unnesting an array's elements to use as keys?

Here's an example:

Docs:

[
  {
    "languages": [ 1, 2 ],
    "value": 100
  },
  {
    "languages": [ 1 ],
    "value": 50
  }
]

its mapping:

{
    "documents": {
        "mappings": {
            "properties": {
                "languages": {
                    "type": "integer"
                },
                "value": {
                    "type": "integer"
                }
            }
        }
    }
}

and the expected output of a summing aggregation would be:

{
  1: 150,
  2: 100
}

Upvotes: 0

Views: 152

Answers (2)

Val
Val

Reputation: 217304

You can achieve what you want by using a simple terms aggregation. Array elements will be bucketed individually:

POST index/_search
{
  "aggs": {
    "languages": {
      "terms": {
        "field": "languages"
      },
      "aggs": {
        "total": {
          "sum": {
            "field": "value"
          }
        }
      }
    }
  }
}

Results:

  "aggregations" : {
    "languages" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : 1,
          "doc_count" : 2,
          "total" : {
            "value" : 150.0
          }
        },
        {
          "key" : 2,
          "doc_count" : 1,
          "total" : {
            "value" : 100.0
          }
        }
      ]
    }
  }

Upvotes: 1

Joe - Check out my books
Joe - Check out my books

Reputation: 16925

The terms agg will sum up the # of occurences. What you want instead is a script to sum up the values based on the language array items as keys:

GET langs/_search
{
  "size": 0,
  "aggs": {
    "lang_sums": {
      "scripted_metric": {
        "init_script": "state.lang_sums=[:]",
        "map_script": """
          for (def lang : doc['languages']) {
            def lang_str = lang.toString();
            def value = doc['value'].value;

            if (state.lang_sums.containsKey(lang_str)) {
              state.lang_sums[lang_str] += value;
            } else {
              state.lang_sums[lang_str] = value; 
            }
          }
        """,
        "combine_script": "return state",
        "reduce_script": "return states"
      }
    }
  }
}

yielding

{
  ...
  "aggregations":{
    "lang_sums":{
      "value":[
        {
          "lang_sums":{
            "1":150,
            "2":100
          }
        }
      ]
    }
  }
}

Upvotes: 0

Related Questions