OpenSearch/docs/reference/aggregations/bucket/range-aggregation.asciidoc
Zachary Tong 715c90bf7d Aggs must specify a field or script (or both) (#52226)
This adds a validation to VSParserHelper to ensure that a field or
script or both are specified by the user.  This is technically
required today already, but throws an exception much deeper
in the agg framework and has a very unintuitive error for the user
(as well as eating more resources instead of failing early)
2020-04-23 19:23:41 -04:00

382 lines
9.7 KiB
Plaintext

[[search-aggregations-bucket-range-aggregation]]
=== Range Aggregation
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]
--------------------------------------------------
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]
--------------------------------------------------
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]
--------------------------------------------------
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
Range aggregation accepts a `script` parameter. This parameter allows to defined an inline `script` that
will be executed during aggregation execution.
The following example shows how to use an `inline` script with the `painless` script language and no script parameters:
[source,console]
--------------------------------------------------
GET /_search
{
"aggs" : {
"price_ranges" : {
"range" : {
"script" : {
"lang": "painless",
"source": "doc['price'].value"
},
"ranges" : [
{ "to" : 100 },
{ "from" : 100, "to" : 200 },
{ "from" : 200 }
]
}
}
}
}
--------------------------------------------------
It is also possible to use stored scripts. Here is a simple stored script:
[source,console]
--------------------------------------------------
POST /_scripts/convert_currency
{
"script": {
"lang": "painless",
"source": "doc[params.field].value * params.conversion_rate"
}
}
--------------------------------------------------
// TEST[setup:sales]
And this new stored script can be used in the range aggregation like this:
[source,console]
--------------------------------------------------
GET /_search
{
"aggs" : {
"price_ranges" : {
"range" : {
"script" : {
"id": "convert_currency", <1>
"params": { <2>
"field": "price",
"conversion_rate": 0.835526591
}
},
"ranges" : [
{ "from" : 0, "to" : 100 },
{ "from" : 100 }
]
}
}
}
}
--------------------------------------------------
// TEST[s/GET \/_search/GET \/_search\?filter_path=aggregations/]
// TEST[continued]
<1> Id of the stored script
<2> Parameters to use when executing the stored script
//////////////////////////
[source,console-result]
--------------------------------------------------
{
"aggregations": {
"price_ranges" : {
"buckets": [
{
"key" : "0.0-100.0",
"from" : 0.0,
"to" : 100.0,
"doc_count" : 2
},
{
"key" : "100.0-*",
"from" : 100.0,
"doc_count" : 5
}
]
}
}
}
--------------------------------------------------
//////////////////////////
==== Value Script
Lets say the product prices are in USD but we would like to get the price ranges in EURO. We can use value script to convert the prices prior the aggregation (assuming conversion rate of 0.8)
[source,console]
--------------------------------------------------
GET /sales/_search
{
"aggs" : {
"price_ranges" : {
"range" : {
"field" : "price",
"script" : {
"source": "_value * params.conversion_rate",
"params" : {
"conversion_rate" : 0.8
}
},
"ranges" : [
{ "to" : 35 },
{ "from" : 35, "to" : 70 },
{ "from" : 70 }
]
}
}
}
}
--------------------------------------------------
// TEST[setup:sales]
==== 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]
--------------------------------------------------
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/\.\.\.//]