[[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/\.\.\.//]