diff --git a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java index 38d635ab3939f..003a2e86048a7 100644 --- a/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java +++ b/modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapper.java @@ -59,6 +59,8 @@ import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.math.BigDecimal; @@ -292,6 +294,11 @@ public IndexFieldData build(IndexSettings indexSettings, MappedFieldType fiel }; } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java index 388d4ca833ff4..f1b75672ccc4b 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/MetaJoinFieldMapper.java @@ -31,6 +31,8 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.StringFieldType; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.List; @@ -95,6 +97,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java index 8130acac1af72..7dc43962ad9c4 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentIdFieldMapper.java @@ -41,6 +41,8 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.StringFieldType; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Collection; @@ -118,6 +120,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java index cd5d37df2f9ba..313bd36c572b2 100644 --- a/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java +++ b/modules/parent-join/src/main/java/org/elasticsearch/join/mapper/ParentJoinFieldMapper.java @@ -43,6 +43,8 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.mapper.StringFieldType; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.ArrayList; @@ -227,6 +229,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java b/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java index 4b29d314356df..f88a693df13e0 100644 --- a/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java +++ b/plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java @@ -46,6 +46,8 @@ import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.ZoneId; @@ -139,6 +141,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override protected BytesRef indexedValueForSearch(Object value) { if (value == null) { diff --git a/plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java b/plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java index cf2b05fe54d90..b6c49a246f433 100644 --- a/plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java +++ b/plugins/mapper-murmur3/src/main/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapper.java @@ -39,6 +39,8 @@ import org.elasticsearch.index.mapper.TypeParsers; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.List; @@ -124,6 +126,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().numericType(NumericType.LONG); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } + @Override public Query existsQuery(QueryShardContext context) { return new DocValuesFieldExistsQuery(name()); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java index 7a5bd97770297..ea8d407f731f2 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BinaryFieldMapper.java @@ -40,6 +40,8 @@ import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.ZoneId; @@ -136,6 +138,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new BytesBinaryDVIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Query existsQuery(QueryShardContext context) { if (hasDocValues()) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java index caf8baac24da1..3def7265d4c9c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/BooleanFieldMapper.java @@ -40,6 +40,8 @@ import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.ZoneId; @@ -189,6 +191,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().numericType(NumericType.BOOLEAN); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BOOLEAN; + } + @Override public DocValueFormat docValueFormat(@Nullable String format, ZoneId timeZone) { if (format != null) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java index 43762645190e0..9ea08e56ddeed 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DateFieldMapper.java @@ -52,6 +52,8 @@ import org.elasticsearch.index.query.QueryRewriteContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.DateTimeException; @@ -465,6 +467,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().numericType(resolution.numericType()); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.DATE; + } + @Override public Object valueForDisplay(Object value) { Long val = (Long) value; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java index f319db284b2d2..93d8215f21cac 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/GeoPointFieldMapper.java @@ -40,6 +40,8 @@ import org.elasticsearch.index.fielddata.plain.AbstractLatLonPointDVIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.query.QueryShardException; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.ArrayList; @@ -235,6 +237,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new AbstractLatLonPointDVIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.GEOPOINT; + } + @Override public Query existsQuery(QueryShardContext context) { if (hasDocValues()) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java index b3c707274c966..353ef8bbcbc2a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java @@ -46,6 +46,8 @@ import org.elasticsearch.indices.IndicesService; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Arrays; @@ -153,6 +155,12 @@ public Query termsQuery(List values, QueryShardContext context) { return new TermInSetQuery(name(), bytesRefs); } + @Override + public ValuesSourceType getValuesSourceType() { + // TODO: should this even exist? Is aggregating on the ID field valid? + return CoreValuesSourceType.BYTES; + } + @Override public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { if (indexOptions() == IndexOptions.NONE) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java index 2500358a3e5d6..88282fa685728 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IndexFieldMapper.java @@ -33,6 +33,8 @@ import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.List; @@ -189,6 +191,12 @@ public Query wildcardQuery(String value, public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new ConstantIndexFieldData.Builder(mapperService -> fullyQualifiedIndexName); } + + @Override + public ValuesSourceType getValuesSourceType() { + // TODO: Should Index fields be aggregatable? What even is an IndexField? + return CoreValuesSourceType.BYTES; + } } private IndexFieldMapper(Settings indexSettings, MappedFieldType existing) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java index 2b52e42ffe558..a3abf963104ae 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IpFieldMapper.java @@ -44,6 +44,8 @@ import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.net.InetAddress; @@ -294,6 +296,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().scriptFunction(IpScriptDocValues::new); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.IP; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java index 099ae9b2aa7a2..7fde4359742e3 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java @@ -43,6 +43,8 @@ import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Iterator; @@ -271,6 +273,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java index 86dad273e71b0..fd7e887c7d4dc 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java @@ -50,6 +50,7 @@ import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.index.similarity.SimilarityProvider; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.ZoneId; @@ -117,6 +118,16 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { throw new IllegalArgumentException("Fielddata is not supported on field [" + name() + "] of type [" + typeName() + "]"); } + /** + * Returns the {@link ValuesSourceType} which supports this field type. This is tightly coupled to field data and aggregations support, + * so any implementation that returns a value from {@link MappedFieldType#fielddataBuilder} should also return a value from here. + * + * @return The appropriate {@link ValuesSourceType} for this field type. + */ + public ValuesSourceType getValuesSourceType() { + throw new IllegalArgumentException("Aggregations are not supported on field [" + name() + "] of type [" + typeName() + "]"); + } + @Override public boolean equals(Object o) { if (!super.equals(o)) return false; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java index 0a473bd189e3b..fa7055cadd2a9 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/NumberFieldMapper.java @@ -55,6 +55,8 @@ import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.time.ZoneId; @@ -958,6 +960,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().numericType(type.numericType()); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } + @Override public Object valueForDisplay(Object value) { if (value == null) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java index 3bb4f24aa61da..f05b063bb262a 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java @@ -45,6 +45,8 @@ import org.elasticsearch.index.fielddata.plain.DocValuesIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.net.InetAddress; @@ -242,6 +244,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().setRangeType(rangeType); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.RANGE; + } + @Override public String typeName() { return rangeType.name; diff --git a/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java index 5bb0007415866..5bbff9e90a21c 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/SeqNoFieldMapper.java @@ -37,6 +37,8 @@ import org.elasticsearch.index.mapper.ParseContext.Document; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.seqno.SequenceNumbers; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.List; @@ -218,6 +220,10 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new DocValuesIndexFieldData.Builder().numericType(NumericType.LONG); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } } public SeqNoFieldMapper(Settings indexSettings) { diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java index 46ef8a732a697..b237d7cbd82ee 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TextFieldMapper.java @@ -70,6 +70,8 @@ import org.elasticsearch.index.fielddata.plain.PagedBytesIndexFieldData; import org.elasticsearch.index.query.IntervalBuilder; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.ArrayList; @@ -756,6 +758,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new PagedBytesIndexFieldData.Builder(fielddataMinFrequency, fielddataMaxFrequency, fielddataMinSegmentSize); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public void checkCompatibility(MappedFieldType other, List conflicts) { super.checkCompatibility(other, conflicts); diff --git a/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java index 5d9c9e20a1c14..e5c33b83f24e5 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/TypeFieldMapper.java @@ -42,6 +42,8 @@ import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.plain.ConstantIndexFieldData; import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Arrays; @@ -113,6 +115,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new ConstantIndexFieldData.Builder(typeFunction); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public boolean isSearchable() { return true; diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java index 4cd407217c6bd..2983054515ebd 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/HistogramAggregatorFactory.java @@ -19,8 +19,6 @@ package org.elasticsearch.search.aggregations.bucket.histogram; -import org.elasticsearch.index.fielddata.IndexNumericFieldData; -import org.elasticsearch.index.mapper.RangeFieldMapper; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.AggregationExecutionException; @@ -72,11 +70,11 @@ public Aggregator build(String name, AggregatorFactories factories, double inter return new RangeHistogramAggregator(name, factories, interval, offset, order, keyed, minDocCount, minBound, maxBound, rangeValueSource, formatter, context, parent, pipelineAggregators, metaData); } - }, - (fieldType, indexFieldData) -> fieldType instanceof RangeFieldMapper.RangeFieldType + } ); - valuesSourceRegistry.register(HistogramAggregationBuilder.NAME, CoreValuesSourceType.NUMERIC, + valuesSourceRegistry.register(HistogramAggregationBuilder.NAME, + List.of(CoreValuesSourceType.NUMERIC, CoreValuesSourceType.DATE, CoreValuesSourceType.BOOLEAN), new HistogramAggregatorSupplier() { @Override public Aggregator build(String name, AggregatorFactories factories, double interval, double offset, @@ -87,8 +85,7 @@ public Aggregator build(String name, AggregatorFactories factories, double inter return new NumericHistogramAggregator(name, factories, interval, offset, order, keyed, minDocCount, minBound, maxBound, (ValuesSource.Numeric) valuesSource, formatter, context, parent, pipelineAggregators, metaData); } - }, - (fieldType, indexFieldData) -> indexFieldData instanceof IndexNumericFieldData + } ); } diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java index 32971b9e12c51..d90f8ee5663c5 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceConfig.java @@ -120,8 +120,8 @@ public static ValuesSourceConfig resolve( } } else { IndexFieldData indexFieldData = context.getForField(fieldType); - valuesSourceType = ValuesSourceRegistry.getInstance().getValuesSourceType(fieldType, indexFieldData, - aggregationName, userValueTypeHint, script, defaultValueSourceType); + valuesSourceType = ValuesSourceRegistry.getInstance().getValuesSourceType(fieldType, aggregationName, indexFieldData, + userValueTypeHint, script, defaultValueSourceType); config = new ValuesSourceConfig(valuesSourceType); config.fieldContext(new FieldContext(field, indexFieldData, fieldType)); diff --git a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java index 90b313c2f44b3..040908235e623 100644 --- a/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java +++ b/server/src/main/java/org/elasticsearch/search/aggregations/support/ValuesSourceRegistry.java @@ -30,8 +30,7 @@ import java.util.AbstractMap; import java.util.List; import java.util.Map; -import java.util.StringJoiner; -import java.util.function.BiFunction; +import java.util.function.Predicate; /* This is a _very_ crude prototype for the ValuesSourceRegistry which basically hard-codes everything. The intent is to define the API @@ -39,10 +38,8 @@ */ public enum ValuesSourceRegistry { INSTANCE { - Map> aggregatorRegistry = Map.of(); - // We use a List of Entries here to approximate an ordered map - Map, ValuesSourceType>>> resolverRegistry - = Map.of(); + // Maps Aggregation names to (ValuesSourceType, Supplier) pairs, keyed by ValuesSourceType + Map, AggregatorSupplier>>> aggregatorRegistry = Map.of(); /** * Threading behavior notes: This call is both synchronized and expensive. It copies the entire existing mapping structure each @@ -52,76 +49,63 @@ public enum ValuesSourceRegistry { * different worker threads. Thus we want to optimize the read case to be thread safe and fast, which the immutable * collections do well. Using immutable collections requires a copy on write mechanic, thus the somewhat non-intuitive * implementation of this method. - * * @param aggregationName The name of the family of aggregations, typically found via ValuesSourceAggregationBuilder.getType() - * @param valuesSourceType The ValuesSourceType this mapping applies to. + * @param appliesTo The ValuesSourceType this mapping applies to. * @param aggregatorSupplier An Aggregation-specific specialization of AggregatorSupplier which will construct the mapped aggregator - * from the aggregation standard set of parameters - * @param resolveValuesSourceType A predicate operating on MappedFieldType and IndexFieldData instances which decides if the mapped */ @Override - public synchronized void register(String aggregationName, ValuesSourceType valuesSourceType, AggregatorSupplier aggregatorSupplier, - BiFunction resolveValuesSourceType) { - // Aggregator registry block - do this first in case we need to throw on duplicate registration - Map innerMap; - if (aggregatorRegistry.containsKey(aggregationName)) { - if (aggregatorRegistry.get(aggregationName).containsKey(valuesSourceType)) { - throw new IllegalStateException("Attempted to register already registered pair [" + aggregationName + ", " - + valuesSourceType.toString() + "]"); - } - innerMap = copyAndAdd(aggregatorRegistry.get(aggregationName), - new AbstractMap.SimpleEntry<>(valuesSourceType, aggregatorSupplier)); - } else { - innerMap = Map.of(valuesSourceType, aggregatorSupplier); - } - aggregatorRegistry = copyAndAdd(aggregatorRegistry, new AbstractMap.SimpleEntry<>(aggregationName, innerMap)); - - // Resolver registry block + public synchronized void register(String aggregationName, Predicate appliesTo, + AggregatorSupplier aggregatorSupplier) { AbstractMap.SimpleEntry[] mappings; - if (resolverRegistry.containsKey(aggregationName)) { - List currentMappings = resolverRegistry.get(aggregationName); + if (aggregatorRegistry.containsKey(aggregationName)) { + List currentMappings = aggregatorRegistry.get(aggregationName); mappings = (AbstractMap.SimpleEntry[]) currentMappings.toArray(new AbstractMap.SimpleEntry[currentMappings.size() + 1]); } else { mappings = new AbstractMap.SimpleEntry[1]; } - mappings[mappings.length - 1] = new AbstractMap.SimpleEntry<>(resolveValuesSourceType, valuesSourceType); - resolverRegistry = copyAndAdd(resolverRegistry,new AbstractMap.SimpleEntry<>(aggregationName, List.of(mappings))); + mappings[mappings.length - 1] = new AbstractMap.SimpleEntry<>(appliesTo, aggregatorSupplier); + aggregatorRegistry = copyAndAdd(aggregatorRegistry,new AbstractMap.SimpleEntry<>(aggregationName, List.of(mappings))); + } + + private AggregatorSupplier findMatchingSuppier(ValuesSourceType valuesSourceType, + List, AggregatorSupplier>> supportedTypes) { + for (Map.Entry, AggregatorSupplier> candidate : supportedTypes) { + if (candidate.getKey().test(valuesSourceType)) { + return candidate.getValue(); + } + } + return null; } @Override public AggregatorSupplier getAggregator(ValuesSourceType valuesSourceType, String aggregationName) { - StringJoiner validSourceTypes = new StringJoiner(",", "[", "]"); if (aggregationName != null && aggregatorRegistry.containsKey(aggregationName)) { - Map innerMap = aggregatorRegistry.get(aggregationName); - if (valuesSourceType != null && innerMap.containsKey(valuesSourceType)) { - return innerMap.get(valuesSourceType); + AggregatorSupplier supplier = findMatchingSuppier(valuesSourceType, aggregatorRegistry.get(aggregationName)); + if (supplier == null) { + throw new AggregationExecutionException("ValuesSource type " + valuesSourceType.toString() + + " is not supported for aggregation" + aggregationName); } - for (ValuesSourceType validVST : innerMap.keySet()) { - validSourceTypes.add(validVST.toString()); - } - throw new AggregationExecutionException("ValuesSource type " + valuesSourceType.toString() + - " is not supported for aggregation" + aggregationName + ". Valid choices are " + validSourceTypes.toString()); + return supplier; } throw new AggregationExecutionException("Unregistered Aggregation [" + aggregationName + "]"); } @Override - public ValuesSourceType getValuesSourceType(MappedFieldType fieldType, IndexFieldData indexFieldData, String aggregationName, + public ValuesSourceType getValuesSourceType(MappedFieldType fieldType, String aggregationName, + // TODO: the following arguments are only needed for the legacy case + IndexFieldData indexFieldData, ValueType valueType, Script script, ValuesSourceType defaultValuesSourceType) { - if (aggregationName != null && resolverRegistry.containsKey(aggregationName)) { - List, ValuesSourceType>> resolverList - = resolverRegistry.get(aggregationName); - for (AbstractMap.SimpleEntry, ValuesSourceType> entry : resolverList) { - BiFunction matcher = entry.getKey(); - if (matcher.apply(fieldType, indexFieldData)) { - return entry.getValue(); - } + if (aggregationName != null && aggregatorRegistry.containsKey(aggregationName)) { + // This will throw if the field doesn't support values sources, although really we probably threw much earlier in that case + ValuesSourceType valuesSourceType = fieldType.getValuesSourceType(); + if (aggregatorRegistry.get(aggregationName) != null + && findMatchingSuppier(valuesSourceType, aggregatorRegistry.get(aggregationName)) != null) { + return valuesSourceType; } - // TODO: Error message should list valid field types - String fieldDescription = fieldType.name() + "(" + fieldType.toString() + ")"; - throw new IllegalArgumentException("Field type " + fieldDescription + " is not supported for aggregation " - + aggregationName); + String fieldDescription = fieldType.typeName() + "(" + fieldType.toString() + ")"; + throw new IllegalArgumentException("Field [" + fieldType.name() + "] of type [" + fieldDescription + + "] is not supported for aggregation [" + aggregationName + "]"); } else { // TODO: Legacy resolve logic; remove this after converting all aggregations to the new system if (indexFieldData instanceof IndexNumericFieldData) { @@ -144,21 +128,49 @@ public ValuesSourceType getValuesSourceType(MappedFieldType fieldType, IndexFiel }; /** - * Register a ValuesSource to Aggregator mapping. - * - * @param aggregationName The name of the family of aggregations, typically found via ValuesSourceAggregationBuilder.getType() + * Register a ValuesSource to Aggregator mapping. This version provides a convenience method for mappings that only apply to a single + * {@link ValuesSourceType}, to allow passing in the type and auto-wrapping it in a predicate + * @param aggregationName The name of the family of aggregations, typically found via ValuesSourceAggregationBuilder.getType() * @param valuesSourceType The ValuesSourceType this mapping applies to. * @param aggregatorSupplier An Aggregation-specific specialization of AggregatorSupplier which will construct the mapped aggregator + * from the aggregation standard set of parameters + */ + public void register(String aggregationName, ValuesSourceType valuesSourceType, AggregatorSupplier aggregatorSupplier) { + register(aggregationName, (candidate) -> valuesSourceType.equals(candidate), aggregatorSupplier); + } + + /** + * Register a ValuesSource to Aggregator mapping. This version provides a convenience method for mappings that only apply to a known + * list of {@link ValuesSourceType}, to allow passing in the type and auto-wrapping it in a predicate + * @param aggregationName The name of the family of aggregations, typically found via ValuesSourceAggregationBuilder.getType() + * @param valuesSourceTypes The ValuesSourceTypes this mapping applies to. + * @param aggregatorSupplier An Aggregation-specific specialization of AggregatorSupplier which will construct the mapped aggregator * from the aggregation standard set of parameters - * @param resolveValuesSourceType A predicate operating on MappedFieldType and IndexFieldData instances which decides if the mapped - * ValuesSourceType can be applied to the given field. */ - public abstract void register(String aggregationName, ValuesSourceType valuesSourceType, AggregatorSupplier aggregatorSupplier, - BiFunction resolveValuesSourceType); + public void register(String aggregationName, List valuesSourceTypes, AggregatorSupplier aggregatorSupplier) { + register(aggregationName, (candidate) -> { + for (ValuesSourceType valuesSourceType : valuesSourceTypes) { + if (valuesSourceType.equals(candidate)) { + return true; + } + } + return false; + }, aggregatorSupplier); + } + + + /** + * Register a ValuesSource to Aggregator mapping. + * @param aggregationName The name of the family of aggregations, typically found via ValuesSourceAggregationBuilder.getType() + * @param appliesTo A predicate which accepts the resolved {@link ValuesSourceType} and decides if the given aggregator can be applied + * to that type. + * @param aggregatorSupplier An Aggregation-specific specialization of AggregatorSupplier which will construct the mapped aggregator + */ + public abstract void register(String aggregationName, Predicate appliesTo, AggregatorSupplier aggregatorSupplier); public abstract AggregatorSupplier getAggregator(ValuesSourceType valuesSourceType, String aggregationName); // TODO: ValueType argument is only needed for legacy logic - public abstract ValuesSourceType getValuesSourceType(MappedFieldType fieldType, IndexFieldData indexFieldData, String aggregationName, + public abstract ValuesSourceType getValuesSourceType(MappedFieldType fieldType, String aggregationName, IndexFieldData indexFieldData, ValueType valueType, Script script, ValuesSourceType defaultValuesSourceType); diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/NumericHistogramAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/NumericHistogramAggregatorTests.java index 44153e6bc9ee7..1d49f871d2816 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/NumericHistogramAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/NumericHistogramAggregatorTests.java @@ -29,7 +29,6 @@ import org.apache.lucene.store.Directory; import org.apache.lucene.util.BytesRef; import org.apache.lucene.util.NumericUtils; -import org.elasticsearch.common.time.DateFormatters; import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.index.mapper.KeywordFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; @@ -119,21 +118,30 @@ public void testDates() throws Exception { "2019-11-09T17:09:50", "2019-11-10T22:55:46"); + String fieldName = "date_field"; + DateFieldMapper.Builder builder = new DateFieldMapper.Builder(fieldName); + DateFieldMapper.DateFieldType fieldType = builder.fieldType(); + fieldType.setName(fieldName); + fieldType.setHasDocValues(true); + try (Directory dir = newDirectory(); - RandomIndexWriter w = new RandomIndexWriter(random(), dir)) { - for (String value : dataset) { - Document doc = new Document(); - long millis = DateFormatters.from(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.parse(value)).toInstant().toEpochMilli(); - doc.add(new SortedNumericDocValuesField("field", millis)); - w.addDocument(doc); + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), dir)) { + Document document = new Document(); + for (String date : dataset) { + if (frequently()) { + indexWriter.commit(); + } + + long instant = fieldType.parse(date); + document.add(new SortedNumericDocValuesField(fieldName, instant)); + indexWriter.addDocument(document); + document.clear(); } HistogramAggregationBuilder aggBuilder = new HistogramAggregationBuilder("my_agg") - .field("field") + .field(fieldName) .interval(1000 * 60 * 60 * 24); - MappedFieldType fieldType = new NumberFieldMapper.NumberFieldType(NumberFieldMapper.NumberType.DOUBLE); - fieldType.setName("field"); - try (IndexReader reader = w.getReader()) { + try (IndexReader reader = indexWriter.getReader()) { IndexSearcher searcher = new IndexSearcher(reader); InternalHistogram histogram = search(searcher, new MatchAllDocsQuery(), aggBuilder, fieldType); assertTrue(AggregationInspectionHelper.hasValue(histogram)); diff --git a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java index 0e5f9a9225178..9cd071aeaac07 100644 --- a/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java +++ b/x-pack/plugin/analytics/src/main/java/org/elasticsearch/xpack/analytics/mapper/HistogramFieldMapper.java @@ -48,6 +48,8 @@ import org.elasticsearch.index.query.QueryShardException; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Iterator; @@ -265,6 +267,12 @@ public SortField sortField(Object missingValue, MultiValueMode sortMode, }; } + @Override + public ValuesSourceType getValuesSourceType() { + // TODO: Histogram ValuesSourceType should move into this plugin. + return CoreValuesSourceType.HISTOGRAM; + } + @Override public Query existsQuery(QueryShardContext context) { if (hasDocValues()) { diff --git a/x-pack/plugin/mapper-flattened/src/main/java/org/elasticsearch/xpack/flattened/mapper/FlatObjectFieldMapper.java b/x-pack/plugin/mapper-flattened/src/main/java/org/elasticsearch/xpack/flattened/mapper/FlatObjectFieldMapper.java index 5928d9889c8c8..ab0a5fd2cb3d2 100644 --- a/x-pack/plugin/mapper-flattened/src/main/java/org/elasticsearch/xpack/flattened/mapper/FlatObjectFieldMapper.java +++ b/x-pack/plugin/mapper-flattened/src/main/java/org/elasticsearch/xpack/flattened/mapper/FlatObjectFieldMapper.java @@ -50,6 +50,8 @@ import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.search.MultiValueMode; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Iterator; @@ -344,6 +346,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { failIfNoDocValues(); return new KeyedFlatObjectFieldData.Builder(key); } + + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } } /** @@ -523,6 +530,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { failIfNoDocValues(); return new DocValuesIndexFieldData.Builder(); } + + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } } private FlatObjectFieldParser fieldParser; diff --git a/x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/mapper/DenseVectorFieldMapper.java b/x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/mapper/DenseVectorFieldMapper.java index a33a9794acb43..1fd7543411efc 100644 --- a/x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/mapper/DenseVectorFieldMapper.java +++ b/x-pack/plugin/vectors/src/main/java/org/elasticsearch/xpack/vectors/mapper/DenseVectorFieldMapper.java @@ -27,6 +27,8 @@ import org.elasticsearch.index.mapper.ParseContext; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import org.elasticsearch.xpack.vectors.query.VectorDVIndexFieldData; import java.io.IOException; @@ -150,6 +152,11 @@ public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName) { return new VectorDVIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Query termQuery(Object value, QueryShardContext context) { throw new UnsupportedOperationException(