From f05b831e43adbc0bb7018bedfd7ec4fe868b018c Mon Sep 17 00:00:00 2001 From: Zachary Tong Date: Thu, 20 Feb 2020 14:08:25 -0500 Subject: [PATCH] Comprehensively test supported/unsupported field type:agg combinations (#52493) This adds a test to AggregatorTestCase that allows us to programmatically verify that an aggregator supports or does not support a particular field type. It fetches the list of registered field type parsers, creates a MappedFieldType from the parser and then attempts to run a basic agg against the field. A supplied list of supported VSTypes are then compared against the output (success or exception) and suceeds or fails the test accordingly. Co-Authored-By: Mark Tozzi * Skip fields that are not aggregatable --- .../index/mapper/ScaledFloatFieldMapper.java | 7 + .../join/mapper/MetaJoinFieldMapper.java | 7 + .../join/mapper/ParentIdFieldMapper.java | 7 + .../join/mapper/ParentJoinFieldMapper.java | 7 + .../ICUCollationKeywordFieldMapper.java | 7 + .../mapper/murmur3/Murmur3FieldMapper.java | 7 + .../index/mapper/BinaryFieldMapper.java | 7 + .../index/mapper/BooleanFieldMapper.java | 7 + .../index/mapper/DateFieldMapper.java | 10 +- .../index/mapper/GeoPointFieldMapper.java | 7 + .../index/mapper/IdFieldMapper.java | 8 + .../index/mapper/IndexFieldMapper.java | 8 + .../index/mapper/IpFieldMapper.java | 7 + .../index/mapper/KeywordFieldMapper.java | 7 + .../index/mapper/MappedFieldType.java | 11 + .../index/mapper/NumberFieldMapper.java | 7 + .../index/mapper/RangeFieldMapper.java | 15 +- .../index/mapper/SeqNoFieldMapper.java | 6 + .../index/mapper/TextFieldMapper.java | 7 + .../index/mapper/TypeFieldMapper.java | 7 + .../aggregations/bucket/IpRangeTests.java | 30 +-- .../bucket/range/IpRangeAggregatorTests.java | 18 -- .../bucket/terms/TermsAggregatorTests.java | 13 ++ .../aggregations/AggregatorTestCase.java | 221 ++++++++++++++++++ .../org/elasticsearch/test/ESTestCase.java | 19 +- .../mapper/HistogramFieldMapper.java | 8 + .../mapper/FlatObjectFieldMapper.java | 7 + .../mapper/DenseVectorFieldMapper.java | 7 + 28 files changed, 427 insertions(+), 52 deletions(-) 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 d71992b8ed11..83a60834b7f1 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 @@ -62,6 +62,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.sort.BucketedSort; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.math.BigDecimal; @@ -295,6 +297,11 @@ public class ScaledFloatFieldMapper extends FieldMapper { }; } + @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 388d4ca833ff..f1b75672ccc4 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.MappedFieldType; 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 class MetaJoinFieldMapper extends FieldMapper { 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 8130acac1af7..7dc43962ad9c 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.Mapper; 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 final class ParentIdFieldMapper extends FieldMapper { 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 331746b4276d..4c7c14893af1 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.MapperService; 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 final class ParentJoinFieldMapper extends FieldMapper { 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 883468941a5d..57a0dba31f41 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.IndexFieldData; 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 class ICUCollationKeywordFieldMapper extends FieldMapper { 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 cf2b05fe54d9..b6c49a246f43 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.ParseContext; 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 class Murmur3FieldMapper extends FieldMapper { 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 7a5bd9777029..ea8d407f731f 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.fielddata.plain.BytesBinaryDVIndexFieldData; 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 class BinaryFieldMapper extends FieldMapper { 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 caf8baac24da..009848df1d8a 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.IndexNumericFieldData.NumericType; 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 class BooleanFieldMapper extends FieldMapper { return new DocValuesIndexFieldData.Builder().numericType(NumericType.BOOLEAN); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } + @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 2feb5213c456..a42a07a3d9e0 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.fielddata.plain.DocValuesIndexFieldData; 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; @@ -71,6 +73,7 @@ import static org.elasticsearch.common.time.DateUtils.toLong; public final class DateFieldMapper extends FieldMapper { public static final String CONTENT_TYPE = "date"; + public static final String DATE_NANOS_CONTENT_TYPE = "date_nanos"; public static final DateFormatter DEFAULT_DATE_TIME_FORMATTER = DateFormatter.forPattern("strict_date_optional_time||epoch_millis"); public static class Defaults { @@ -94,7 +97,7 @@ public final class DateFieldMapper extends FieldMapper { return instant; } }, - NANOSECONDS("date_nanos", NumericType.DATE_NANOSECONDS) { + NANOSECONDS(DATE_NANOS_CONTENT_TYPE, NumericType.DATE_NANOSECONDS) { @Override public long convert(Instant instant) { return toLong(instant); @@ -505,6 +508,11 @@ public final class DateFieldMapper extends FieldMapper { return new DocValuesIndexFieldData.Builder().numericType(resolution.numericType()); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.NUMERIC; + } + @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 f319db284b2d..93d8215f21ca 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.IndexFieldData; 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 class GeoPointFieldMapper extends FieldMapper implements ArrayValueMapper 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 74d69be93d36..25e5523c008d 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/IdFieldMapper.java @@ -50,6 +50,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.sort.BucketedSort; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Arrays; @@ -157,6 +159,12 @@ public class IdFieldMapper extends MetadataFieldMapper { 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 2500358a3e5d..88282fa68572 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.common.xcontent.XContentBuilder; 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 class IndexFieldMapper extends MetadataFieldMapper { 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 2b52e42ffe55..c1cf50a56cde 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.ScriptDocValues; 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 class IpFieldMapper extends FieldMapper { return new DocValuesIndexFieldData.Builder().scriptFunction(IpScriptDocValues::new); } + @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/KeywordFieldMapper.java b/server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java index 099ae9b2aa7a..7fde4359742e 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.analysis.NamedAnalyzer; 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 final class KeywordFieldMapper extends FieldMapper { 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 eebe7c0f23fe..67bbdaf79ce7 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.QueryShardContext; 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 abstract class MappedFieldType extends FieldType { 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 0a473bd189e3..fa7055cadd2a 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.IndexNumericFieldData.NumericType; 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 class NumberFieldMapper extends FieldMapper { 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 289eae555514..44de6d88fd36 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.IndexFieldData; 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; @@ -92,14 +94,6 @@ public class RangeFieldMapper extends FieldMapper { return (RangeFieldType)fieldType; } - @Override - public Builder docValues(boolean docValues) { - if (docValues) { - throw new IllegalArgumentException("field [" + name + "] does not currently support " + TypeParsers.DOC_VALUES); - } - return super.docValues(docValues); - } - public Builder coerce(boolean coerce) { this.coerce = coerce; return builder; @@ -242,6 +236,11 @@ public class RangeFieldMapper extends FieldMapper { 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 5bb000741586..5bbff9e90a21 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.fielddata.plain.DocValuesIndexFieldData; 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 class SeqNoFieldMapper extends MetadataFieldMapper { 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 46ef8a732a69..b237d7cbd82e 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.IndexFieldData; 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 class TextFieldMapper extends FieldMapper { 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 5d9c9e20a1c1..e5c33b83f24e 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.IndexSettings; 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 class TypeFieldMapper extends MetadataFieldMapper { return new ConstantIndexFieldData.Builder(typeFunction); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public boolean isSearchable() { return true; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeTests.java index 56587c78708c..0bf34f6e4a3e 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/IpRangeTests.java @@ -23,27 +23,8 @@ import org.elasticsearch.common.network.NetworkAddress; import org.elasticsearch.search.aggregations.BaseAggregationTestCase; import org.elasticsearch.search.aggregations.bucket.range.IpRangeAggregationBuilder; -import java.net.InetAddress; -import java.net.UnknownHostException; - public class IpRangeTests extends BaseAggregationTestCase { - private static String randomIp(boolean v4) { - try { - if (v4) { - byte[] ipv4 = new byte[4]; - random().nextBytes(ipv4); - return NetworkAddress.format(InetAddress.getByAddress(ipv4)); - } else { - byte[] ipv6 = new byte[16]; - random().nextBytes(ipv6); - return NetworkAddress.format(InetAddress.getByAddress(ipv6)); - } - } catch (UnknownHostException e) { - throw new AssertionError(); - } - } - @Override protected IpRangeAggregationBuilder createTestAggregatorBuilder() { int numRanges = randomIntBetween(1, 10); @@ -62,16 +43,17 @@ public class IpRangeTests extends BaseAggregationTestCase 0) && (from == null || from.compareTo(value) <= 0)) { return true; diff --git a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java index 3c510f0bf782..858d3207e0a7 100644 --- a/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java +++ b/server/src/test/java/org/elasticsearch/search/aggregations/bucket/terms/TermsAggregatorTests.java @@ -80,7 +80,9 @@ import org.elasticsearch.search.aggregations.metrics.InternalTopHits; import org.elasticsearch.search.aggregations.metrics.TopHitsAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.BucketScriptPipelineAggregationBuilder; import org.elasticsearch.search.aggregations.support.AggregationInspectionHelper; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; import org.elasticsearch.search.aggregations.support.ValueType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.ScoreSortBuilder; import org.elasticsearch.test.geo.RandomGeoGenerator; @@ -122,6 +124,17 @@ public class TermsAggregatorTests extends AggregatorTestCase { } } + @Override + protected AggregationBuilder createAggBuilderForTypeTest(MappedFieldType fieldType, String fieldName) { + return new TermsAggregationBuilder("foo", ValueType.STRING).field(fieldName); + } + + @Override + protected List getSupportedValuesSourceTypes() { + return List.of(CoreValuesSourceType.NUMERIC, + CoreValuesSourceType.BYTES); + } + public void testGlobalOrdinalsExecutionHint() throws Exception { randomizeAggregatorImpl = false; diff --git a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java index eca219a868bf..ee08622fef07 100644 --- a/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java @@ -18,20 +18,29 @@ */ package org.elasticsearch.search.aggregations; +import org.apache.lucene.document.BinaryDocValuesField; +import org.apache.lucene.document.InetAddressPoint; +import org.apache.lucene.document.LatLonDocValuesField; +import org.apache.lucene.document.SortedNumericDocValuesField; +import org.apache.lucene.document.SortedSetDocValuesField; import org.apache.lucene.index.AssertingDirectoryReader; import org.apache.lucene.index.CompositeReaderContext; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.RandomIndexWriter; import org.apache.lucene.search.AssertingIndexSearcher; import org.apache.lucene.search.Collector; import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; import org.apache.lucene.search.QueryCache; import org.apache.lucene.search.QueryCachingPolicy; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; +import org.apache.lucene.store.Directory; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.Version; import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.common.breaker.CircuitBreaker; @@ -51,23 +60,39 @@ import org.elasticsearch.index.cache.query.DisabledQueryCache; import org.elasticsearch.index.fielddata.IndexFieldData; import org.elasticsearch.index.fielddata.IndexFieldDataCache; import org.elasticsearch.index.fielddata.IndexFieldDataService; +import org.elasticsearch.index.mapper.BinaryFieldMapper; +import org.elasticsearch.index.mapper.BooleanFieldMapper; +import org.elasticsearch.index.mapper.CompletionFieldMapper; import org.elasticsearch.index.mapper.ContentPath; +import org.elasticsearch.index.mapper.DateFieldMapper; import org.elasticsearch.index.mapper.DocumentMapper; +import org.elasticsearch.index.mapper.FieldAliasMapper; +import org.elasticsearch.index.mapper.FieldMapper; +import org.elasticsearch.index.mapper.GeoShapeFieldMapper; +import org.elasticsearch.index.mapper.IpFieldMapper; import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.Mapper; import org.elasticsearch.index.mapper.Mapper.BuilderContext; import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.mapper.ObjectMapper; import org.elasticsearch.index.mapper.ObjectMapper.Nested; +import org.elasticsearch.index.mapper.RangeFieldMapper; +import org.elasticsearch.index.mapper.RangeType; +import org.elasticsearch.index.mapper.TextFieldMapper; import org.elasticsearch.index.query.QueryShardContext; import org.elasticsearch.index.shard.IndexShard; import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.indices.breaker.CircuitBreakerService; import org.elasticsearch.indices.breaker.NoneCircuitBreakerService; import org.elasticsearch.indices.fielddata.cache.IndicesFieldDataCache; +import org.elasticsearch.indices.mapper.MapperRegistry; import org.elasticsearch.mock.orig.Mockito; import org.elasticsearch.script.ScriptService; import org.elasticsearch.search.aggregations.MultiBucketConsumerService.MultiBucketConsumer; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import org.elasticsearch.search.fetch.FetchPhase; import org.elasticsearch.search.fetch.subphase.FetchDocValuesPhase; import org.elasticsearch.search.fetch.subphase.FetchSourcePhase; @@ -90,6 +115,7 @@ import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; +import static java.util.Collections.singleton; import static org.elasticsearch.test.InternalAggregationTestCase.DEFAULT_MAX_BUCKETS; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.anyString; @@ -107,6 +133,18 @@ public abstract class AggregatorTestCase extends ESTestCase { private List releasables = new ArrayList<>(); private static final String TYPE_NAME = "type"; + // A list of field types that should not be tested, or are not currently supported + private static List TYPE_TEST_BLACKLIST = List.of( + ObjectMapper.CONTENT_TYPE, // Cannot aggregate objects + GeoShapeFieldMapper.CONTENT_TYPE, // Cannot aggregate geoshapes (yet) + + TextFieldMapper.CONTENT_TYPE, // TODO Does not support doc values, but does support FD, needs a lot of mocking + ObjectMapper.NESTED_CONTENT_TYPE, // TODO support for nested + CompletionFieldMapper.CONTENT_TYPE, // TODO support completion + FieldAliasMapper.CONTENT_TYPE // TODO support alias + ); + + /** * Allows subclasses to provide alternate names for the provided field type, which * can be useful when testing aggregations on field aliases. @@ -524,6 +562,189 @@ public abstract class AggregatorTestCase extends ESTestCase { } } + /** + * Implementors should return a list of {@link ValuesSourceType} that the aggregator supports. + * This is used to test the matrix of supported/unsupported field types against the aggregator + * and verify it works (or doesn't) as expected. + * + * If this method is implemented, {@link AggregatorTestCase#createAggBuilderForTypeTest(MappedFieldType, String)} + * should be implemented as well. + * + * @return list of supported ValuesSourceTypes + */ + protected List getSupportedValuesSourceTypes() { + // If aggs don't override this method, an empty list allows the test to be skipped. + // Once all aggs implement this method we should make it abstract and not allow skipping. + return Collections.emptyList(); + } + + /** + * This method is invoked each time a field type is tested in {@link AggregatorTestCase#testSupportedFieldTypes()}. + * The field type and name are provided, and the implementor is expected to return an AggBuilder accordingly. + * The AggBuilder should be returned even if the aggregation does not support the field type, because + * the test will check if an exception is thrown in that case. + * + * The list of supported types are provided by {@link AggregatorTestCase#getSupportedValuesSourceTypes()}, + * which must also be implemented. + * + * @param fieldType the type of the field that will be tested + * @param fieldName the name of the field that will be test + * @return an aggregation builder to test against the field + */ + protected AggregationBuilder createAggBuilderForTypeTest(MappedFieldType fieldType, String fieldName) { + throw new UnsupportedOperationException("If getSupportedValuesSourceTypes() is implemented, " + + "createAggBuilderForTypeTest() must be implemented as well."); + } + + /** + * This test will validate that an aggregator succeeds or fails to run against all the field types + * that are registered in {@link IndicesModule} (e.g. all the core field types). An aggregator + * is provided by the implementor class, and it is executed against each field type in turn. If + * an exception is thrown when the field is supported, that will fail the test. Similarly, if + * an exception _is not_ thrown when a field is unsupported, that will also fail the test. + * + * Exception types/messages are not currently checked, just presence/absence of an exception. + */ + public void testSupportedFieldTypes() throws IOException { + MapperRegistry mapperRegistry = new IndicesModule(Collections.emptyList()).getMapperRegistry(); + Settings settings = Settings.builder().put("index.version.created", Version.CURRENT.id).build(); + String fieldName = "typeTestFieldName"; + List supportedVSTypes = getSupportedValuesSourceTypes(); + + if (supportedVSTypes.isEmpty()) { + // If the test says it doesn't support any VStypes, it has not been converted yet so skip + return; + } else if (supportedVSTypes.contains(CoreValuesSourceType.ANY)) { + throw new IllegalArgumentException("Tests should not specify CoreValuesSourceType.ANY as a supported ValuesSourceType, " + + "but should instead list the concrete ValuesSourceTypes that are supported"); + } + + for (Map.Entry mappedType : mapperRegistry.getMapperParsers().entrySet()) { + + // Some field types should not be tested, or require more work and are not ready yet + if (TYPE_TEST_BLACKLIST.contains(mappedType.getKey())) { + continue; + } + + Map source = new HashMap<>(); + source.put("type", mappedType.getKey()); + source.put("doc_values", "true"); + + Mapper.Builder builder = mappedType.getValue().parse(fieldName, source, new MockParserContext()); + FieldMapper mapper = (FieldMapper) builder.build(new BuilderContext(settings, new ContentPath())); + + MappedFieldType fieldType = mapper.fieldType(); + + // Non-aggregatable fields are not testable (they will throw an error on all aggs anyway), so skip + if (fieldType.isAggregatable() == false) { + continue; + } + + try (Directory directory = newDirectory()) { + RandomIndexWriter indexWriter = new RandomIndexWriter(random(), directory); + writeTestDoc(fieldType, fieldName, indexWriter); + indexWriter.close(); + + try (IndexReader indexReader = DirectoryReader.open(directory)) { + IndexSearcher indexSearcher = newSearcher(indexReader, true, true); + AggregationBuilder aggregationBuilder = createAggBuilderForTypeTest(fieldType, fieldName); + + // TODO in the future we can make this more explicit with expectThrows(), when the exceptions are standardized + try { + searchAndReduce(indexSearcher, new MatchAllDocsQuery(), aggregationBuilder, fieldType); + if (supportedVSTypes.contains(fieldType.getValuesSourceType()) == false) { + fail("Aggregator [" + aggregationBuilder.getType() + "] should not support field type [" + + fieldType.typeName() + "] but executing against the field did not throw an excetion"); + } + } catch (Exception e) { + if (supportedVSTypes.contains(fieldType.getValuesSourceType())) { + fail("Aggregator [" + aggregationBuilder.getType() + "] supports field type [" + + fieldType.typeName() + "] but executing against the field threw an exception: [" + e.getMessage() + "]"); + } + } + } + } + } + } + + /** + * Helper method to write a single document with a single value specific to the requested fieldType. + * + * Throws an exception if it encounters an unknown field type, to prevent new ones from sneaking in without + * being tested. + */ + private void writeTestDoc(MappedFieldType fieldType, String fieldName, RandomIndexWriter iw) throws IOException { + + if (fieldType.getValuesSourceType().equals(CoreValuesSourceType.NUMERIC)) { + // TODO note: once VS refactor adds DATE/BOOLEAN, this conditional will go away + if (fieldType.typeName().equals(DateFieldMapper.CONTENT_TYPE) + || fieldType.typeName().equals(DateFieldMapper.DATE_NANOS_CONTENT_TYPE)) { + iw.addDocument(singleton(new SortedNumericDocValuesField(fieldName, randomNonNegativeLong()))); + } else if (fieldType.typeName().equals(BooleanFieldMapper.CONTENT_TYPE)) { + iw.addDocument(singleton(new SortedNumericDocValuesField(fieldName, randomBoolean() ? 0 : 1))); + } else { + iw.addDocument(singleton(new SortedNumericDocValuesField(fieldName, randomLong()))); + } + } else if (fieldType.getValuesSourceType().equals(CoreValuesSourceType.BYTES)) { + if (fieldType.typeName().equals(BinaryFieldMapper.CONTENT_TYPE)) { + iw.addDocument(singleton(new BinaryFieldMapper.CustomBinaryDocValuesField(fieldName, new BytesRef("a").bytes))); + } else if (fieldType.typeName().equals(IpFieldMapper.CONTENT_TYPE)) { + // TODO note: once VS refactor adds IP, this conditional will go away + boolean v4 = randomBoolean(); + iw.addDocument(singleton(new SortedSetDocValuesField(fieldName, new BytesRef(InetAddressPoint.encode(randomIp(v4)))))); + } else { + iw.addDocument(singleton(new SortedSetDocValuesField(fieldName, new BytesRef("a")))); + } + } else if (fieldType.getValuesSourceType().equals(CoreValuesSourceType.RANGE)) { + Object start; + Object end; + RangeType rangeType; + + if (fieldType.typeName().equals(RangeType.DOUBLE.typeName())) { + start = randomDouble(); + end = RangeType.DOUBLE.nextUp(start); + rangeType = RangeType.DOUBLE; + } else if (fieldType.typeName().equals(RangeType.FLOAT.typeName())) { + start = randomFloat(); + end = RangeType.FLOAT.nextUp(start); + rangeType = RangeType.DOUBLE; + } else if (fieldType.typeName().equals(RangeType.IP.typeName())) { + boolean v4 = randomBoolean(); + start = randomIp(v4); + end = RangeType.IP.nextUp(start); + rangeType = RangeType.IP; + } else if (fieldType.typeName().equals(RangeType.LONG.typeName())) { + start = randomLong(); + end = RangeType.LONG.nextUp(start); + rangeType = RangeType.LONG; + } else if (fieldType.typeName().equals(RangeType.INTEGER.typeName())) { + start = randomInt(); + end = RangeType.INTEGER.nextUp(start); + rangeType = RangeType.INTEGER; + } else if (fieldType.typeName().equals(RangeType.DATE.typeName())) { + start = randomNonNegativeLong(); + end = RangeType.DATE.nextUp(start); + rangeType = RangeType.DATE; + } else { + throw new IllegalStateException("Unknown type of range [" + fieldType.typeName() + "]"); + } + + final RangeFieldMapper.Range range = new RangeFieldMapper.Range(rangeType, start, end, true, true); + iw.addDocument(singleton(new BinaryDocValuesField(fieldName, rangeType.encodeRanges(Collections.singleton(range))))); + + } else if (fieldType.getValuesSourceType().equals(CoreValuesSourceType.GEOPOINT)) { + iw.addDocument(singleton(new LatLonDocValuesField(fieldName, randomDouble(), randomDouble()))); + } else { + throw new IllegalStateException("Unknown field type [" + fieldType.typeName() + "]"); + } + } + + private class MockParserContext extends Mapper.TypeParser.ParserContext { + MockParserContext() { + super(null, null, null, null, null); + } + } + @After private void cleanupReleasables() { Releasables.close(releasables); diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java index a5005c7583a8..21078a085b01 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java @@ -29,7 +29,6 @@ import com.carrotsearch.randomizedtesting.generators.RandomNumbers; import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomStrings; import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter; - import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -118,6 +117,8 @@ import org.junit.rules.RuleChain; import java.io.IOException; import java.io.InputStream; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.nio.file.Path; import java.time.ZoneId; import java.util.ArrayList; @@ -1394,4 +1395,20 @@ public abstract class ESTestCase extends LuceneTestCase { assert startAt >= 0 : "Unexpected test worker Id, resulting port range would be negative"; return 10300 + (startAt * 100); } + + protected static InetAddress randomIp(boolean v4) { + try { + if (v4) { + byte[] ipv4 = new byte[4]; + random().nextBytes(ipv4); + return InetAddress.getByAddress(ipv4); + } else { + byte[] ipv6 = new byte[16]; + random().nextBytes(ipv6); + return InetAddress.getByAddress(ipv6); + } + } catch (UnknownHostException e) { + throw new AssertionError(); + } + } } 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 20326e82cadc..f656f9bdfc83 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 @@ -53,6 +53,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.sort.BucketedSort; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Iterator; @@ -276,6 +278,12 @@ public class HistogramFieldMapper extends FieldMapper { }; } + @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 2cad82871170..09ef54db97a6 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 @@ -55,6 +55,8 @@ import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.MultiValueMode; import org.elasticsearch.search.sort.BucketedSort; import org.elasticsearch.search.sort.SortOrder; +import org.elasticsearch.search.aggregations.support.CoreValuesSourceType; +import org.elasticsearch.search.aggregations.support.ValuesSourceType; import java.io.IOException; import java.util.Iterator; @@ -349,6 +351,11 @@ public final class FlatObjectFieldMapper extends DynamicKeyFieldMapper { failIfNoDocValues(); return new KeyedFlatObjectFieldData.Builder(key); } + + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } } /** 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 a33a9794acb4..1fd7543411ef 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.MapperParsingException; 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 class DenseVectorFieldMapper extends FieldMapper implements ArrayValueMap return new VectorDVIndexFieldData.Builder(); } + @Override + public ValuesSourceType getValuesSourceType() { + return CoreValuesSourceType.BYTES; + } + @Override public Query termQuery(Object value, QueryShardContext context) { throw new UnsupportedOperationException(