Skip to content

Commit 8fbff73

Browse files
authored
Wire up Max & Min aggregations (#52219)
1 parent e567ecb commit 8fbff73

File tree

8 files changed

+128
-12
lines changed

8 files changed

+128
-12
lines changed

server/src/main/java/org/elasticsearch/search/SearchModule.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,11 @@ private void registerAggregations(List<SearchPlugin> plugins) {
345345
registerAggregation(new AggregationSpec(SumAggregationBuilder.NAME, SumAggregationBuilder::new, SumAggregationBuilder::parse)
346346
.addResultReader(InternalSum::new));
347347
registerAggregation(new AggregationSpec(MinAggregationBuilder.NAME, MinAggregationBuilder::new, MinAggregationBuilder::parse)
348-
.addResultReader(InternalMin::new));
348+
.addResultReader(InternalMin::new)
349+
.setAggregatorRegistrar(MinAggregationBuilder::registerAggregators));
349350
registerAggregation(new AggregationSpec(MaxAggregationBuilder.NAME, MaxAggregationBuilder::new, MaxAggregationBuilder::parse)
350-
.addResultReader(InternalMax::new));
351+
.addResultReader(InternalMax::new)
352+
.setAggregatorRegistrar(MaxAggregationBuilder::registerAggregators));
351353
registerAggregation(new AggregationSpec(StatsAggregationBuilder.NAME, StatsAggregationBuilder::new, StatsAggregationBuilder::parse)
352354
.addResultReader(InternalStats::new));
353355
registerAggregation(new AggregationSpec(ExtendedStatsAggregationBuilder.NAME, ExtendedStatsAggregationBuilder::new,

server/src/main/java/org/elasticsearch/search/aggregations/metrics/MaxAggregationBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
3434
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
3535
import org.elasticsearch.search.aggregations.support.ValuesSourceParserHelper;
36+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
3637
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
3738

3839
import java.io.IOException;
@@ -51,6 +52,10 @@ public static AggregationBuilder parse(String aggregationName, XContentParser pa
5152
return PARSER.parse(parser, new MaxAggregationBuilder(aggregationName), null);
5253
}
5354

55+
public static void registerAggregators(ValuesSourceRegistry valuesSourceRegistry) {
56+
MaxAggregatorFactory.registerAggregators(valuesSourceRegistry);
57+
}
58+
5459
public MaxAggregationBuilder(String name) {
5560
super(name);
5661
}

server/src/main/java/org/elasticsearch/search/aggregations/metrics/MaxAggregatorFactory.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@
2525
import org.elasticsearch.search.aggregations.AggregatorFactories;
2626
import org.elasticsearch.search.aggregations.AggregatorFactory;
2727
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
28+
import org.elasticsearch.search.aggregations.support.AggregatorSupplier;
29+
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
2830
import org.elasticsearch.search.aggregations.support.ValuesSource;
2931
import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric;
3032
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
3133
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
34+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
3235
import org.elasticsearch.search.internal.SearchContext;
3336

3437
import java.io.IOException;
@@ -37,6 +40,23 @@
3740

3841
class MaxAggregatorFactory extends ValuesSourceAggregatorFactory {
3942

43+
static void registerAggregators(ValuesSourceRegistry valuesSourceRegistry) {
44+
valuesSourceRegistry.register(MaxAggregationBuilder.NAME,
45+
List.of(CoreValuesSourceType.NUMERIC, CoreValuesSourceType.DATE, CoreValuesSourceType.BOOLEAN),
46+
new MinMaxAggregatorSupplier() {
47+
@Override
48+
public Aggregator build(String name,
49+
ValuesSourceConfig config,
50+
ValuesSource valuesSource,
51+
SearchContext context,
52+
Aggregator parent,
53+
List<PipelineAggregator> pipelineAggregators,
54+
Map<String, Object> metaData) throws IOException {
55+
return new MaxAggregator(name, config, (Numeric) valuesSource, context, parent, pipelineAggregators, metaData);
56+
}
57+
});
58+
}
59+
4060
MaxAggregatorFactory(String name, ValuesSourceConfig config, QueryShardContext queryShardContext,
4161
AggregatorFactory parent, AggregatorFactories.Builder subFactoriesBuilder,
4262
Map<String, Object> metaData) throws IOException {
@@ -58,10 +78,14 @@ protected Aggregator doCreateInternal(ValuesSource valuesSource,
5878
boolean collectsFromSingleBucket,
5979
List<PipelineAggregator> pipelineAggregators,
6080
Map<String, Object> metaData) throws IOException {
61-
if (valuesSource instanceof Numeric == false) {
62-
throw new AggregationExecutionException("ValuesSource type " + valuesSource.toString() + "is not supported for aggregation " +
63-
this.name());
81+
AggregatorSupplier aggregatorSupplier = queryShardContext.getValuesSourceRegistry().getAggregator(config.valueSourceType(),
82+
MaxAggregationBuilder.NAME);
83+
84+
if (aggregatorSupplier instanceof MinMaxAggregatorSupplier == false) {
85+
throw new AggregationExecutionException("Registry miss-match - expected MinMaxAggregatorSupplier, found [" +
86+
aggregatorSupplier.getClass().toString() + "]");
6487
}
65-
return new MaxAggregator(name, config, (Numeric) valuesSource, searchContext, parent, pipelineAggregators, metaData);
88+
return ((MinMaxAggregatorSupplier) aggregatorSupplier).build(name, config, valuesSource, searchContext, parent,
89+
pipelineAggregators, metaData);
6690
}
6791
}

server/src/main/java/org/elasticsearch/search/aggregations/metrics/MinAggregationBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregationBuilder;
3434
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
3535
import org.elasticsearch.search.aggregations.support.ValuesSourceParserHelper;
36+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
3637
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
3738

3839
import java.io.IOException;
@@ -64,6 +65,10 @@ protected AggregationBuilder shallowCopy(Builder factoriesBuilder, Map<String, O
6465
return new MinAggregationBuilder(this, factoriesBuilder, metaData);
6566
}
6667

68+
public static void registerAggregators(ValuesSourceRegistry valuesSourceRegistry) {
69+
MinAggregatorFactory.registerAggregators(valuesSourceRegistry);
70+
}
71+
6772
/**
6873
* Read from a stream.
6974
*/

server/src/main/java/org/elasticsearch/search/aggregations/metrics/MinAggregatorFactory.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,13 @@
2525
import org.elasticsearch.search.aggregations.AggregatorFactories;
2626
import org.elasticsearch.search.aggregations.AggregatorFactory;
2727
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
28+
import org.elasticsearch.search.aggregations.support.AggregatorSupplier;
29+
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
2830
import org.elasticsearch.search.aggregations.support.ValuesSource;
2931
import org.elasticsearch.search.aggregations.support.ValuesSource.Numeric;
3032
import org.elasticsearch.search.aggregations.support.ValuesSourceAggregatorFactory;
3133
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
34+
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
3235
import org.elasticsearch.search.internal.SearchContext;
3336

3437
import java.io.IOException;
@@ -37,6 +40,23 @@
3740

3841
class MinAggregatorFactory extends ValuesSourceAggregatorFactory {
3942

43+
static void registerAggregators(ValuesSourceRegistry valuesSourceRegistry) {
44+
valuesSourceRegistry.register(MinAggregationBuilder.NAME,
45+
List.of(CoreValuesSourceType.NUMERIC, CoreValuesSourceType.DATE, CoreValuesSourceType.BOOLEAN),
46+
new MinMaxAggregatorSupplier() {
47+
@Override
48+
public Aggregator build(String name,
49+
ValuesSourceConfig config,
50+
ValuesSource valuesSource,
51+
SearchContext context,
52+
Aggregator parent,
53+
List<PipelineAggregator> pipelineAggregators,
54+
Map<String, Object> metaData) throws IOException {
55+
return new MinAggregator(name, config, (Numeric) valuesSource, context, parent, pipelineAggregators, metaData);
56+
}
57+
});
58+
}
59+
4060
MinAggregatorFactory(String name, ValuesSourceConfig config, QueryShardContext queryShardContext,
4161
AggregatorFactory parent, AggregatorFactories.Builder subFactoriesBuilder,
4262
Map<String, Object> metaData) throws IOException {
@@ -58,10 +78,14 @@ protected Aggregator doCreateInternal(ValuesSource valuesSource,
5878
boolean collectsFromSingleBucket,
5979
List<PipelineAggregator> pipelineAggregators,
6080
Map<String, Object> metaData) throws IOException {
61-
if (valuesSource instanceof Numeric == false) {
62-
throw new AggregationExecutionException("ValuesSource type " + valuesSource.toString() + "is not supported for aggregation " +
63-
this.name());
81+
AggregatorSupplier aggregatorSupplier = queryShardContext.getValuesSourceRegistry().getAggregator(config.valueSourceType(),
82+
MinAggregationBuilder.NAME);
83+
84+
if (aggregatorSupplier instanceof MinMaxAggregatorSupplier == false) {
85+
throw new AggregationExecutionException("Registry miss-match - expected MinMaxAggregatorSupplier, found [" +
86+
aggregatorSupplier.getClass().toString() + "]");
6487
}
65-
return new MinAggregator(name, config, (Numeric) valuesSource, searchContext, parent, pipelineAggregators, metaData);
88+
return ((MinMaxAggregatorSupplier) aggregatorSupplier).build(name, config, valuesSource, searchContext, parent,
89+
pipelineAggregators, metaData);
6690
}
6791
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.elasticsearch.search.aggregations.metrics;
20+
21+
import org.elasticsearch.search.aggregations.Aggregator;
22+
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
23+
import org.elasticsearch.search.aggregations.support.AggregatorSupplier;
24+
import org.elasticsearch.search.aggregations.support.ValuesSource;
25+
import org.elasticsearch.search.aggregations.support.ValuesSourceConfig;
26+
import org.elasticsearch.search.internal.SearchContext;
27+
28+
import java.io.IOException;
29+
import java.util.List;
30+
import java.util.Map;
31+
32+
public interface MinMaxAggregatorSupplier extends AggregatorSupplier {
33+
Aggregator build(String name,
34+
ValuesSourceConfig config,
35+
ValuesSource valuesSource,
36+
SearchContext context,
37+
Aggregator parent,
38+
List<PipelineAggregator> pipelineAggregators,
39+
Map<String, Object> metaData) throws IOException;
40+
}

server/src/test/java/org/elasticsearch/search/aggregations/metrics/MinAggregatorTests.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.lucene.document.DoublePoint;
2424
import org.apache.lucene.document.Field;
2525
import org.apache.lucene.document.FloatPoint;
26+
import org.apache.lucene.document.InetAddressPoint;
2627
import org.apache.lucene.document.IntPoint;
2728
import org.apache.lucene.document.LongPoint;
2829
import org.apache.lucene.document.NumericDocValuesField;
@@ -54,6 +55,7 @@
5455
import org.elasticsearch.index.IndexSettings;
5556
import org.elasticsearch.index.mapper.ContentPath;
5657
import org.elasticsearch.index.mapper.DateFieldMapper;
58+
import org.elasticsearch.index.mapper.IpFieldMapper;
5759
import org.elasticsearch.index.mapper.KeywordFieldMapper;
5860
import org.elasticsearch.index.mapper.MappedFieldType;
5961
import org.elasticsearch.index.mapper.Mapper;
@@ -259,6 +261,20 @@ public void testQueryFiltersAll() throws IOException {
259261
});
260262
}
261263

264+
public void testIpField() throws IOException {
265+
final String fieldName = "IP_field";
266+
MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("min").field(fieldName);
267+
268+
MappedFieldType fieldType = new IpFieldMapper.IpFieldType();
269+
fieldType.setName(fieldName);
270+
271+
boolean v4 = randomBoolean();
272+
expectThrows(IllegalArgumentException.class, () -> testCase(aggregationBuilder, new MatchAllDocsQuery(), iw -> {
273+
iw.addDocument(singleton(new SortedSetDocValuesField(fieldName, new BytesRef(InetAddressPoint.encode(randomIp(v4))))));
274+
iw.addDocument(singleton(new SortedSetDocValuesField(fieldName, new BytesRef(InetAddressPoint.encode(randomIp(v4))))));
275+
}, min -> fail("expected an exception"), fieldType));
276+
}
277+
262278
public void testUnmappedWithMissingField() throws IOException {
263279
MinAggregationBuilder aggregationBuilder = new MinAggregationBuilder("min").field("does_not_exist").missing(0L);
264280

@@ -287,7 +303,7 @@ public void testUnsupportedType() {
287303
}, (Consumer<InternalMin>) min -> {
288304
fail("Should have thrown exception");
289305
}, fieldType));
290-
assertEquals(e.getMessage(), "Expected numeric type on field [not_a_number], but got [keyword]");
306+
assertEquals("Field [not_a_number] of type [keyword(indexed,tokenized)] is not supported for aggregation [min]", e.getMessage());
291307
}
292308

293309
public void testBadMissingField() {

test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@
9393
import org.elasticsearch.search.aggregations.MultiBucketConsumerService.MultiBucketConsumer;
9494
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
9595
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
96-
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
9796
import org.elasticsearch.search.aggregations.support.ValuesSourceRegistry;
97+
import org.elasticsearch.search.aggregations.support.ValuesSourceType;
9898
import org.elasticsearch.search.fetch.FetchPhase;
9999
import org.elasticsearch.search.fetch.subphase.FetchDocValuesPhase;
100100
import org.elasticsearch.search.fetch.subphase.FetchSourcePhase;

0 commit comments

Comments
 (0)