Skip to content

Commit 6ae5622

Browse files
authored
Add block loader tests for aggregate_metric_double (#127119) (#127154)
1 parent ffe75ea commit 6ae5622

File tree

8 files changed

+308
-1
lines changed

8 files changed

+308
-1
lines changed

test/framework/src/main/java/org/elasticsearch/index/mapper/TestBlock.java

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.io.IOException;
1717
import java.io.UncheckedIOException;
1818
import java.util.ArrayList;
19+
import java.util.HashMap;
1920
import java.util.List;
2021

2122
import static org.junit.Assert.assertNotNull;
@@ -150,7 +151,7 @@ public SingletonOrdsBuilder appendOrd(int value) {
150151

151152
@Override
152153
public BlockLoader.AggregateMetricDoubleBuilder aggregateMetricDoubleBuilder(int count) {
153-
return null;
154+
return new AggregateMetricDoubleBlockBuilder();
154155
}
155156
};
156157
}
@@ -243,4 +244,98 @@ public void close() {
243244
// TODO assert that we close the test block builders
244245
}
245246
}
247+
248+
/**
249+
* Test implementation of {@link org.elasticsearch.index.mapper.BlockLoader.AggregateMetricDoubleBuilder}.
250+
* The implementation here is fairly close to the production one.
251+
*/
252+
private static class AggregateMetricDoubleBlockBuilder implements BlockLoader.AggregateMetricDoubleBuilder {
253+
private final DoubleBuilder min = new DoubleBuilder();
254+
private final DoubleBuilder max = new DoubleBuilder();
255+
private final DoubleBuilder sum = new DoubleBuilder();
256+
private final IntBuilder count = new IntBuilder();
257+
258+
private static class DoubleBuilder extends TestBlock.Builder implements BlockLoader.DoubleBuilder {
259+
@Override
260+
public BlockLoader.DoubleBuilder appendDouble(double value) {
261+
add(value);
262+
return this;
263+
}
264+
}
265+
266+
private static class IntBuilder extends TestBlock.Builder implements BlockLoader.IntBuilder {
267+
@Override
268+
public BlockLoader.IntBuilder appendInt(int value) {
269+
add(value);
270+
return this;
271+
}
272+
}
273+
274+
@Override
275+
public BlockLoader.DoubleBuilder min() {
276+
return min;
277+
}
278+
279+
@Override
280+
public BlockLoader.DoubleBuilder max() {
281+
return max;
282+
}
283+
284+
@Override
285+
public BlockLoader.DoubleBuilder sum() {
286+
return sum;
287+
}
288+
289+
@Override
290+
public BlockLoader.IntBuilder count() {
291+
return count;
292+
}
293+
294+
@Override
295+
public BlockLoader.Block build() {
296+
var minBlock = min.build();
297+
var maxBlock = max.build();
298+
var sumBlock = sum.build();
299+
var countBlock = count.build();
300+
301+
assert minBlock.size() == maxBlock.size();
302+
assert maxBlock.size() == sumBlock.size();
303+
assert sumBlock.size() == countBlock.size();
304+
305+
var values = new ArrayList<>(minBlock.size());
306+
307+
for (int i = 0; i < minBlock.size(); i++) {
308+
// we need to represent this complex block somehow
309+
var value = new HashMap<String, Object>();
310+
value.put("min", minBlock.values.get(i));
311+
value.put("max", maxBlock.values.get(i));
312+
value.put("sum", sumBlock.values.get(i));
313+
value.put("value_count", countBlock.values.get(i));
314+
315+
values.add(value);
316+
}
317+
318+
return new TestBlock(values);
319+
}
320+
321+
@Override
322+
public BlockLoader.Builder appendNull() {
323+
throw new UnsupportedOperationException();
324+
}
325+
326+
@Override
327+
public BlockLoader.Builder beginPositionEntry() {
328+
throw new UnsupportedOperationException();
329+
}
330+
331+
@Override
332+
public BlockLoader.Builder endPositionEntry() {
333+
throw new UnsupportedOperationException();
334+
}
335+
336+
@Override
337+
public void close() {
338+
339+
}
340+
}
246341
}

test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DataSourceHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,10 @@ default DataSourceResponse.VersionStringGenerator handle(DataSourceRequest.Versi
8282
return null;
8383
}
8484

85+
default DataSourceResponse.AggregateMetricDoubleGenerator handle(DataSourceRequest.AggregateMetricDoubleGenerator request) {
86+
return null;
87+
}
88+
8589
default DataSourceResponse.NullWrapper handle(DataSourceRequest.NullWrapper request) {
8690
return null;
8791
}

test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DataSourceRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,12 @@ public DataSourceResponse.VersionStringGenerator accept(DataSourceHandler handle
132132
}
133133
}
134134

135+
record AggregateMetricDoubleGenerator() implements DataSourceRequest<DataSourceResponse.AggregateMetricDoubleGenerator> {
136+
public DataSourceResponse.AggregateMetricDoubleGenerator accept(DataSourceHandler handler) {
137+
return handler.handle(this);
138+
}
139+
}
140+
135141
record NullWrapper() implements DataSourceRequest<DataSourceResponse.NullWrapper> {
136142
public DataSourceResponse.NullWrapper accept(DataSourceHandler handler) {
137143
return handler.handle(this);

test/framework/src/main/java/org/elasticsearch/logsdb/datageneration/datasource/DataSourceResponse.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ record IpGenerator(Supplier<InetAddress> generator) implements DataSourceRespons
5555

5656
record VersionStringGenerator(Supplier<String> generator) implements DataSourceResponse {}
5757

58+
record AggregateMetricDoubleGenerator(Supplier<Map<String, Number>> generator) implements DataSourceResponse {}
59+
5860
record NullWrapper(Function<Supplier<Object>, Supplier<Object>> wrapper) implements DataSourceResponse {}
5961

6062
record ArrayWrapper(Function<Supplier<Object>, Supplier<Object>> wrapper) implements DataSourceResponse {}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,7 @@ protected void parseCreateField(DocumentParserContext context) throws IOExceptio
851851
// by its FieldMapper#parse()
852852
throw e;
853853
}
854+
854855
for (Map.Entry<Metric, Number> parsed : metricsParsed.entrySet()) {
855856
NumberFieldMapper delegateFieldMapper = metricFieldMappers.get(parsed.getKey());
856857
delegateFieldMapper.indexValue(context, parsed.getValue());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.aggregatemetric.mapper;
9+
10+
import org.elasticsearch.index.mapper.BlockLoaderTestCase;
11+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceHandler;
12+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceRequest;
13+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceResponse;
14+
import org.elasticsearch.plugins.Plugin;
15+
import org.elasticsearch.xpack.aggregatemetric.AggregateMetricMapperPlugin;
16+
import org.elasticsearch.xpack.aggregatemetric.mapper.datageneration.AggregateMetricDoubleDataSourceHandler;
17+
18+
import java.util.Arrays;
19+
import java.util.Collection;
20+
import java.util.HashMap;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.Optional;
24+
25+
public class AggregateMetricDoubleFieldBlockLoaderTests extends BlockLoaderTestCase {
26+
public AggregateMetricDoubleFieldBlockLoaderTests(Params params) {
27+
super("aggregate_metric_double", List.of(new AggregateMetricDoubleDataSourceHandler(), new DataSourceHandler() {
28+
@Override
29+
public DataSourceResponse.ObjectArrayGenerator handle(DataSourceRequest.ObjectArrayGenerator request) {
30+
// aggregate_metric_double does not support multiple values in a document so we can't have object arrays
31+
return new DataSourceResponse.ObjectArrayGenerator(Optional::empty);
32+
}
33+
}), params);
34+
}
35+
36+
@Override
37+
protected Object expected(Map<String, Object> fieldMapping, Object value, TestContext testContext) {
38+
if (value instanceof Map<?, ?> map) {
39+
var expected = new HashMap<String, Object>(map.size());
40+
41+
// put explicit `null` for metrics that are not present, this is how the block looks like
42+
Arrays.stream(AggregateMetricDoubleFieldMapper.Metric.values())
43+
.map(AggregateMetricDoubleFieldMapper.Metric::toString)
44+
.sorted()
45+
.forEach(m -> {
46+
expected.put(m, map.get(m));
47+
});
48+
49+
return expected;
50+
}
51+
52+
// malformed or null, return "empty row"
53+
return new HashMap<>() {
54+
{
55+
put("min", null);
56+
put("max", null);
57+
put("sum", null);
58+
put("value_count", null);
59+
}
60+
};
61+
}
62+
63+
@Override
64+
protected Collection<? extends Plugin> getPlugins() {
65+
return List.of(new AggregateMetricMapperPlugin());
66+
}
67+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.aggregatemetric.mapper.datageneration;
9+
10+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceHandler;
11+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceRequest;
12+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceResponse;
13+
import org.elasticsearch.test.ESTestCase;
14+
import org.elasticsearch.xpack.aggregatemetric.mapper.AggregateMetricDoubleFieldMapper;
15+
16+
import java.util.Arrays;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
20+
public class AggregateMetricDoubleDataSourceHandler implements DataSourceHandler {
21+
@Override
22+
public DataSourceResponse.AggregateMetricDoubleGenerator handle(DataSourceRequest.AggregateMetricDoubleGenerator request) {
23+
return new DataSourceResponse.AggregateMetricDoubleGenerator(() -> {
24+
var metricContainer = new HashMap<String, Number>();
25+
26+
// min and max must make sense - max has to be gte min
27+
double min = ESTestCase.randomDoubleBetween(-Double.MAX_VALUE, 1_000_000_000, false);
28+
double max = ESTestCase.randomDoubleBetween(min, Double.MAX_VALUE, true);
29+
30+
metricContainer.put("min", min);
31+
metricContainer.put("max", max);
32+
metricContainer.put("sum", ESTestCase.randomDouble());
33+
metricContainer.put("value_count", ESTestCase.randomIntBetween(1, Integer.MAX_VALUE));
34+
35+
return metricContainer;
36+
});
37+
}
38+
39+
@Override
40+
public DataSourceResponse.LeafMappingParametersGenerator handle(DataSourceRequest.LeafMappingParametersGenerator request) {
41+
if (request.fieldType().equals("aggregate_metric_double") == false) {
42+
return null;
43+
}
44+
45+
return new DataSourceResponse.LeafMappingParametersGenerator(() -> {
46+
var map = new HashMap<String, Object>();
47+
48+
List<AggregateMetricDoubleFieldMapper.Metric> metrics = ESTestCase.randomNonEmptySubsetOf(
49+
Arrays.asList(AggregateMetricDoubleFieldMapper.Metric.values())
50+
);
51+
52+
map.put("metrics", metrics.stream().map(Enum::toString).toList());
53+
map.put("default_metric", metrics.get(ESTestCase.randomIntBetween(0, metrics.size() - 1)));
54+
55+
if (ESTestCase.randomBoolean()) {
56+
map.put("ignore_malformed", ESTestCase.randomBoolean());
57+
}
58+
59+
return map;
60+
});
61+
}
62+
63+
@Override
64+
public DataSourceResponse.FieldDataGenerator handle(DataSourceRequest.FieldDataGenerator request) {
65+
if (request.fieldType().equals("aggregate_metric_double") == false) {
66+
return null;
67+
}
68+
69+
return new DataSourceResponse.FieldDataGenerator(new AggregateMetricDoubleFieldDataGenerator(request.dataSource()));
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.aggregatemetric.mapper.datageneration;
9+
10+
import org.elasticsearch.logsdb.datageneration.FieldDataGenerator;
11+
import org.elasticsearch.logsdb.datageneration.datasource.DataSource;
12+
import org.elasticsearch.logsdb.datageneration.datasource.DataSourceRequest;
13+
14+
import java.util.HashMap;
15+
import java.util.List;
16+
import java.util.Map;
17+
import java.util.function.Function;
18+
import java.util.function.Supplier;
19+
20+
public class AggregateMetricDoubleFieldDataGenerator implements FieldDataGenerator {
21+
private final Supplier<Map<String, Number>> metrics;
22+
private final Function<Supplier<Object>, Supplier<Object>> nullWrapper;
23+
private final Function<Supplier<Object>, Supplier<Object>> nullAndMalformedWrapper;
24+
25+
public AggregateMetricDoubleFieldDataGenerator(DataSource dataSource) {
26+
this.metrics = dataSource.get(new DataSourceRequest.AggregateMetricDoubleGenerator()).generator();
27+
28+
this.nullWrapper = dataSource.get(new DataSourceRequest.NullWrapper()).wrapper();
29+
30+
var strings = dataSource.get(new DataSourceRequest.StringGenerator()).generator();
31+
var malformed = dataSource.get(new DataSourceRequest.MalformedWrapper(strings::get)).wrapper();
32+
this.nullAndMalformedWrapper = malformed.andThen(nullWrapper);
33+
}
34+
35+
@Override
36+
@SuppressWarnings("unchecked")
37+
public Object generateValue(Map<String, Object> fieldMapping) {
38+
if (fieldMapping == null) {
39+
// this field can't be properly mapped with dynamic mapping
40+
return null;
41+
}
42+
43+
// metrics returned have all metric fields but we only need those that appear in the mapping
44+
Supplier<Object> metricsAdjustedForMapping = () -> {
45+
var metric = metrics.get();
46+
47+
var adjusted = new HashMap<String, Number>();
48+
for (var metricName : (List<String>) fieldMapping.get("metrics")) {
49+
adjusted.put(metricName, metric.get(metricName));
50+
}
51+
52+
return adjusted;
53+
};
54+
55+
if ((Boolean) fieldMapping.getOrDefault("ignore_malformed", false)) {
56+
return nullAndMalformedWrapper.apply(metricsAdjustedForMapping).get();
57+
}
58+
59+
return nullWrapper.apply(metricsAdjustedForMapping).get();
60+
}
61+
}

0 commit comments

Comments
 (0)