Skip to content

Commit f70a599

Browse files
authored
[7.x] Add rate aggregation (#61369) (#61554)
Adds a new rate aggregation that can calculate a document rate for buckets of a date_histogram. Closes #60674
1 parent 8258510 commit f70a599

File tree

25 files changed

+1839
-42
lines changed

25 files changed

+1839
-42
lines changed

docs/reference/aggregations/metrics.asciidoc

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ include::metrics/valuecount-aggregation.asciidoc[]
5151

5252
include::metrics/t-test-aggregation.asciidoc[]
5353

54+
include::metrics/rate-aggregation.asciidoc[]
5455

5556

5657

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
[role="xpack"]
2+
[testenv="basic"]
3+
[[search-aggregations-metrics-rate-aggregation]]
4+
=== Rate Aggregation
5+
6+
A `rate` metrics aggregation can be used only inside a `date_histogram` and calculates a rate of documents or a field in each
7+
`date_histogram` bucket.
8+
9+
==== Syntax
10+
11+
A `rate` aggregation looks like this in isolation:
12+
13+
[source,js]
14+
--------------------------------------------------
15+
{
16+
"rate": {
17+
"unit": "month",
18+
"field": "requests"
19+
}
20+
}
21+
--------------------------------------------------
22+
// NOTCONSOLE
23+
24+
The following request will group all sales records into monthly bucket and than convert the number of sales transaction in each bucket
25+
into per annual sales rate.
26+
27+
[source,console]
28+
--------------------------------------------------
29+
GET sales/_search
30+
{
31+
"size": 0,
32+
"aggs": {
33+
"by_date": {
34+
"date_histogram": {
35+
"field": "date",
36+
"calendar_interval": "month" <1>
37+
},
38+
"aggs": {
39+
"my_rate": {
40+
"rate": {
41+
"unit": "year" <2>
42+
}
43+
}
44+
}
45+
}
46+
}
47+
}
48+
--------------------------------------------------
49+
// TEST[setup:sales]
50+
<1> Histogram is grouped by month.
51+
<2> But the rate is converted into annual rate.
52+
53+
The response will return the annual rate of transaction in each bucket. Since there are 12 months per year, the annual rate will
54+
be automatically calculated by multiplying monthly rate by 12.
55+
56+
[source,console-result]
57+
--------------------------------------------------
58+
{
59+
...
60+
"aggregations" : {
61+
"by_date" : {
62+
"buckets" : [
63+
{
64+
"key_as_string" : "2015/01/01 00:00:00",
65+
"key" : 1420070400000,
66+
"doc_count" : 3,
67+
"my_rate" : {
68+
"value" : 36.0
69+
}
70+
},
71+
{
72+
"key_as_string" : "2015/02/01 00:00:00",
73+
"key" : 1422748800000,
74+
"doc_count" : 2,
75+
"my_rate" : {
76+
"value" : 24.0
77+
}
78+
},
79+
{
80+
"key_as_string" : "2015/03/01 00:00:00",
81+
"key" : 1425168000000,
82+
"doc_count" : 2,
83+
"my_rate" : {
84+
"value" : 24.0
85+
}
86+
}
87+
]
88+
}
89+
}
90+
}
91+
--------------------------------------------------
92+
// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/]
93+
94+
Instead of counting the number of documents, it is also possible to calculate a sum of all values of the fields in the documents in each
95+
bucket. The following request will group all sales records into monthly bucket and than calculate the total monthly sales and convert them
96+
into average daily sales.
97+
98+
[source,console]
99+
--------------------------------------------------
100+
GET sales/_search
101+
{
102+
"size": 0,
103+
"aggs": {
104+
"by_date": {
105+
"date_histogram": {
106+
"field": "date",
107+
"calendar_interval": "month" <1>
108+
},
109+
"aggs": {
110+
"avg_price": {
111+
"rate": {
112+
"field": "price", <2>
113+
"unit": "day" <3>
114+
}
115+
}
116+
}
117+
}
118+
}
119+
}
120+
--------------------------------------------------
121+
// TEST[setup:sales]
122+
<1> Histogram is grouped by month.
123+
<2> Calculate sum of all sale prices
124+
<3> Convert to average daily sales
125+
126+
The response will contain the average daily sale prices for each month.
127+
128+
[source,console-result]
129+
--------------------------------------------------
130+
{
131+
...
132+
"aggregations" : {
133+
"by_date" : {
134+
"buckets" : [
135+
{
136+
"key_as_string" : "2015/01/01 00:00:00",
137+
"key" : 1420070400000,
138+
"doc_count" : 3,
139+
"avg_price" : {
140+
"value" : 17.741935483870968
141+
}
142+
},
143+
{
144+
"key_as_string" : "2015/02/01 00:00:00",
145+
"key" : 1422748800000,
146+
"doc_count" : 2,
147+
"avg_price" : {
148+
"value" : 2.142857142857143
149+
}
150+
},
151+
{
152+
"key_as_string" : "2015/03/01 00:00:00",
153+
"key" : 1425168000000,
154+
"doc_count" : 2,
155+
"avg_price" : {
156+
"value" : 12.096774193548388
157+
}
158+
}
159+
]
160+
}
161+
}
162+
}
163+
--------------------------------------------------
164+
// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/]
165+
166+
167+
==== Relationship between bucket sizes and rate
168+
169+
The `rate` aggregation supports all rate that can be used <<calendar_intervals,calendar_intervals parameter>> of `date_histogram`
170+
aggregation. The specified rate should compatible with the `date_histogram` aggregation interval, i.e. it should be possible to
171+
convert the bucket size into the rate. By default the interval of the `date_histogram` is used.
172+
173+
`"rate": "second"`:: compatible with all intervals
174+
`"rate": "minute"`:: compatible with all intervals
175+
`"rate": "hour"`:: compatible with all intervals
176+
`"rate": "day"`:: compatible with all intervals
177+
`"rate": "week"`:: compatible with all intervals
178+
`"rate": "month"`:: compatible with only with `month`, `quarter` and `year` calendar intervals
179+
`"rate": "quarter"`:: compatible with only with `month`, `quarter` and `year` calendar intervals
180+
`"rate": "year"`:: compatible with only with `month`, `quarter` and `year` calendar intervals
181+
182+
==== Script
183+
184+
The `rate` aggregation also supports scripting. For example, if we need to adjust out prices before calculating rates, we could use
185+
a script to recalculate them on-the-fly:
186+
187+
[source,console]
188+
--------------------------------------------------
189+
GET sales/_search
190+
{
191+
"size": 0,
192+
"aggs": {
193+
"by_date": {
194+
"date_histogram": {
195+
"field": "date",
196+
"calendar_interval": "month"
197+
},
198+
"aggs": {
199+
"avg_price": {
200+
"rate": {
201+
"script": { <1>
202+
"lang": "painless",
203+
"source": "doc['price'].value * params.adjustment",
204+
"params": {
205+
"adjustment": 0.9 <2>
206+
}
207+
}
208+
}
209+
}
210+
}
211+
}
212+
}
213+
}
214+
--------------------------------------------------
215+
// TEST[setup:sales]
216+
217+
<1> The `field` parameter is replaced with a `script` parameter, which uses the
218+
script to generate values which percentiles are calculated on.
219+
<2> Scripting supports parameterized input just like any other script.
220+
221+
[source,console-result]
222+
--------------------------------------------------
223+
{
224+
...
225+
"aggregations" : {
226+
"by_date" : {
227+
"buckets" : [
228+
{
229+
"key_as_string" : "2015/01/01 00:00:00",
230+
"key" : 1420070400000,
231+
"doc_count" : 3,
232+
"avg_price" : {
233+
"value" : 495.0
234+
}
235+
},
236+
{
237+
"key_as_string" : "2015/02/01 00:00:00",
238+
"key" : 1422748800000,
239+
"doc_count" : 2,
240+
"avg_price" : {
241+
"value" : 54.0
242+
}
243+
},
244+
{
245+
"key_as_string" : "2015/03/01 00:00:00",
246+
"key" : 1425168000000,
247+
"doc_count" : 2,
248+
"avg_price" : {
249+
"value" : 337.5
250+
}
251+
}
252+
]
253+
}
254+
}
255+
}
256+
--------------------------------------------------
257+
// TESTRESPONSE[s/\.\.\./"took": $body.took,"timed_out": false,"_shards": $body._shards,"hits": $body.hits,/]

docs/reference/rest-api/usage.asciidoc

+10-2
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,16 @@ GET /_xpack/usage
279279
"analytics" : {
280280
"available" : true,
281281
"enabled" : true,
282-
...
282+
"stats": {
283+
"boxplot_usage" : 0,
284+
"top_metrics_usage" : 0,
285+
"normalize_usage" : 0,
286+
"cumulative_cardinality_usage" : 0,
287+
"t_test_usage" : 0,
288+
"rate_usage" : 0,
289+
"string_stats_usage" : 0,
290+
"moving_percentiles_usage" : 0
291+
}
283292
},
284293
"data_streams" : {
285294
"available" : true,
@@ -294,7 +303,6 @@ GET /_xpack/usage
294303
// TESTRESPONSE[s/"eql" : \{[^\}]*\},/"eql" : $body.$_path,/]
295304
// TESTRESPONSE[s/"ilm" : \{[^\}]*\},/"ilm" : $body.$_path,/]
296305
// TESTRESPONSE[s/"slm" : \{[^\}]*\},/"slm" : $body.$_path,/]
297-
// TESTRESPONSE[s/"analytics" : \{[^\}]*\}/"analytics" : $body.$_path/]
298306
// TESTRESPONSE[s/ : true/ : $body.$_path/]
299307
// TESTRESPONSE[s/ : false/ : $body.$_path/]
300308
// TESTRESPONSE[s/ : (\-)?[0-9]+/ : $body.$_path/]

0 commit comments

Comments
 (0)