Skip to content

Commit b53d5f7

Browse files
authored
Esql dimension aware attributes (#131463)
Relates to #128621 This PR adds more building blocks for working with dimensions in ESQL. Specifically, it makes the TSDB metadata available on attributes.
1 parent 24eb3db commit b53d5f7

File tree

48 files changed

+313
-132
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+313
-132
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ public void setup() {
9292
var fields = 10_000;
9393
var mapping = LinkedHashMap.<String, EsField>newLinkedHashMap(fields);
9494
for (int i = 0; i < fields; i++) {
95-
mapping.put("field" + i, new EsField("field-" + i, TEXT, emptyMap(), true));
95+
// We're creating a standard index, so none of these fields should be marked as dimensions.
96+
mapping.put("field" + i, new EsField("field-" + i, TEXT, emptyMap(), true, EsField.TimeSeriesFieldType.NONE));
9697
}
9798

9899
var esIndex = new EsIndex("test", mapping, Map.of("test", IndexMode.STANDARD));

benchmarks/src/main/java/org/elasticsearch/benchmark/compute/operator/EvalBenchmark.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) {
212212
FieldAttribute timestamp = new FieldAttribute(
213213
Source.EMPTY,
214214
"timestamp",
215-
new EsField("timestamp", DataType.DATETIME, Map.of(), true)
215+
new EsField("timestamp", DataType.DATETIME, Map.of(), true, EsField.TimeSeriesFieldType.NONE)
216216
);
217217
yield EvalMapper.toEvaluator(
218218
FOLD_CONTEXT,
@@ -321,19 +321,35 @@ private static EvalOperator.ExpressionEvaluator evaluator(String operation) {
321321
}
322322

323323
private static FieldAttribute longField() {
324-
return new FieldAttribute(Source.EMPTY, "long", new EsField("long", DataType.LONG, Map.of(), true));
324+
return new FieldAttribute(
325+
Source.EMPTY,
326+
"long",
327+
new EsField("long", DataType.LONG, Map.of(), true, EsField.TimeSeriesFieldType.NONE)
328+
);
325329
}
326330

327331
private static FieldAttribute doubleField() {
328-
return new FieldAttribute(Source.EMPTY, "double", new EsField("double", DataType.DOUBLE, Map.of(), true));
332+
return new FieldAttribute(
333+
Source.EMPTY,
334+
"double",
335+
new EsField("double", DataType.DOUBLE, Map.of(), true, EsField.TimeSeriesFieldType.NONE)
336+
);
329337
}
330338

331339
private static FieldAttribute intField() {
332-
return new FieldAttribute(Source.EMPTY, "int", new EsField("int", DataType.INTEGER, Map.of(), true));
340+
return new FieldAttribute(
341+
Source.EMPTY,
342+
"int",
343+
new EsField("int", DataType.INTEGER, Map.of(), true, EsField.TimeSeriesFieldType.NONE)
344+
);
333345
}
334346

335347
private static FieldAttribute keywordField() {
336-
return new FieldAttribute(Source.EMPTY, "keyword", new EsField("keyword", DataType.KEYWORD, Map.of(), true));
348+
return new FieldAttribute(
349+
Source.EMPTY,
350+
"keyword",
351+
new EsField("keyword", DataType.KEYWORD, Map.of(), true, EsField.TimeSeriesFieldType.NONE)
352+
);
337353
}
338354

339355
private static Configuration configuration() {

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,9 @@ public static boolean dataTypeEquals(List<Attribute> left, List<Attribute> right
149149
}
150150
return true;
151151
}
152+
153+
/**
154+
* @return true if the attribute represents a TSDB dimension type
155+
*/
156+
public abstract boolean isDimension();
152157
}

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/EmptyAttribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ protected String label() {
4444
return "e";
4545
}
4646

47+
@Override
48+
public boolean isDimension() {
49+
return false;
50+
}
51+
4752
@Override
4853
public boolean resolved() {
4954
return true;

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/FieldAttribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ protected String label() {
216216
return "f";
217217
}
218218

219+
@Override
220+
public boolean isDimension() {
221+
return field.getTimeSeriesFieldType() == EsField.TimeSeriesFieldType.DIMENSION;
222+
}
223+
219224
public EsField field() {
220225
return field;
221226
}

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/MetadataAttribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ protected String label() {
120120
return "m";
121121
}
122122

123+
@Override
124+
public boolean isDimension() {
125+
return false;
126+
}
127+
123128
@Override
124129
protected NodeInfo<? extends Expression> info() {
125130
return NodeInfo.create(this, MetadataAttribute::new, name(), dataType(), nullable(), id(), synthetic(), searchable);

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/ReferenceAttribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,9 @@ protected NodeInfo<ReferenceAttribute> info() {
117117
protected String label() {
118118
return "r";
119119
}
120+
121+
@Override
122+
public boolean isDimension() {
123+
return false;
124+
}
120125
}

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/UnresolvedAttribute.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,11 @@ protected String label() {
107107
return UNRESOLVED_PREFIX;
108108
}
109109

110+
@Override
111+
public boolean isDimension() {
112+
return false;
113+
}
114+
110115
@Override
111116
public String nodeString() {
112117
return toString();

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
*/
2121
public class DateEsField extends EsField {
2222

23-
public static DateEsField dateEsField(String name, Map<String, EsField> properties, boolean hasDocValues) {
24-
return new DateEsField(name, DataType.DATETIME, properties, hasDocValues, TimeSeriesFieldType.UNKNOWN);
23+
public static DateEsField dateEsField(String name, Map<String, EsField> properties, boolean hasDocValues, TimeSeriesFieldType tsType) {
24+
return new DateEsField(name, DataType.DATETIME, properties, hasDocValues, tsType);
2525
}
2626

2727
private DateEsField(

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

Lines changed: 51 additions & 15 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,19 @@ 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+
assert capabilities.metricType() == null;
91+
return DIMENSION;
92+
}
93+
if (capabilities.metricType() != null) {
94+
return METRIC;
95+
}
96+
return NONE;
97+
}
98+
99+
public abstract TimeSeriesFieldType merge(TimeSeriesFieldType other);
60100
}
61101

62102
private static Map<String, Reader<? extends EsField>> readers = Map.ofEntries(
@@ -83,13 +123,8 @@ public static Reader<? extends EsField> getReader(String name) {
83123
private final Map<String, EsField> properties;
84124
private final String name;
85125
private final boolean isAlias;
86-
// Because the subclasses all reimplement serialization, this needs to be writeable from subclass constructors
87126
private final TimeSeriesFieldType timeSeriesFieldType;
88127

89-
public EsField(String name, DataType esDataType, Map<String, EsField> properties, boolean aggregatable) {
90-
this(name, esDataType, properties, aggregatable, false, TimeSeriesFieldType.UNKNOWN);
91-
}
92-
93128
public EsField(
94129
String name,
95130
DataType esDataType,
@@ -100,10 +135,6 @@ public EsField(
100135
this(name, esDataType, properties, aggregatable, false, timeSeriesFieldType);
101136
}
102137

103-
public EsField(String name, DataType esDataType, Map<String, EsField> properties, boolean aggregatable, boolean isAlias) {
104-
this(name, esDataType, properties, aggregatable, isAlias, TimeSeriesFieldType.UNKNOWN);
105-
}
106-
107138
public EsField(
108139
String name,
109140
DataType esDataType,
@@ -247,6 +278,10 @@ public Exact getExactInfo() {
247278
return Exact.EXACT_FIELD;
248279
}
249280

281+
public TimeSeriesFieldType getTimeSeriesFieldType() {
282+
return timeSeriesFieldType;
283+
}
284+
250285
@Override
251286
public String toString() {
252287
return name + "@" + esDataType.typeName() + "=" + properties;
@@ -265,12 +300,13 @@ public boolean equals(Object o) {
265300
&& isAlias == field.isAlias
266301
&& esDataType == field.esDataType
267302
&& Objects.equals(name, field.name)
268-
&& Objects.equals(properties, field.properties);
303+
&& Objects.equals(properties, field.properties)
304+
&& Objects.equals(timeSeriesFieldType, field.timeSeriesFieldType);
269305
}
270306

271307
@Override
272308
public int hashCode() {
273-
return Objects.hash(esDataType, aggregatable, properties, name, isAlias);
309+
return Objects.hash(esDataType, aggregatable, properties, name, isAlias, timeSeriesFieldType);
274310
}
275311

276312
public static final class Exact {

0 commit comments

Comments
 (0)