[[search-aggregations-bucket-range-aggregation]]
=== Range aggregation
++++
<titleabbrev>Range</titleabbrev>
++++

A multi-bucket value source based aggregation that enables the user to define a set of ranges - each representing a bucket. During the aggregation process, the values extracted from each document will be checked against each bucket range and "bucket" the relevant/matching document.
Note that this aggregation includes the `from` value and excludes the `to` value for each range.

Example:

[source,console,id=range-aggregation-example]
--------------------------------------------------
GET /_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 100.0 },
          { "from": 100.0, "to": 200.0 },
          { "from": 200.0 }
        ]
      }
    }
  }
}
--------------------------------------------------
// TEST[setup:sales]
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]

Response:

[source,console-result]
--------------------------------------------------
{
  ...
  "aggregations": {
    "price_ranges": {
      "buckets": [
        {
          "key": "*-100.0",
          "to": 100.0,
          "doc_count": 2
        },
        {
          "key": "100.0-200.0",
          "from": 100.0,
          "to": 200.0,
          "doc_count": 2
        },
        {
          "key": "200.0-*",
          "from": 200.0,
          "doc_count": 3
        }
      ]
    }
  }
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\.//]

==== Keyed Response

Setting the `keyed` flag to `true` will associate a unique string key with each bucket and return the ranges as a hash rather than an array:

[source,console,id=range-aggregation-keyed-example]
--------------------------------------------------
GET /_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "keyed": true,
        "ranges": [
          { "to": 100 },
          { "from": 100, "to": 200 },
          { "from": 200 }
        ]
      }
    }
  }
}
--------------------------------------------------
// TEST[setup:sales]
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]

Response:

[source,console-result]
--------------------------------------------------
{
  ...
  "aggregations": {
    "price_ranges": {
      "buckets": {
        "*-100.0": {
          "to": 100.0,
          "doc_count": 2
        },
        "100.0-200.0": {
          "from": 100.0,
          "to": 200.0,
          "doc_count": 2
        },
        "200.0-*": {
          "from": 200.0,
          "doc_count": 3
        }
      }
    }
  }
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\.//]

It is also possible to customize the key for each range:

[source,console,id=range-aggregation-custom-keys-example]
--------------------------------------------------
GET /_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "keyed": true,
        "ranges": [
          { "key": "cheap", "to": 100 },
          { "key": "average", "from": 100, "to": 200 },
          { "key": "expensive", "from": 200 }
        ]
      }
    }
  }
}
--------------------------------------------------
// TEST[setup:sales]
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]

Response:

[source,console-result]
--------------------------------------------------
{
  ...
  "aggregations": {
    "price_ranges": {
      "buckets": {
        "cheap": {
          "to": 100.0,
          "doc_count": 2
        },
        "average": {
          "from": 100.0,
          "to": 200.0,
          "doc_count": 2
        },
        "expensive": {
          "from": 200.0,
          "doc_count": 3
        }
      }
    }
  }
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\.//]

==== Script

If the data in your documents doesn't exactly match what you'd like to aggregate,
use a <<runtime,runtime field>>. For example, if you need to
apply a particular currency conversion rate:

[source,console,id=range-aggregation-runtime-field-example]
----
GET /_search
{
  "runtime_mappings": {
    "price.euros": {
      "type": "double",
      "script": {
        "source": """
          emit(doc['price'].value * params.conversion_rate)
        """,
        "params": {
          "conversion_rate": 0.835526591
        }
      }
    }
  },
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price.euros",
        "ranges": [
          { "to": 100 },
          { "from": 100, "to": 200 },
          { "from": 200 }
        ]
      }
    }
  }
}
----
// TEST[setup:sales]
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]

//////////////////////////

[source,console-result]
----
{
  "aggregations": {
    "price_ranges": {
      "buckets": [
        {
          "key": "*-100.0",
          "to": 100.0,
          "doc_count": 2
        },
        {
          "key": "100.0-200.0",
          "from": 100.0,
          "to": 200.0,
          "doc_count": 5
        },
        {
          "key": "200.0-*",
          "from": 200.0,
          "doc_count": 0
        }
      ]
    }
  }
}
----

//////////////////////////

==== Sub Aggregations

The following example, not only "bucket" the documents to the different buckets but also computes statistics over the prices in each price range

[source,console,id=range-aggregation-sub-aggregation-example]
--------------------------------------------------
GET /_search
{
  "aggs": {
    "price_ranges": {
      "range": {
        "field": "price",
        "ranges": [
          { "to": 100 },
          { "from": 100, "to": 200 },
          { "from": 200 }
        ]
      },
      "aggs": {
        "price_stats": {
          "stats": { "field": "price" }
        }
      }
    }
  }
}
--------------------------------------------------
// TEST[setup:sales]
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]

Response:

[source,console-result]
--------------------------------------------------
{
  ...
  "aggregations": {
    "price_ranges": {
      "buckets": [
        {
          "key": "*-100.0",
          "to": 100.0,
          "doc_count": 2,
          "price_stats": {
            "count": 2,
            "min": 10.0,
            "max": 50.0,
            "avg": 30.0,
            "sum": 60.0
          }
        },
        {
          "key": "100.0-200.0",
          "from": 100.0,
          "to": 200.0,
          "doc_count": 2,
          "price_stats": {
            "count": 2,
            "min": 150.0,
            "max": 175.0,
            "avg": 162.5,
            "sum": 325.0
          }
        },
        {
          "key": "200.0-*",
          "from": 200.0,
          "doc_count": 3,
          "price_stats": {
            "count": 3,
            "min": 200.0,
            "max": 200.0,
            "avg": 200.0,
            "sum": 600.0
          }
        }
      ]
    }
  }
}
--------------------------------------------------
// TESTRESPONSE[s/\.\.\.//]
