diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java index 3b4d445002073..8cbb2f7aff068 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java @@ -92,7 +92,8 @@ public void setup() { var fields = 10_000; var mapping = LinkedHashMap.newLinkedHashMap(fields); for (int i = 0; i < fields; i++) { - mapping.put("field" + i, new EsField("field-" + i, TEXT, emptyMap(), true)); + // We're creating a standard index, so none of these fields should be marked as dimensions. + mapping.put("field" + i, new EsField("field-" + i, TEXT, emptyMap(), true, EsField.TimeSeriesFieldType.NONE)); } var esIndex = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD)); diff --git a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java index 5bd003fe4271f..6357c0e9c396f 100644 --- a/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java +++ b/benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java @@ -212,7 +212,7 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) { FieldAttribute timestamp = new FieldAttribute( Source.EMPTY, "timestamp", - new EsField("timestamp", DataType.DATETIME, Map.of(), true) + new EsField("timestamp", DataType.DATETIME, Map.of(), true, EsField.TimeSeriesFieldType.NONE) ); yield EvalMapper.toEvaluator( FOLD_CONTEXT, @@ -321,19 +321,35 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) { } private static FieldAttribute longField() { - return new FieldAttribute(Source.EMPTY, "long", new EsField("long", DataType.LONG, Map.of(), true)); + return new FieldAttribute( + Source.EMPTY, + "long", + new EsField("long", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); } private static FieldAttribute doubleField() { - return new FieldAttribute(Source.EMPTY, "double", new EsField("double", DataType.DOUBLE, Map.of(), true)); + return new FieldAttribute( + Source.EMPTY, + "double", + new EsField("double", DataType.DOUBLE, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); } private static FieldAttribute intField() { - return new FieldAttribute(Source.EMPTY, "int", new EsField("int", DataType.INTEGER, Map.of(), true)); + return new FieldAttribute( + Source.EMPTY, + "int", + new EsField("int", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); } private static FieldAttribute keywordField() { - return new FieldAttribute(Source.EMPTY, "keyword", new EsField("keyword", DataType.KEYWORD, Map.of(), true)); + return new FieldAttribute( + Source.EMPTY, + "keyword", + new EsField("keyword", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); } private static Configuration configuration() { diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java index 6b700f0ee6a7f..28ea103164c79 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java @@ -149,4 +149,9 @@ public static boolean dataTypeEquals(List left, List right } return true; } + + /** + * @return true if the attribute represents a TSDB dimension type + */ + public abstract boolean isDimension(); } diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/EmptyAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/EmptyAttribute.java index 1cacf74a8207c..cf6a696aabb7b 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/EmptyAttribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/EmptyAttribute.java @@ -44,6 +44,11 @@ protected String label() { return "e"; } + @Override + public boolean isDimension() { + return false; + } + @Override public boolean resolved() { return true; diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/FieldAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/FieldAttribute.java index 5a31be1ef5b70..e9cca5c79c4bf 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/FieldAttribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/FieldAttribute.java @@ -216,6 +216,11 @@ protected String label() { return "f"; } + @Override + public boolean isDimension() { + return field.getTimeSeriesFieldType() == EsField.TimeSeriesFieldType.DIMENSION; + } + public EsField field() { return field; } diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java index 34ff2cec2960a..07fe6b16b3bd1 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java @@ -120,6 +120,11 @@ protected String label() { return "m"; } + @Override + public boolean isDimension() { + return false; + } + @Override protected NodeInfo info() { return NodeInfo.create(this, MetadataAttribute::new, name(), dataType(), nullable(), id(), synthetic(), searchable); diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/ReferenceAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/ReferenceAttribute.java index 404cd75edd5e4..9e203f84b68d9 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/ReferenceAttribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/ReferenceAttribute.java @@ -117,4 +117,9 @@ protected NodeInfo info() { protected String label() { return "r"; } + + @Override + public boolean isDimension() { + return false; + } } diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/UnresolvedAttribute.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/UnresolvedAttribute.java index e73ee2f09458d..bcafeb17ac808 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/UnresolvedAttribute.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/UnresolvedAttribute.java @@ -107,6 +107,11 @@ protected String label() { return UNRESOLVED_PREFIX; } + @Override + public boolean isDimension() { + return false; + } + @Override public String nodeString() { return toString(); diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java index bd66519f2f5e2..7eab34f01a1c2 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/DateEsField.java @@ -20,8 +20,8 @@ */ public class DateEsField extends EsField { - public static DateEsField dateEsField(String name, Map properties, boolean hasDocValues) { - return new DateEsField(name, DataType.DATETIME, properties, hasDocValues, TimeSeriesFieldType.UNKNOWN); + public static DateEsField dateEsField(String name, Map properties, boolean hasDocValues, TimeSeriesFieldType tsType) { + return new DateEsField(name, DataType.DATETIME, properties, hasDocValues, tsType); } private DateEsField( diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java index daf26a7b5348f..87dadaed4da91 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java @@ -7,6 +7,7 @@ package org.elasticsearch.xpack.esql.core.type; import org.elasticsearch.TransportVersions; +import org.elasticsearch.action.fieldcaps.IndexFieldCapabilities; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Writeable; @@ -31,10 +32,36 @@ public class EsField implements Writeable { * roles within the ESQL query processing pipeline. */ public enum TimeSeriesFieldType implements Writeable { - UNKNOWN(0), - NONE(1), - METRIC(2), - DIMENSION(3); + UNKNOWN(0) { + @Override + public TimeSeriesFieldType merge(TimeSeriesFieldType other) { + return other; + } + }, + NONE(1) { + @Override + public TimeSeriesFieldType merge(TimeSeriesFieldType other) { + return other; + } + }, + METRIC(2) { + @Override + public TimeSeriesFieldType merge(TimeSeriesFieldType other) { + if (other != DIMENSION) { + return METRIC; + } + throw new IllegalStateException("Time Series Metadata conflict. Cannot merge [" + other + "] with [METRIC]."); + } + }, + DIMENSION(3) { + @Override + public TimeSeriesFieldType merge(TimeSeriesFieldType other) { + if (other != METRIC) { + return DIMENSION; + } + throw new IllegalStateException("Time Series Metadata conflict. Cannot merge [" + other + "] with [DIMENSION]."); + } + }; private final int id; @@ -57,6 +84,19 @@ public static TimeSeriesFieldType readFromStream(StreamInput in) throws IOExcept default -> throw new IOException("Unexpected value for TimeSeriesFieldType: " + id); }; } + + public static TimeSeriesFieldType fromIndexFieldCapabilities(IndexFieldCapabilities capabilities) { + if (capabilities.isDimension()) { + assert capabilities.metricType() == null; + return DIMENSION; + } + if (capabilities.metricType() != null) { + return METRIC; + } + return NONE; + } + + public abstract TimeSeriesFieldType merge(TimeSeriesFieldType other); } private static Map> readers = Map.ofEntries( @@ -83,13 +123,8 @@ public static Reader getReader(String name) { private final Map properties; private final String name; private final boolean isAlias; - // Because the subclasses all reimplement serialization, this needs to be writeable from subclass constructors private final TimeSeriesFieldType timeSeriesFieldType; - public EsField(String name, DataType esDataType, Map properties, boolean aggregatable) { - this(name, esDataType, properties, aggregatable, false, TimeSeriesFieldType.UNKNOWN); - } - public EsField( String name, DataType esDataType, @@ -100,10 +135,6 @@ public EsField( this(name, esDataType, properties, aggregatable, false, timeSeriesFieldType); } - public EsField(String name, DataType esDataType, Map properties, boolean aggregatable, boolean isAlias) { - this(name, esDataType, properties, aggregatable, isAlias, TimeSeriesFieldType.UNKNOWN); - } - public EsField( String name, DataType esDataType, @@ -247,6 +278,10 @@ public Exact getExactInfo() { return Exact.EXACT_FIELD; } + public TimeSeriesFieldType getTimeSeriesFieldType() { + return timeSeriesFieldType; + } + @Override public String toString() { return name + "@" + esDataType.typeName() + "=" + properties; @@ -265,12 +300,13 @@ public boolean equals(Object o) { && isAlias == field.isAlias && esDataType == field.esDataType && Objects.equals(name, field.name) - && Objects.equals(properties, field.properties); + && Objects.equals(properties, field.properties) + && Objects.equals(timeSeriesFieldType, field.timeSeriesFieldType); } @Override public int hashCode() { - return Objects.hash(esDataType, aggregatable, properties, name, isAlias); + return Objects.hash(esDataType, aggregatable, properties, name, isAlias, timeSeriesFieldType); } public static final class Exact { diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java index e56aafa267400..85a348a7a87d6 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/KeywordEsField.java @@ -10,7 +10,6 @@ import org.elasticsearch.common.io.stream.StreamOutput; import java.io.IOException; -import java.util.Collections; import java.util.Map; import java.util.Objects; @@ -26,23 +25,16 @@ public class KeywordEsField extends EsField { private final int precision; private final boolean normalized; - public KeywordEsField(String name) { - this(name, Collections.emptyMap(), true, Short.MAX_VALUE, false); - } - - public KeywordEsField(String name, Map properties, boolean hasDocValues, int precision, boolean normalized) { - this(name, properties, hasDocValues, precision, normalized, false); - } - public KeywordEsField( String name, Map properties, boolean hasDocValues, int precision, boolean normalized, - boolean isAlias + boolean isAlias, + TimeSeriesFieldType timeSeriesFieldType ) { - this(name, KEYWORD, properties, hasDocValues, precision, normalized, isAlias, TimeSeriesFieldType.UNKNOWN); + this(name, KEYWORD, properties, hasDocValues, precision, normalized, isAlias, timeSeriesFieldType); } protected KeywordEsField( diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/MultiTypeEsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/MultiTypeEsField.java index 8056580b13431..b26f1f060afc4 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/MultiTypeEsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/MultiTypeEsField.java @@ -33,11 +33,6 @@ public class MultiTypeEsField extends EsField { private final Map indexToConversionExpressions; - public MultiTypeEsField(String name, DataType dataType, boolean aggregatable, Map indexToConversionExpressions) { - super(name, dataType, Map.of(), aggregatable); - this.indexToConversionExpressions = indexToConversionExpressions; - } - public MultiTypeEsField( String name, DataType dataType, @@ -99,7 +94,13 @@ public static MultiTypeEsField resolveFrom( indexToConversionExpressions.put(indexName, convertExpr); } } - return new MultiTypeEsField(invalidMappedField.getName(), resolvedDataType, false, indexToConversionExpressions); + return new MultiTypeEsField( + invalidMappedField.getName(), + resolvedDataType, + false, + indexToConversionExpressions, + invalidMappedField.getTimeSeriesFieldType() + ); } @Override diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/PotentiallyUnmappedKeywordEsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/PotentiallyUnmappedKeywordEsField.java index 8672b6b61dee7..679e77ad2c636 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/PotentiallyUnmappedKeywordEsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/PotentiallyUnmappedKeywordEsField.java @@ -9,6 +9,7 @@ import org.elasticsearch.common.io.stream.StreamInput; import java.io.IOException; +import java.util.Collections; /** * This class is used as a marker for fields that may be unmapped, where an unmapped field is a field which exists in the _source but is not @@ -17,7 +18,7 @@ */ public class PotentiallyUnmappedKeywordEsField extends KeywordEsField { public PotentiallyUnmappedKeywordEsField(String name) { - super(name); + super(name, Collections.emptyMap(), true, Short.MAX_VALUE, false, false, TimeSeriesFieldType.UNKNOWN); } public PotentiallyUnmappedKeywordEsField(StreamInput in) throws IOException { diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java index 7c9508959dcae..bb05dbd4f9030 100644 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java +++ b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/TextEsField.java @@ -25,14 +25,6 @@ */ public class TextEsField extends EsField { - public TextEsField(String name, Map properties, boolean hasDocValues) { - this(name, properties, hasDocValues, false); - } - - public TextEsField(String name, Map properties, boolean hasDocValues, boolean isAlias) { - super(name, TEXT, properties, hasDocValues, isAlias, TimeSeriesFieldType.UNKNOWN); - } - public TextEsField( String name, Map properties, diff --git a/x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/util/TestUtils.java b/x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/util/TestUtils.java index 8f07d40bd0665..c86f704e62cb2 100644 --- a/x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/util/TestUtils.java +++ b/x-pack/plugin/esql-core/src/test/java/org/elasticsearch/xpack/esql/core/util/TestUtils.java @@ -54,7 +54,7 @@ public static FieldAttribute fieldAttribute() { } public static FieldAttribute fieldAttribute(String name, DataType type) { - return new FieldAttribute(EMPTY, name, new EsField(name, type, emptyMap(), randomBoolean())); + return new FieldAttribute(EMPTY, name, new EsField(name, type, emptyMap(), randomBoolean(), EsField.TimeSeriesFieldType.NONE)); } public static FieldAttribute getFieldAttribute(String name) { @@ -62,7 +62,7 @@ public static FieldAttribute getFieldAttribute(String name) { } public static FieldAttribute getFieldAttribute(String name, DataType dataType) { - return new FieldAttribute(EMPTY, name, new EsField(name + "f", dataType, emptyMap(), true)); + return new FieldAttribute(EMPTY, name, new EsField(name + "f", dataType, emptyMap(), true, EsField.TimeSeriesFieldType.NONE)); } /** diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java index 7f9f77509310e..49f6b907a850a 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java @@ -205,7 +205,7 @@ public static FieldAttribute getFieldAttribute(String name) { } public static FieldAttribute getFieldAttribute(String name, DataType dataType) { - return new FieldAttribute(EMPTY, name, new EsField(name + "f", dataType, emptyMap(), true)); + return new FieldAttribute(EMPTY, name, new EsField(name + "f", dataType, emptyMap(), true, EsField.TimeSeriesFieldType.NONE)); } public static FieldAttribute fieldAttribute() { @@ -213,7 +213,7 @@ public static FieldAttribute fieldAttribute() { } public static FieldAttribute fieldAttribute(String name, DataType type) { - return new FieldAttribute(EMPTY, name, new EsField(name, type, emptyMap(), randomBoolean())); + return new FieldAttribute(EMPTY, name, new EsField(name, type, emptyMap(), randomBoolean(), EsField.TimeSeriesFieldType.NONE)); } public static Literal of(Object value) { diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/LoadMapping.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/LoadMapping.java index 1146d7729a8d4..29f2f6ed4e927 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/LoadMapping.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/LoadMapping.java @@ -102,19 +102,19 @@ private static void walkMapping(String name, Object value, Map boolean docValues = boolSetting(content.get("doc_values"), esDataType.hasDocValues()); final EsField field; if (esDataType == TEXT) { - field = new TextEsField(name, properties, docValues); + field = new TextEsField(name, properties, docValues, false, EsField.TimeSeriesFieldType.NONE); } else if (esDataType == KEYWORD) { int length = intSetting(content.get("ignore_above"), Short.MAX_VALUE); boolean normalized = Strings.hasText(textSetting(content.get("normalizer"), null)); - field = new KeywordEsField(name, properties, docValues, length, normalized); + field = new KeywordEsField(name, properties, docValues, length, normalized, false, EsField.TimeSeriesFieldType.NONE); } else if (esDataType == DATETIME) { - field = DateEsField.dateEsField(name, properties, docValues); + field = DateEsField.dateEsField(name, properties, docValues, EsField.TimeSeriesFieldType.NONE); } else if (esDataType == UNSUPPORTED) { String type = content.get("type").toString(); field = new UnsupportedEsField(name, List.of(type), null, properties); propagateUnsupportedType(name, type, properties); } else { - field = new EsField(name, esDataType, properties, docValues); + field = new EsField(name, esDataType, properties, docValues, EsField.TimeSeriesFieldType.NONE); } mapping.put(name, field); } else { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java index dd8b20412c2b8..b23f8e60fd88d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java @@ -311,7 +311,7 @@ private static void mappingAsAttributes(List list, Source source, Str // due to a bug also copy the field since the Attribute hierarchy extracts the data type // directly even if the data type is passed explicitly if (type != t.getDataType()) { - t = new EsField(t.getName(), type, t.getProperties(), t.isAggregatable(), t.isAlias()); + t = new EsField(t.getName(), type, t.getProperties(), t.isAggregatable(), t.isAlias(), t.getTimeSeriesFieldType()); } FieldAttribute attribute = t instanceof UnsupportedEsField uef @@ -453,7 +453,7 @@ private LocalRelation tableMapAsRelation(Source source, Map mapT String name = entry.getKey(); Column column = entry.getValue(); // create a fake ES field - alternative is to use a ReferenceAttribute - EsField field = new EsField(name, column.type(), Map.of(), false, false); + EsField field = new EsField(name, column.type(), Map.of(), false, false, EsField.TimeSeriesFieldType.UNKNOWN); attributes.add(new FieldAttribute(source, null, name, field)); // prepare the block for the supplier blocks[i++] = column.values(); @@ -1809,7 +1809,8 @@ private Expression resolveConvertFunction(ConvertFunction convert, List mergeLookupResults( DataType.fromTypeName(field.getDataType().typeName()), field.getProperties(), field.isAggregatable(), - field.isAlias() + field.isAlias(), + field.getTimeSeriesFieldType() ); EsField old = mappings.putIfAbsent(m.getKey(), field); if (old != null && old.getDataType().equals(field.getDataType()) == false) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ResolvedEnrichPolicy.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ResolvedEnrichPolicy.java index 64595e776a96e..a42881a13835d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ResolvedEnrichPolicy.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/ResolvedEnrichPolicy.java @@ -54,7 +54,14 @@ public void writeTo(StreamOutput out) throws IOException { * as though it were the base class. */ (o, v) -> { - var field = new EsField(v.getName(), v.getDataType(), v.getProperties(), v.isAggregatable(), v.isAlias()); + var field = new EsField( + v.getName(), + v.getDataType(), + v.getProperties(), + v.isAggregatable(), + v.isAlias(), + v.getTimeSeriesFieldType() + ); if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_15_2)) { field.writeTo(o); } else { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/EsQueryExec.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/EsQueryExec.java index 2e74c7153f77e..f29554081ed01 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/EsQueryExec.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/physical/EsQueryExec.java @@ -46,7 +46,13 @@ public class EsQueryExec extends LeafExec implements EstimatesRowSize { EsQueryExec::readFrom ); - public static final EsField DOC_ID_FIELD = new EsField("_doc", DataType.DOC_DATA_TYPE, Map.of(), false); + public static final EsField DOC_ID_FIELD = new EsField( + "_doc", + DataType.DOC_DATA_TYPE, + Map.of(), + false, + EsField.TimeSeriesFieldType.NONE + ); public static final List NO_SORTS = List.of(); // only exists to mimic older serialization, but we no longer serialize sorts private final String indexPattern; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java index d72f3ef0529ad..2f44db243b85c 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java @@ -98,6 +98,7 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit return IndexResolution.notFound(indexPattern); } + // For each field name, store a list of the field caps responses from each index Map> fieldsCaps = collectFieldCaps(fieldCapsResponse); // Build hierarchical fields - it's easier to do it in sorted order so the object fields come first. @@ -119,7 +120,8 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit String parent = name.substring(0, nextDot); EsField obj = fields.get(parent); if (obj == null) { - obj = new EsField(parent, OBJECT, new HashMap<>(), false, true); + // Object fields can't be dimensions, so we can safely hard code that here + obj = new EsField(parent, OBJECT, new HashMap<>(), false, true, EsField.TimeSeriesFieldType.NONE); isAlias = true; fields.put(parent, obj); } else if (firstUnsupportedParent == null && obj instanceof UnsupportedEsField unsupportedParent) { @@ -197,11 +199,17 @@ private static EsField createField( List rest = fcs.subList(1, fcs.size()); DataType type = EsqlDataTypeRegistry.INSTANCE.fromEs(first.type(), first.metricType()); boolean aggregatable = first.isAggregatable(); + EsField.TimeSeriesFieldType timeSeriesFieldType = EsField.TimeSeriesFieldType.UNKNOWN; if (rest.isEmpty() == false) { for (IndexFieldCapabilities fc : rest) { if (first.metricType() != fc.metricType()) { return conflictingMetricTypes(name, fullName, fieldCapsResponse); } + try { + timeSeriesFieldType = timeSeriesFieldType.merge(EsField.TimeSeriesFieldType.fromIndexFieldCapabilities(fc)); + } catch (IllegalArgumentException e) { + return new InvalidMappedField(name, e.getMessage()); + } } for (IndexFieldCapabilities fc : rest) { if (type != EsqlDataTypeRegistry.INSTANCE.fromEs(fc.type(), fc.metricType())) { @@ -216,22 +224,22 @@ private static EsField createField( // TODO I think we only care about unmapped fields if we're aggregating on them. do we even then? if (type == TEXT) { - return new TextEsField(name, new HashMap<>(), false, isAlias); + return new TextEsField(name, new HashMap<>(), false, isAlias, timeSeriesFieldType); } if (type == KEYWORD) { int length = Short.MAX_VALUE; // TODO: to check whether isSearchable/isAggregateable takes into account the presence of the normalizer boolean normalized = false; - return new KeywordEsField(name, new HashMap<>(), aggregatable, length, normalized, isAlias); + return new KeywordEsField(name, new HashMap<>(), aggregatable, length, normalized, isAlias, timeSeriesFieldType); } if (type == DATETIME) { - return DateEsField.dateEsField(name, new HashMap<>(), aggregatable); + return DateEsField.dateEsField(name, new HashMap<>(), aggregatable, timeSeriesFieldType); } if (type == UNSUPPORTED) { return unsupported(name, first); } - return new EsField(name, type, new HashMap<>(), aggregatable, isAlias); + return new EsField(name, type, new HashMap<>(), aggregatable, isAlias, timeSeriesFieldType); } private static UnsupportedEsField unsupported(String name, IndexFieldCapabilities fc) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java index d149fb012a14b..869a851a1fb34 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/CsvTests.java @@ -427,7 +427,13 @@ private static Map createMappingForIndex(CsvTestsDataLoader.Tes if (mapping.containsKey(entry.getKey())) { DataType dataType = DataType.fromTypeName(entry.getValue()); EsField field = mapping.get(entry.getKey()); - EsField editedField = new EsField(field.getName(), dataType, field.getProperties(), field.isAggregatable()); + EsField editedField = new EsField( + field.getName(), + dataType, + field.getProperties(), + field.isAggregatable(), + field.getTimeSeriesFieldType() + ); mapping.put(entry.getKey(), editedField); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java index 00f20b9376a6f..be3d70527c3f4 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/AbstractFunctionTestCase.java @@ -447,7 +447,7 @@ public static Stream validFunctionParameters() { * Build an {@link Attribute} that loads a field. */ public static FieldAttribute field(String name, DataType type) { - return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true)); + return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } /** @@ -456,7 +456,7 @@ public static FieldAttribute field(String name, DataType type) { public static Expression deepCopyOfField(String name, DataType type) { return new DeepCopy( Source.synthetic(name), - new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true)) + new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KnnTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KnnTests.java index 9bd4896350ca7..002c519b001f8 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KnnTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/KnnTests.java @@ -67,7 +67,7 @@ private static List testCaseSuppliers() { new FieldAttribute( Source.EMPTY, randomIdentifier(), - new EsField(randomIdentifier(), DENSE_VECTOR, Map.of(), false) + new EsField(randomIdentifier(), DENSE_VECTOR, Map.of(), false, EsField.TimeSeriesFieldType.NONE) ), DENSE_VECTOR, "dense_vector field" diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/NamedExpressionTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/NamedExpressionTests.java index 06e60fc437df0..00afd4947c890 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/NamedExpressionTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/NamedExpressionTests.java @@ -53,7 +53,11 @@ public void testArithmeticFunctionName() { } public void testNameForArithmeticFunctionAppliedOnTableColumn() { - FieldAttribute fa = new FieldAttribute(EMPTY, "myField", new EsField("myESField", DataType.INTEGER, emptyMap(), true)); + FieldAttribute fa = new FieldAttribute( + EMPTY, + "myField", + new EsField("myESField", DataType.INTEGER, emptyMap(), true, EsField.TimeSeriesFieldType.NONE) + ); String e = "myField + 10"; Add add = new Add(s(e), fa, l(10)); assertEquals(e, add.sourceText()); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java index 1235a175294af..e7d34e17b44c8 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/nulls/CoalesceTests.java @@ -205,7 +205,11 @@ protected Coalesce build(Source source, List args) { public void testCoalesceIsLazy() { List sub = new ArrayList<>(testCase.getDataAsFields()); - FieldAttribute evil = new FieldAttribute(Source.EMPTY, "evil", new EsField("evil", sub.get(0).dataType(), Map.of(), true)); + FieldAttribute evil = new FieldAttribute( + Source.EMPTY, + "evil", + new EsField("evil", sub.get(0).dataType(), Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); sub.add(evil); Coalesce exp = build(Source.EMPTY, sub); Layout.Builder builder = new Layout.Builder(); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithStaticTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithStaticTests.java index ddde306deed7a..7d8ab4f65b498 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithStaticTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/EndsWithStaticTests.java @@ -33,8 +33,16 @@ public void testLuceneQuery_AllLiterals_NonTranslatable() { public void testLuceneQuery_NonFoldableSuffix_NonTranslatable() { EndsWith function = new EndsWith( Source.EMPTY, - new FieldAttribute(Source.EMPTY, "field", new EsField("field", DataType.KEYWORD, Map.of(), true)), - new FieldAttribute(Source.EMPTY, "field", new EsField("suffix", DataType.KEYWORD, Map.of(), true)) + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("field", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ), + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("suffix", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ) ); assertThat(function.translatable(LucenePushdownPredicates.DEFAULT), equalTo(TranslationAware.Translatable.NO)); @@ -43,7 +51,11 @@ public void testLuceneQuery_NonFoldableSuffix_NonTranslatable() { public void testLuceneQuery_NonFoldableSuffix_Translatable() { EndsWith function = new EndsWith( Source.EMPTY, - new FieldAttribute(Source.EMPTY, "field", new EsField("suffix", DataType.KEYWORD, Map.of(), true)), + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("suffix", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ), Literal.keyword(Source.EMPTY, "a*b?c\\") ); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatStaticTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatStaticTests.java index 95db9daa21283..539e285b485cf 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatStaticTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/RepeatStaticTests.java @@ -83,7 +83,7 @@ private static Page row(List values) { } private static FieldAttribute field(String name, DataType type) { - return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true)); + return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } private DriverContext driverContext() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceStaticTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceStaticTests.java index cac4b5acfa320..c9796e3bc34bb 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceStaticTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ReplaceStaticTests.java @@ -120,7 +120,7 @@ private static Page row(List values) { } private static FieldAttribute field(String name, DataType type) { - return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true)); + return new FieldAttribute(Source.synthetic(name), name, new EsField(name, type, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } private DriverContext driverContext() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithStaticTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithStaticTests.java index 105ce6a9e4142..9c206908d58c5 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithStaticTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/StartsWithStaticTests.java @@ -33,8 +33,16 @@ public void testLuceneQuery_AllLiterals_NonTranslatable() { public void testLuceneQuery_NonFoldablePrefix_NonTranslatable() { var function = new StartsWith( Source.EMPTY, - new FieldAttribute(Source.EMPTY, "field", new EsField("field", DataType.KEYWORD, Map.of(), true)), - new FieldAttribute(Source.EMPTY, "field", new EsField("prefix", DataType.KEYWORD, Map.of(), true)) + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("field", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ), + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("prefix", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ) ); assertThat(function.translatable(LucenePushdownPredicates.DEFAULT), equalTo(TranslationAware.Translatable.NO)); @@ -43,7 +51,11 @@ public void testLuceneQuery_NonFoldablePrefix_NonTranslatable() { public void testLuceneQuery_NonFoldablePrefix_Translatable() { var function = new StartsWith( Source.EMPTY, - new FieldAttribute(Source.EMPTY, "field", new EsField("prefix", DataType.KEYWORD, Map.of(), true)), + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("prefix", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ), Literal.keyword(Source.EMPTY, "a*b?c\\") ); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InStaticTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InStaticTests.java index b2fa9f4221769..48bf4f6dbb6fc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InStaticTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InStaticTests.java @@ -59,7 +59,11 @@ public void testHandleNullsOnRightValue() { public void testConvertedNull() { In in = new In( EMPTY, - new FieldAttribute(Source.EMPTY, "field", new EsField("suffix", DataType.KEYWORD, Map.of(), true)), + new FieldAttribute( + Source.EMPTY, + "field", + new EsField("suffix", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ), Arrays.asList(ONE, new Literal(Source.EMPTY, null, randomFrom(DataType.types())), THREE) ); var query = in.asQuery(LucenePushdownPredicates.DEFAULT, TranslatorHandler.TRANSLATOR_HANDLER); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/index/EsIndexSerializationTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/index/EsIndexSerializationTests.java index 05a08cc1402c1..22cdef70182cc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/index/EsIndexSerializationTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/index/EsIndexSerializationTests.java @@ -115,11 +115,11 @@ public static EsIndex indexWithManyConflicts(boolean withParent) { } for (int i = 0; i < nonConflictingCount; i++) { String name = String.format(Locale.ROOT, "blah.blah.blah.blah.blah.blah.nonconflict.name%04d", i); - fields.put(name, new EsField(name, DataType.KEYWORD, Map.of(), true)); + fields.put(name, new EsField(name, DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } if (withParent) { - EsField parent = new EsField("parent", DataType.OBJECT, Map.copyOf(fields), false); + EsField parent = new EsField("parent", DataType.OBJECT, Map.copyOf(fields), false, EsField.TimeSeriesFieldType.NONE); fields.put("parent", parent); } @@ -199,7 +199,7 @@ private static EsField fieldWithRecursiveChildren(int depth, int childrenPerLeve if (depth == 1) { for (int i = 0; i < childrenPerLevel; i++) { childName = "leaf" + i; - children.put(childName, new EsField(childName, DataType.KEYWORD, Map.of(), true)); + children.put(childName, new EsField(childName, DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } } else { for (int i = 0; i < childrenPerLevel; i++) { @@ -208,7 +208,7 @@ private static EsField fieldWithRecursiveChildren(int depth, int childrenPerLeve } } - return new EsField(name, DataType.OBJECT, children, false); + return new EsField(name, DataType.OBJECT, children, false, EsField.TimeSeriesFieldType.NONE); } /** diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/AbstractLogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/AbstractLogicalPlanOptimizerTests.java index 53efa26043226..ee3ea8112e738 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/AbstractLogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/AbstractLogicalPlanOptimizerTests.java @@ -151,7 +151,10 @@ public static void init() { ); var multiIndexMapping = loadMapping("mapping-basic.json"); - multiIndexMapping.put("partial_type_keyword", new EsField("partial_type_keyword", KEYWORD, emptyMap(), true)); + multiIndexMapping.put( + "partial_type_keyword", + new EsField("partial_type_keyword", KEYWORD, emptyMap(), true, EsField.TimeSeriesFieldType.NONE) + ); var multiIndex = IndexResolution.valid( new EsIndex( "multi_index", diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java index 5c26c4eb0e1aa..bc19252d35b0d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalLogicalPlanOptimizerTests.java @@ -350,7 +350,11 @@ public void testMissingFieldInNewCommand() { new MockFieldAttributeCommand( EMPTY, new Row(EMPTY, List.of()), - new FieldAttribute(EMPTY, "last_name", new EsField("last_name", DataType.KEYWORD, Map.of(), true)) + new FieldAttribute( + EMPTY, + "last_name", + new EsField("last_name", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ) ), testStats ); @@ -504,7 +508,7 @@ public void testSparseDocument() throws Exception { Map large = Maps.newLinkedHashMapWithExpectedSize(size); for (int i = 0; i < size; i++) { var name = String.format(Locale.ROOT, "field%03d", i); - large.put(name, new EsField(name, DataType.INTEGER, emptyMap(), true, false)); + large.put(name, new EsField(name, DataType.INTEGER, emptyMap(), true, false, EsField.TimeSeriesFieldType.NONE)); } SearchStats searchStats = statsForExistingField("field000", "field001", "field002", "field003", "field004"); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java index cd6371e4d4d5e..465226e8b5bf9 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java @@ -224,8 +224,8 @@ public void init() { List.of("a", "b"), Map.of("", "idx"), Map.ofEntries( - Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true)), - Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true)) + Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)), + Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ) ); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java index 5ff6687145798..9e255d9322e73 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/PhysicalPlanOptimizerTests.java @@ -411,8 +411,8 @@ private static EnrichResolution setupEnrichResolution() { List.of("a", "b"), Map.of("", "idx"), Map.ofEntries( - Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true)), - Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true)) + Map.entry("a", new EsField("a", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)), + Map.entry("b", new EsField("b", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ) ); @@ -425,10 +425,13 @@ private static EnrichResolution setupEnrichResolution() { List.of("city", "airport", "region", "city_boundary"), Map.of("", "airport_city_boundaries"), Map.ofEntries( - Map.entry("city", new EsField("city", DataType.KEYWORD, Map.of(), true)), - Map.entry("airport", new EsField("airport", DataType.TEXT, Map.of(), false)), - Map.entry("region", new EsField("region", DataType.TEXT, Map.of(), false)), - Map.entry("city_boundary", new EsField("city_boundary", DataType.GEO_SHAPE, Map.of(), false)) + Map.entry("city", new EsField("city", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)), + Map.entry("airport", new EsField("airport", DataType.TEXT, Map.of(), false, EsField.TimeSeriesFieldType.NONE)), + Map.entry("region", new EsField("region", DataType.TEXT, Map.of(), false, EsField.TimeSeriesFieldType.NONE)), + Map.entry( + "city_boundary", + new EsField("city_boundary", DataType.GEO_SHAPE, Map.of(), false, EsField.TimeSeriesFieldType.NONE) + ) ) ) ); @@ -440,7 +443,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("department"), Map.of("", ".enrich-departments-1", "cluster_1", ".enrich-departments-2"), - Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true)) + Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); enrichResolution.addResolvedPolicy( @@ -451,7 +454,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("department"), Map.of("", ".enrich-departments-3"), - Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true)) + Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); enrichResolution.addResolvedPolicy( @@ -462,7 +465,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("department"), Map.of("cluster_1", ".enrich-departments-2"), - Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true)) + Map.of("department", new EsField("department", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); enrichResolution.addResolvedPolicy( @@ -473,7 +476,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("supervisor"), Map.of("", ".enrich-supervisors-a", "cluster_1", ".enrich-supervisors-b"), - Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true)) + Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); enrichResolution.addResolvedPolicy( @@ -484,7 +487,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("supervisor"), Map.of("", ".enrich-supervisors-c"), - Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true)) + Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); enrichResolution.addResolvedPolicy( @@ -495,7 +498,7 @@ private static EnrichResolution setupEnrichResolution() { EnrichPolicy.MATCH_TYPE, List.of("supervisor"), Map.of("cluster_1", ".enrich-supervisors-b"), - Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true)) + Map.of("supervisor", new EsField("supervisor", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)) ) ); return enrichResolution; @@ -2994,9 +2997,9 @@ public void testProjectAwayColumns() { "test", Map.of( "some_field1", - new EsField("some_field1", DataType.KEYWORD, Map.of(), true), + new EsField("some_field1", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE), "some_field2", - new EsField("some_field2", DataType.KEYWORD, Map.of(), true) + new EsField("some_field2", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE) ) ), IndexMode.STANDARD @@ -7871,7 +7874,7 @@ private Map fields(Collection fieldNames) { Map fields = new HashMap<>(); for (String fieldName : fieldNames) { - fields.put(fieldName, new EsField(fieldName, DataType.KEYWORD, Map.of(), false)); + fields.put(fieldName, new EsField(fieldName, DataType.KEYWORD, Map.of(), false, EsField.TimeSeriesFieldType.NONE)); } return fields; diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushTopNToSourceTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushTopNToSourceTests.java index 00f8dfb9aaacc..15fc4d75f4f13 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushTopNToSourceTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushTopNToSourceTests.java @@ -505,7 +505,10 @@ private static void addSortableFieldAttributes(Map field } private static void addFieldAttribute(Map fields, String name, DataType type) { - fields.put(name, new FieldAttribute(Source.EMPTY, name, new EsField(name, type, new HashMap<>(), true))); + fields.put( + name, + new FieldAttribute(Source.EMPTY, name, new EsField(name, type, new HashMap<>(), true, EsField.TimeSeriesFieldType.NONE)) + ); } static TestPhysicalPlanBuilder from(String index) { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java index 3ea56d6d15abd..21bbedd0313f6 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/EvalMapperTests.java @@ -161,7 +161,11 @@ public void testExpressionSerialization() { } private static FieldAttribute field(String name, DataType type) { - return new FieldAttribute(Source.EMPTY, name, new EsField(name, type, Collections.emptyMap(), false)); + return new FieldAttribute( + Source.EMPTY, + name, + new EsField(name, type, Collections.emptyMap(), false, EsField.TimeSeriesFieldType.NONE) + ); } static DriverContext driverContext() { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java index b56f4a3a4898b..f820ee636f630 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlannerTests.java @@ -150,7 +150,11 @@ public void testLuceneSourceOperatorHugeRowSize() throws IOException { public void testLuceneTopNSourceOperator() throws IOException { int estimatedRowSize = randomEstimatedRowSize(estimatedRowSizeIsHuge); - FieldAttribute sortField = new FieldAttribute(Source.EMPTY, "field", new EsField("field", DataType.INTEGER, Map.of(), true)); + FieldAttribute sortField = new FieldAttribute( + Source.EMPTY, + "field", + new EsField("field", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); EsQueryExec.FieldSort sort = new EsQueryExec.FieldSort(sortField, Order.OrderDirection.ASC, Order.NullsPosition.LAST); Literal limit = new Literal(Source.EMPTY, 10, DataType.INTEGER); LocalExecutionPlanner.LocalExecutionPlan plan = planner().plan( @@ -177,7 +181,11 @@ public void testLuceneTopNSourceOperator() throws IOException { public void testLuceneTopNSourceOperatorDistanceSort() throws IOException { int estimatedRowSize = randomEstimatedRowSize(estimatedRowSizeIsHuge); - FieldAttribute sortField = new FieldAttribute(Source.EMPTY, "point", new EsField("point", DataType.GEO_POINT, Map.of(), true)); + FieldAttribute sortField = new FieldAttribute( + Source.EMPTY, + "point", + new EsField("point", DataType.GEO_POINT, Map.of(), true, EsField.TimeSeriesFieldType.NONE) + ); EsQueryExec.GeoDistanceSort sort = new EsQueryExec.GeoDistanceSort(sortField, Order.OrderDirection.ASC, 1, -1); Literal limit = new Literal(Source.EMPTY, 10, DataType.INTEGER); LocalExecutionPlanner.LocalExecutionPlan plan = planner().plan( diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/ClusterRequestTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/ClusterRequestTests.java index cba3c3a7556e0..17d5407c3c468 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/ClusterRequestTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/plugin/ClusterRequestTests.java @@ -165,7 +165,6 @@ public void testFallbackIndicesOptions() throws Exception { assertThat(cloned.clusterAlias(), equalTo(request.clusterAlias())); assertThat(cloned.sessionId(), equalTo(request.sessionId())); RemoteClusterPlan plan = cloned.remoteClusterPlan(); - assertThat(plan.plan(), equalTo(request.remoteClusterPlan().plan())); assertThat(plan.targetIndices(), equalTo(request.remoteClusterPlan().targetIndices())); OriginalIndices originalIndices = plan.originalIndices(); assertThat(originalIndices.indices(), equalTo(request.remoteClusterPlan().originalIndices().indices())); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/querydsl/query/MatchQueryTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/querydsl/query/MatchQueryTests.java index bf3f3baa0b634..2df727f9bc000 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/querydsl/query/MatchQueryTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/querydsl/query/MatchQueryTests.java @@ -65,14 +65,14 @@ public void testQueryBuilding() { private static MatchQueryBuilder getBuilder(Map options) { final Source source = new Source(1, 1, StringUtils.EMPTY); - FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", KEYWORD, emptyMap(), true)); + FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", KEYWORD, emptyMap(), true, EsField.TimeSeriesFieldType.NONE)); final MatchQuery mmq = new MatchQuery(source, "eggplant", "foo", options); return (MatchQueryBuilder) mmq.asBuilder(); } public void testToString() { final Source source = new Source(1, 1, StringUtils.EMPTY); - FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", KEYWORD, emptyMap(), true)); + FieldAttribute fa = new FieldAttribute(EMPTY, "a", new EsField("af", KEYWORD, emptyMap(), true, EsField.TimeSeriesFieldType.NONE)); final MatchQuery mmq = new MatchQuery(source, "eggplant", "foo"); assertEquals("MatchQuery@1:2[eggplant:foo]", mmq.toString()); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java index 716ae56d85d7a..e24e6adc2becb 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/tree/EsqlNodeSubclassTests.java @@ -733,7 +733,11 @@ static EsRelation randomEsRelation() { } static FieldAttribute field(String name, DataType type) { - return new FieldAttribute(Source.EMPTY, name, new EsField(name, type, Collections.emptyMap(), false)); + return new FieldAttribute( + Source.EMPTY, + name, + new EsField(name, type, Collections.emptyMap(), false, EsField.TimeSeriesFieldType.NONE) + ); } public static Set> subclassesOf(Class clazz) throws IOException { diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/DateEsFieldTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/DateEsFieldTests.java index bf0494d5fd043..a7cf5b499e7ee 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/DateEsFieldTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/DateEsFieldTests.java @@ -14,7 +14,12 @@ public class DateEsFieldTests extends AbstractEsFieldTypeTests { static DateEsField randomDateEsField(int maxPropertiesDepth) { - return DateEsField.dateEsField(randomAlphaOfLength(5), randomProperties(maxPropertiesDepth), randomBoolean()); + return DateEsField.dateEsField( + randomAlphaOfLength(5), + randomProperties(maxPropertiesDepth), + randomBoolean(), + randomFrom(EsField.TimeSeriesFieldType.values()) + ); } @Override @@ -27,12 +32,14 @@ protected DateEsField mutate(DateEsField instance) { String name = instance.getName(); Map properties = instance.getProperties(); boolean aggregatable = instance.isAggregatable(); - switch (between(0, 2)) { + EsField.TimeSeriesFieldType tsType = instance.getTimeSeriesFieldType(); + switch (between(0, 3)) { case 0 -> name = randomAlphaOfLength(name.length() + 1); case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4)); case 2 -> aggregatable = false == aggregatable; + case 3 -> tsType = randomValueOtherThan(tsType, () -> randomFrom(EsField.TimeSeriesFieldType.values())); default -> throw new IllegalArgumentException(); } - return DateEsField.dateEsField(name, properties, aggregatable); + return DateEsField.dateEsField(name, properties, aggregatable, tsType); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsFieldTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsFieldTests.java index 18e0405a65892..c29cbde8a7877 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsFieldTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/EsFieldTests.java @@ -19,7 +19,8 @@ public static EsField randomEsField(int maxPropertiesDepth) { Map properties = randomProperties(maxPropertiesDepth); boolean aggregatable = randomBoolean(); boolean isAlias = randomBoolean(); - return new EsField(name, esDataType, properties, aggregatable, isAlias); + EsField.TimeSeriesFieldType tsType = randomFrom(EsField.TimeSeriesFieldType.values()); + return new EsField(name, esDataType, properties, aggregatable, isAlias, tsType); } @Override @@ -34,14 +35,16 @@ protected EsField mutate(EsField instance) { Map properties = instance.getProperties(); boolean aggregatable = instance.isAggregatable(); boolean isAlias = instance.isAlias(); - switch (between(0, 4)) { + EsField.TimeSeriesFieldType tsType = instance.getTimeSeriesFieldType(); + switch (between(0, 5)) { case 0 -> name = randomAlphaOfLength(name.length() + 1); case 1 -> esDataType = randomValueOtherThan(esDataType, () -> randomFrom(DataType.types())); case 2 -> properties = randomValueOtherThan(properties, () -> randomProperties(4)); case 3 -> aggregatable = false == aggregatable; case 4 -> isAlias = false == isAlias; + case 5 -> tsType = randomValueOtherThan(tsType, () -> randomFrom(EsField.TimeSeriesFieldType.values())); default -> throw new IllegalArgumentException(); } - return new EsField(name, esDataType, properties, aggregatable, isAlias); + return new EsField(name, esDataType, properties, aggregatable, isAlias, tsType); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/KeywordEsFieldTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/KeywordEsFieldTests.java index ef04f0e27c096..9441b2e896f10 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/KeywordEsFieldTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/KeywordEsFieldTests.java @@ -21,7 +21,8 @@ static KeywordEsField randomKeywordEsField(int maxPropertiesDepth) { int precision = randomInt(); boolean normalized = randomBoolean(); boolean isAlias = randomBoolean(); - return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias); + EsField.TimeSeriesFieldType tsType = randomFrom(EsField.TimeSeriesFieldType.values()); + return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias, tsType); } @Override @@ -37,15 +38,17 @@ protected KeywordEsField mutate(KeywordEsField instance) { int precision = instance.getPrecision(); boolean normalized = instance.getNormalized(); boolean isAlias = instance.isAlias(); - switch (between(0, 5)) { + EsField.TimeSeriesFieldType tsType = instance.getTimeSeriesFieldType(); + switch (between(0, 6)) { case 0 -> name = randomAlphaOfLength(name.length() + 1); case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4)); case 2 -> hasDocValues = false == hasDocValues; case 3 -> precision = randomValueOtherThan(precision, ESTestCase::randomInt); case 4 -> normalized = false == normalized; case 5 -> isAlias = false == isAlias; + case 6 -> tsType = randomValueOtherThan(tsType, () -> randomFrom(EsField.TimeSeriesFieldType.values())); default -> throw new IllegalArgumentException(); } - return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias); + return new KeywordEsField(name, properties, hasDocValues, precision, normalized, isAlias, tsType); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/MultiTypeEsFieldTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/MultiTypeEsFieldTests.java index 154605a199d22..f64d2417aff1d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/MultiTypeEsFieldTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/MultiTypeEsFieldTests.java @@ -72,7 +72,8 @@ protected MultiTypeEsField createTestInstance() { DataType dataType = randomFrom(types()); DataType toType = toString ? DataType.KEYWORD : dataType; Map indexToConvertExpressions = randomConvertExpressions(name, toString, dataType); - return new MultiTypeEsField(name, toType, false, indexToConvertExpressions); + EsField.TimeSeriesFieldType tsType = randomFrom(EsField.TimeSeriesFieldType.values()); + return new MultiTypeEsField(name, toType, false, indexToConvertExpressions, tsType); } @Override @@ -80,13 +81,15 @@ protected MultiTypeEsField mutateInstance(MultiTypeEsField instance) throws IOEx String name = instance.getName(); DataType dataType = instance.getDataType(); Map indexToConvertExpressions = instance.getIndexToConversionExpressions(); - switch (between(0, 2)) { + EsField.TimeSeriesFieldType tsType = instance.getTimeSeriesFieldType(); + switch (between(0, 3)) { case 0 -> name = randomAlphaOfLength(name.length() + 1); case 1 -> dataType = randomValueOtherThan(dataType, () -> randomFrom(DataType.types())); case 2 -> indexToConvertExpressions = mutateConvertExpressions(name, dataType, indexToConvertExpressions); + case 3 -> tsType = randomValueOtherThan(tsType, () -> randomFrom(EsField.TimeSeriesFieldType.values())); default -> throw new IllegalArgumentException(); } - return new MultiTypeEsField(name, dataType, false, indexToConvertExpressions); + return new MultiTypeEsField(name, dataType, false, indexToConvertExpressions, tsType); } @Override @@ -169,6 +172,6 @@ private static Expression testConvertExpression(String name, DataType fromType, } private static FieldAttribute fieldAttribute(String name, DataType dataType) { - return new FieldAttribute(Source.EMPTY, name, new EsField(name, dataType, Map.of(), true)); + return new FieldAttribute(Source.EMPTY, name, new EsField(name, dataType, Map.of(), true, EsField.TimeSeriesFieldType.NONE)); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/TextEsFieldTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/TextEsFieldTests.java index 9af3b7376f2b2..cc2270d94ddfd 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/TextEsFieldTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/type/TextEsFieldTests.java @@ -18,7 +18,8 @@ static TextEsField randomTextEsField(int maxPropertiesDepth) { Map properties = randomProperties(maxPropertiesDepth); boolean hasDocValues = randomBoolean(); boolean isAlias = randomBoolean(); - return new TextEsField(name, properties, hasDocValues, isAlias); + EsField.TimeSeriesFieldType tsType = randomFrom(EsField.TimeSeriesFieldType.values()); + return new TextEsField(name, properties, hasDocValues, isAlias, tsType); } @Override @@ -32,13 +33,15 @@ protected TextEsField mutate(TextEsField instance) { Map properties = instance.getProperties(); boolean hasDocValues = instance.isAggregatable(); boolean isAlias = instance.isAlias(); - switch (between(0, 3)) { + EsField.TimeSeriesFieldType tsType = instance.getTimeSeriesFieldType(); + switch (between(0, 4)) { case 0 -> name = randomAlphaOfLength(name.length() + 1); case 1 -> properties = randomValueOtherThan(properties, () -> randomProperties(4)); case 2 -> hasDocValues = false == hasDocValues; case 3 -> isAlias = false == isAlias; + case 4 -> tsType = randomValueOtherThan(tsType, () -> randomFrom(EsField.TimeSeriesFieldType.values())); default -> throw new IllegalArgumentException(); } - return new TextEsField(name, properties, hasDocValues, isAlias); + return new TextEsField(name, properties, hasDocValues, isAlias, tsType); } }