Skip to content

Commit 936cdf5

Browse files
authored
[ES|QL] Support subset of metrics in agg metric double (#121805) (#122087)
This commit adds adds support for mappings containing only a subset of metrics in aggregate metric double (i.e. only sum and value_count, or just max, etc) as well as tests for grouped aggregations on aggregate metric double.
1 parent 77608f0 commit 936cdf5

File tree

5 files changed

+213
-23
lines changed

5 files changed

+213
-23
lines changed

docs/changelog/121805.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 121805
2+
summary: Support subset of metrics in aggregate metric double
3+
area: "ES|QL"
4+
type: enhancement
5+
issues: []

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,12 @@ public enum Cap {
671671
/**
672672
* Support for aggregate_metric_double type
673673
*/
674-
AGGREGATE_METRIC_DOUBLE(AGGREGATE_METRIC_DOUBLE_FEATURE_FLAG.isEnabled());
674+
AGGREGATE_METRIC_DOUBLE(AGGREGATE_METRIC_DOUBLE_FEATURE_FLAG),
675+
676+
/**
677+
* Support for partial subset of metrics in aggregate_metric_double type
678+
*/
679+
AGGREGATE_METRIC_DOUBLE_PARTIAL_SUBMETRICS(AGGREGATE_METRIC_DOUBLE_FEATURE_FLAG);
675680

676681
private final boolean enabled;
677682

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/AggregateMapper.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,6 @@ private static Stream<AggDef> groupingAndNonGrouping(Tuple<Class<?>, Tuple<Strin
214214
if (tuple.v1().isAssignableFrom(Rate.class)) {
215215
// rate doesn't support non-grouping aggregations
216216
return Stream.of(new AggDef(tuple.v1(), tuple.v2().v1(), tuple.v2().v2(), true));
217-
} else if (tuple.v2().v1().equals("AggregateMetricDouble")) {
218-
// TODO: support grouping aggregations for aggregate metric double
219-
return Stream.of(new AggDef(tuple.v1(), tuple.v2().v1(), tuple.v2().v2(), false));
220217
} else {
221218
return Stream.of(
222219
new AggDef(tuple.v1(), tuple.v2().v1(), tuple.v2().v2(), true),

x-pack/plugin/mapper-aggregate-metric/src/main/java/org/elasticsearch/xpack/aggregatemetric/mapper/AggregateMetricDoubleFieldMapper.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -540,9 +540,6 @@ public AllReader reader(LeafReaderContext context) throws IOException {
540540
NumericDocValues sumValues = getNumericDocValues(sumFieldType, context.reader());
541541
NumericDocValues valueCountValues = getNumericDocValues(countFieldType, context.reader());
542542

543-
if (minValues == null || maxValues == null || sumValues == null || valueCountValues == null) {
544-
throw new UnsupportedOperationException("Must have all subfields to use aggregate double metric in ESQL");
545-
}
546543
return new BlockDocValuesReader() {
547544

548545
private int docID = -1;
@@ -576,13 +573,13 @@ private void copyDoubleValuesToBuilder(Docs docs, BlockLoader.DoubleBuilder buil
576573
if (doc < lastDoc) {
577574
throw new IllegalStateException("docs within same block must be in order");
578575
}
579-
if (values.advanceExact(doc)) {
576+
if (values == null || values.advanceExact(doc) == false) {
577+
builder.appendNull();
578+
} else {
580579
double value = NumericUtils.sortableLongToDouble(values.longValue());
581580
lastDoc = doc;
582581
this.docID = doc;
583582
builder.appendDouble(value);
584-
} else {
585-
builder.appendNull();
586583
}
587584
}
588585
}
@@ -595,13 +592,13 @@ private void copyIntValuesToBuilder(Docs docs, BlockLoader.IntBuilder builder, N
595592
if (doc < lastDoc) {
596593
throw new IllegalStateException("docs within same block must be in order");
597594
}
598-
if (values.advanceExact(doc)) {
595+
if (values == null || values.advanceExact(doc) == false) {
596+
builder.appendNull();
597+
} else {
599598
int value = Math.toIntExact(values.longValue());
600599
lastDoc = doc;
601600
this.docID = doc;
602601
builder.appendInt(value);
603-
} else {
604-
builder.appendNull();
605602
}
606603
}
607604
}
@@ -610,10 +607,10 @@ private void copyIntValuesToBuilder(Docs docs, BlockLoader.IntBuilder builder, N
610607
public void read(int docId, StoredFields storedFields, Builder builder) throws IOException {
611608
var blockBuilder = (AggregateMetricDoubleBuilder) builder;
612609
this.docID = docId;
613-
read(docId, blockBuilder);
610+
readSingleRow(docId, blockBuilder);
614611
}
615612

616-
private void read(int docId, AggregateMetricDoubleBuilder builder) throws IOException {
613+
private void readSingleRow(int docId, AggregateMetricDoubleBuilder builder) throws IOException {
617614
if (minValues.advanceExact(docId)) {
618615
builder.min().appendDouble(NumericUtils.sortableLongToDouble(minValues.longValue()));
619616
} else {

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/40_tsdb.yml

Lines changed: 194 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ setup:
8282
time_series_dimension: true
8383
agg_metric:
8484
type: aggregate_metric_double
85-
# TODO: tests with a subset of metrics
8685
metrics: [ min, max, sum, value_count ]
8786
default_metric: max
8887
k8s:
@@ -104,6 +103,80 @@ setup:
104103
- '{"@timestamp": "2021-04-28T18:50:04.467Z", "dim": "A", "agg_metric": {"max": 10, "min": -1, "sum": 20, "value_count": 5}}'
105104
- '{"index": {}}'
106105
- '{"@timestamp": "2021-04-28T18:50:24.467Z", "dim": "B", "agg_metric": {"max": 20, "min": 3, "sum": 50, "value_count": 7}}'
106+
- '{"index": {}}'
107+
- '{"@timestamp": "2021-04-28T18:50:44.467Z", "dim": "B", "agg_metric": {"max": 17, "min": -5, "sum": 33, "value_count": 9}}'
108+
109+
- do:
110+
indices.create:
111+
index: test3
112+
body:
113+
settings:
114+
index:
115+
mode: time_series
116+
routing_path: [ k8s.pod.uid ]
117+
time_series:
118+
start_time: 2021-04-28T00:00:00Z
119+
end_time: 2021-04-29T00:00:00Z
120+
mappings:
121+
properties:
122+
"@timestamp":
123+
type: date
124+
agg_metric:
125+
type: aggregate_metric_double
126+
metrics: [ min, max ]
127+
default_metric: min
128+
k8s:
129+
properties:
130+
pod:
131+
properties:
132+
uid:
133+
type: keyword
134+
time_series_dimension: true
135+
- do:
136+
bulk:
137+
refresh: true
138+
index: test3
139+
body:
140+
- '{"index": {}}'
141+
- '{"@timestamp": "2021-04-28T19:50:04.467Z", "agg_metric": {"max": 1, "min": -3}, "k8s": {"pod": {"uid":"947e4ced-1786-4e53-9e0c-5c447e959507"}}}'
142+
- '{"index": {}}'
143+
- '{"@timestamp": "2021-04-28T19:50:24.467Z", "agg_metric": {"max": 10, "min": 3}, "k8s": {"pod": {"uid":"947e4ced-1786-4e53-9e0c-5c447e959507"}}}'
144+
- '{"index": {}}'
145+
- '{"@timestamp": "2021-04-28T19:50:44.467Z", "agg_metric": {"max": 17, "min": 2}, "k8s": {"pod": {"uid":"df3145b3-0563-4d3b-a0f7-897eb2876ea9"}}}'
146+
147+
- do:
148+
indices.create:
149+
index: test4
150+
body:
151+
settings:
152+
index:
153+
mode: time_series
154+
routing_path: [ k8s.pod.uid ]
155+
time_series:
156+
start_time: 2021-04-28T00:00:00Z
157+
end_time: 2021-04-29T00:00:00Z
158+
mappings:
159+
properties:
160+
"@timestamp":
161+
type: date
162+
agg_metric:
163+
type: aggregate_metric_double
164+
metrics: [ sum, value_count ]
165+
default_metric: sum
166+
k8s:
167+
properties:
168+
pod:
169+
properties:
170+
uid:
171+
type: keyword
172+
time_series_dimension: true
173+
- do:
174+
bulk:
175+
refresh: true
176+
index: test4
177+
body:
178+
- '{"index": {}}'
179+
- '{"@timestamp": "2021-04-28T23:50:04.467Z", "agg_metric": {"sum": 1, "value_count": 10}, "k8s": {"pod": {"uid":"947e4ced-1786-4e53-9e0c-5c447e959507"}}}'
107180

108181
---
109182
load everything:
@@ -228,7 +301,7 @@ from doc with aggregate_metric_double:
228301
- match: {columns.3.type: "ip"}
229302
- match: {columns.4.name: "k8s.pod.network.tx"}
230303
- match: {columns.4.type: "long"}
231-
- length: {values: 2}
304+
- length: {values: 3}
232305

233306
---
234307
stats on aggregate_metric_double:
@@ -257,12 +330,12 @@ stats on aggregate_metric_double:
257330
- match: {columns.3.name: "count(agg_metric)"}
258331
- match: {columns.3.type: "long"}
259332
- match: {values.0.0: 20.0}
260-
- match: {values.0.1: -1.0}
261-
- match: {values.0.2: 70.0}
262-
- match: {values.0.3: 12.0}
333+
- match: {values.0.1: -5.0}
334+
- match: {values.0.2: 103.0}
335+
- match: {values.0.3: 21.0}
263336

264337
---
265-
from index pattern unsupported counter:
338+
grouping stats on aggregate_metric_double:
266339
- requires:
267340
test_runner_features: [capabilities]
268341
capabilities:
@@ -271,6 +344,119 @@ from index pattern unsupported counter:
271344
parameters: []
272345
capabilities: [aggregate_metric_double]
273346
reason: "Support for aggregate_metric_double"
347+
- do:
348+
allowed_warnings_regex:
349+
- "No limit defined, adding default limit of \\[.*\\]"
350+
esql.query:
351+
body:
352+
query: "FROM test2
353+
| STATS max(agg_metric), min(agg_metric), sum(agg_metric), count(agg_metric) BY dim
354+
| SORT dim"
355+
- length: {values: 2}
356+
- length: {values.0: 5}
357+
- match: {columns.0.name: "max(agg_metric)"}
358+
- match: {columns.0.type: "double"}
359+
- match: {columns.1.name: "min(agg_metric)"}
360+
- match: {columns.1.type: "double"}
361+
- match: {columns.2.name: "sum(agg_metric)"}
362+
- match: {columns.2.type: "double"}
363+
- match: {columns.3.name: "count(agg_metric)"}
364+
- match: {columns.3.type: "long"}
365+
- match: {columns.4.name: "dim"}
366+
- match: {columns.4.type: "keyword"}
367+
- match: {values.0.0: 10.0}
368+
- match: {values.0.1: -1.0}
369+
- match: {values.0.2: 20.0}
370+
- match: {values.0.3: 5.0}
371+
- match: {values.0.4: "A"}
372+
- match: {values.1.0: 20.0}
373+
- match: {values.1.1: -5.0}
374+
- match: {values.1.2: 83.0}
375+
- match: {values.1.3: 16.0}
376+
- match: {values.1.4: "B"}
377+
378+
---
379+
stats on aggregate_metric_double with partial submetrics:
380+
- requires:
381+
test_runner_features: [capabilities]
382+
capabilities:
383+
- method: POST
384+
path: /_query
385+
parameters: []
386+
capabilities: [aggregate_metric_double_partial_submetrics]
387+
reason: "Support for partial submetrics in aggregate_metric_double"
388+
- do:
389+
allowed_warnings_regex:
390+
- "No limit defined, adding default limit of \\[.*\\]"
391+
esql.query:
392+
body:
393+
query: 'FROM test3 | STATS max(agg_metric), min(agg_metric), sum(agg_metric), count(agg_metric) BY k8s.pod.uid | SORT k8s.pod.uid'
394+
395+
- length: {values: 2}
396+
- length: {values.0: 5}
397+
- match: {columns.0.name: "max(agg_metric)"}
398+
- match: {columns.0.type: "double"}
399+
- match: {columns.1.name: "min(agg_metric)"}
400+
- match: {columns.1.type: "double"}
401+
- match: {columns.2.name: "sum(agg_metric)"}
402+
- match: {columns.2.type: "double"}
403+
- match: {columns.3.name: "count(agg_metric)"}
404+
- match: {columns.3.type: "long"}
405+
- match: {columns.4.name: "k8s.pod.uid"}
406+
- match: {columns.4.type: "keyword"}
407+
- match: {values.0.0: 10.0}
408+
- match: {values.0.1: -3.0}
409+
- match: {values.0.2: null}
410+
- match: {values.0.3: null}
411+
- match: {values.0.4: "947e4ced-1786-4e53-9e0c-5c447e959507"}
412+
- match: {values.1.0: 17.0}
413+
- match: {values.1.1: 2.0}
414+
- match: {values.1.2: null}
415+
- match: {values.1.3: null}
416+
- match: {values.1.4: "df3145b3-0563-4d3b-a0f7-897eb2876ea9"}
417+
418+
---
419+
stats on aggregate_metric_double missing min and max:
420+
- requires:
421+
test_runner_features: [ capabilities ]
422+
capabilities:
423+
- method: POST
424+
path: /_query
425+
parameters: [ ]
426+
capabilities: [ aggregate_metric_double_partial_submetrics ]
427+
reason: "Support for partial submetrics in aggregate_metric_double"
428+
- do:
429+
allowed_warnings_regex:
430+
- "No limit defined, adding default limit of \\[.*\\]"
431+
esql.query:
432+
body:
433+
query: 'FROM test4 | STATS max(agg_metric), min(agg_metric), sum(agg_metric), count(agg_metric)'
434+
435+
- length: {values: 1}
436+
- length: {values.0: 4}
437+
- match: {columns.0.name: "max(agg_metric)"}
438+
- match: {columns.0.type: "double"}
439+
- match: {columns.1.name: "min(agg_metric)"}
440+
- match: {columns.1.type: "double"}
441+
- match: {columns.2.name: "sum(agg_metric)"}
442+
- match: {columns.2.type: "double"}
443+
- match: {columns.3.name: "count(agg_metric)"}
444+
- match: {columns.3.type: "long"}
445+
- match: {values.0.0: null}
446+
- match: {values.0.1: null}
447+
- match: {values.0.2: 1.0}
448+
- match: {values.0.3: 10}
449+
450+
---
451+
from index pattern unsupported counter:
452+
- requires:
453+
test_runner_features: [capabilities]
454+
capabilities:
455+
- method: POST
456+
path: /_query
457+
parameters: []
458+
capabilities: [aggregate_metric_double_partial_submetrics]
459+
reason: "Support for partial submetrics in aggregate_metric_double"
274460
- do:
275461
allowed_warnings_regex:
276462
- "No limit defined, adding default limit of \\[.*\\]"
@@ -296,7 +482,7 @@ from index pattern unsupported counter:
296482
- match: {columns.7.type: "keyword"}
297483
- match: {columns.8.name: "metricset"}
298484
- match: {columns.8.type: "keyword"}
299-
- length: {values: 10}
485+
- length: {values: 15}
300486

301487
---
302488
from index pattern explicit counter use:
@@ -317,7 +503,7 @@ from index pattern explicit counter use:
317503
query: 'FROM test* | keep *.tx'
318504
- match: {columns.0.name: "k8s.pod.network.tx"}
319505
- match: {columns.0.type: "unsupported"}
320-
- length: {values: 10}
506+
- length: {values: 15}
321507

322508
---
323509
_source:

0 commit comments

Comments
 (0)