Skip to content

Commit c11fa12

Browse files
committed
begin wiring up the time series types
1 parent 2f9e7d2 commit c11fa12

File tree

3 files changed

+58
-8
lines changed

3 files changed

+58
-8
lines changed

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/type/EsField.java

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.elasticsearch.xpack.esql.core.type;
88

99
import org.elasticsearch.TransportVersions;
10+
import org.elasticsearch.action.fieldcaps.IndexFieldCapabilities;
1011
import org.elasticsearch.common.io.stream.StreamInput;
1112
import org.elasticsearch.common.io.stream.StreamOutput;
1213
import org.elasticsearch.common.io.stream.Writeable;
@@ -31,10 +32,36 @@ public class EsField implements Writeable {
3132
* roles within the ESQL query processing pipeline.
3233
*/
3334
public enum TimeSeriesFieldType implements Writeable {
34-
UNKNOWN(0),
35-
NONE(1),
36-
METRIC(2),
37-
DIMENSION(3);
35+
UNKNOWN(0) {
36+
@Override
37+
public TimeSeriesFieldType merge(TimeSeriesFieldType other) {
38+
return other;
39+
}
40+
},
41+
NONE(1) {
42+
@Override
43+
public TimeSeriesFieldType merge(TimeSeriesFieldType other) {
44+
return other;
45+
}
46+
},
47+
METRIC(2) {
48+
@Override
49+
public TimeSeriesFieldType merge(TimeSeriesFieldType other) {
50+
if (other != DIMENSION) {
51+
return METRIC;
52+
}
53+
throw new IllegalStateException("Time Series Metadata conflict. Cannot merge [" + other + "] with [METRIC].");
54+
}
55+
},
56+
DIMENSION(3) {
57+
@Override
58+
public TimeSeriesFieldType merge(TimeSeriesFieldType other) {
59+
if (other != METRIC) {
60+
return DIMENSION;
61+
}
62+
throw new IllegalStateException("Time Series Metadata conflict. Cannot merge [" + other + "] with [DIMENSION].");
63+
}
64+
};
3865

3966
private final int id;
4067

@@ -57,6 +84,18 @@ public static TimeSeriesFieldType readFromStream(StreamInput in) throws IOExcept
5784
default -> throw new IOException("Unexpected value for TimeSeriesFieldType: " + id);
5885
};
5986
}
87+
88+
public static TimeSeriesFieldType fromIndexFieldCapabilities(IndexFieldCapabilities capabilities) {
89+
if (capabilities.isDimension()) {
90+
return DIMENSION;
91+
}
92+
if (capabilities.metricType() != null) {
93+
return METRIC;
94+
}
95+
return NONE;
96+
}
97+
98+
public abstract TimeSeriesFieldType merge(TimeSeriesFieldType other);
6099
}
61100

62101
private static Map<String, Reader<? extends EsField>> readers = Map.ofEntries(
@@ -83,7 +122,6 @@ public static Reader<? extends EsField> getReader(String name) {
83122
private final Map<String, EsField> properties;
84123
private final String name;
85124
private final boolean isAlias;
86-
// Because the subclasses all reimplement serialization, this needs to be writeable from subclass constructors
87125
private final TimeSeriesFieldType timeSeriesFieldType;
88126

89127
public EsField(String name, DataType esDataType, Map<String, EsField> properties, boolean aggregatable) {
@@ -247,6 +285,10 @@ public Exact getExactInfo() {
247285
return Exact.EXACT_FIELD;
248286
}
249287

288+
public TimeSeriesFieldType getTimeSeriesFieldType() {
289+
return timeSeriesFieldType;
290+
}
291+
250292
@Override
251293
public String toString() {
252294
return name + "@" + esDataType.typeName() + "=" + properties;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Analyzer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ private static void mappingAsAttributes(List<Attribute> list, Source source, Str
294294
// due to a bug also copy the field since the Attribute hierarchy extracts the data type
295295
// directly even if the data type is passed explicitly
296296
if (type != t.getDataType()) {
297-
t = new EsField(t.getName(), type, t.getProperties(), t.isAggregatable(), t.isAlias());
297+
t = new EsField(t.getName(), type, t.getProperties(), t.isAggregatable(), t.isAlias(), t.getTimeSeriesFieldType());
298298
}
299299

300300
FieldAttribute attribute = t instanceof UnsupportedEsField uef

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/session/IndexResolver.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
100100
return IndexResolution.notFound(indexPattern);
101101
}
102102

103+
// For each field name, store a list of the field caps responses from each index
103104
Map<String, List<IndexFieldCapabilities>> fieldsCaps = collectFieldCaps(fieldCapsResponse);
104105

105106
// Build hierarchical fields - it's easier to do it in sorted order so the object fields come first.
@@ -121,7 +122,8 @@ public static IndexResolution mergedMappings(String indexPattern, FieldCapabilit
121122
String parent = name.substring(0, nextDot);
122123
EsField obj = fields.get(parent);
123124
if (obj == null) {
124-
obj = new EsField(parent, OBJECT, new HashMap<>(), false, true);
125+
// Object fields can't be dimensions, so we can safely hard code that here
126+
obj = new EsField(parent, OBJECT, new HashMap<>(), false, true, EsField.TimeSeriesFieldType.NONE);
125127
isAlias = true;
126128
fields.put(parent, obj);
127129
} else if (firstUnsupportedParent == null && obj instanceof UnsupportedEsField unsupportedParent) {
@@ -204,11 +206,17 @@ private static EsField createField(
204206
List<IndexFieldCapabilities> rest = fcs.subList(1, fcs.size());
205207
DataType type = EsqlDataTypeRegistry.INSTANCE.fromEs(first.type(), first.metricType());
206208
boolean aggregatable = first.isAggregatable();
209+
EsField.TimeSeriesFieldType timeSeriesFieldType = EsField.TimeSeriesFieldType.UNKNOWN;
207210
if (rest.isEmpty() == false) {
208211
for (IndexFieldCapabilities fc : rest) {
209212
if (first.metricType() != fc.metricType()) {
210213
return conflictingMetricTypes(name, fullName, fieldCapsResponse);
211214
}
215+
try {
216+
timeSeriesFieldType = timeSeriesFieldType.merge(EsField.TimeSeriesFieldType.fromIndexFieldCapabilities(fc));
217+
} catch (IllegalArgumentException e) {
218+
return new InvalidMappedField(name, e.getMessage());
219+
}
212220
}
213221
for (IndexFieldCapabilities fc : rest) {
214222
if (type != EsqlDataTypeRegistry.INSTANCE.fromEs(fc.type(), fc.metricType())) {
@@ -223,7 +231,7 @@ private static EsField createField(
223231
// TODO I think we only care about unmapped fields if we're aggregating on them. do we even then?
224232

225233
if (type == TEXT) {
226-
return new TextEsField(name, new HashMap<>(), false, isAlias);
234+
return new TextEsField(name, new HashMap<>(), false, isAlias, timeSeriesFieldType);
227235
}
228236
if (type == KEYWORD) {
229237
int length = Short.MAX_VALUE;

0 commit comments

Comments
 (0)