[[search-aggregations-bucket-range-aggregation]] === Range aggregation ++++ Range ++++ 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 <>. 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/\.\.\.//]