Skip to content

Commit 1c609ac

Browse files
Support flattened fields as time series dimension fields (#95273)
Normally dimension fields are identified by means of a boolean parameter at mapping time, time_series_dimension. Flattened fields do not have mappings, other than identifying the top level field as a flattened field type. Moreover a boolean is not enough to identify the top-level field as a dimension since we would like users to be able to specify a subset of the fields in the flattened field to be dimensions (not necessarily all of them). For this reason we introduce a new mapping parameter, time_series_dimensions, which lists the fields, in any order, in the flattened field that the user wants as dimensions. Field names must not include the root field name and their name is the relative path from the root down to the leaf field name. We require flattened fields to be indexed, to have doc values and disallow usage of the ignore_above parameter together with time_series_dimensions.
1 parent 29091e8 commit 1c609ac

File tree

16 files changed

+793
-72
lines changed

16 files changed

+793
-72
lines changed

docs/changelog/95273.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 95273
2+
summary: Support flattened fields as time series dimension fields
3+
area: TSDB
4+
type: feature
5+
issues: []

modules/data-streams/src/internalClusterTest/java/org/elasticsearch/datastreams/TSDBIndexingIT.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ public void testInvalidTsdbTemplatesNoTimeSeriesDimensionAttribute() throws Exce
249249
assertThat(
250250
e.getCause().getCause().getMessage(),
251251
equalTo(
252-
"All fields that match routing_path must be keywords with [time_series_dimension: true] and "
253-
+ "without the [script] parameter. [metricset] was not [time_series_dimension: true]."
252+
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
253+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] and "
254+
+ "without the [script] parameter. [metricset] was not a dimension."
254255
)
255256
);
256257
}
@@ -316,7 +317,8 @@ public void testInvalidTsdbTemplatesNoKeywordFieldType() throws Exception {
316317
assertThat(
317318
e.getCause().getCause().getMessage(),
318319
equalTo(
319-
"All fields that match routing_path must be keywords with [time_series_dimension: true] and "
320+
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
321+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] and "
320322
+ "without the [script] parameter. [metricset] was [long]."
321323
)
322324
);

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/20_mapping.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -114,11 +114,11 @@ top level wildcard dim object:
114114
---
115115
exact match object type:
116116
- skip:
117-
version: " - 8.1.99"
118-
reason: routing_path object type check improve in 8.2.0
117+
version: " - 8.7.99"
118+
reason: routing_path error message updated in 8.8.0
119119

120120
- do:
121-
catch: '/All fields that match routing_path must be keywords with \[time_series_dimension: true\] and without the \[script\] parameter. \[dim\] was \[object\]./'
121+
catch: '/All fields that match routing_path must be keywords with \[time_series_dimension: true\] or flattened fields with a list of dimensions in \[time_series_dimensions\] and without the \[script\] parameter. \[dim\] was \[object\]./'
122122
indices.create:
123123
index: tsdb_index
124124
body:
@@ -147,11 +147,11 @@ exact match object type:
147147
---
148148
non keyword matches routing_path:
149149
- skip:
150-
version: " - 8.0.99"
151-
reason: introduced in 8.1.0
150+
version: " - 8.7.99"
151+
reason: routing_path error message updated in 8.8.0
152152

153153
- do:
154-
catch: '/All fields that match routing_path must be keywords with \[time_series_dimension: true\] and without the \[script\] parameter. \[@timestamp\] was \[date\]./'
154+
catch: '/All fields that match routing_path must be keywords with \[time_series_dimension: true\] or flattened fields with a list of dimensions in \[time_series_dimensions\] and without the \[script\] parameter. \[@timestamp\] was \[date\]./'
155155
indices.create:
156156
index: test_index
157157
body:
@@ -234,8 +234,8 @@ runtime field matching routing path:
234234
---
235235
"dynamic: runtime matches routing_path":
236236
- skip:
237-
version: " - 8.1.99"
238-
reason: tsdb indexing changed in 8.2.0
237+
version: " - 8.7.99"
238+
reason: routing_path error message updated in 8.8.0
239239

240240
- do:
241241
indices.create:
@@ -266,7 +266,7 @@ runtime field matching routing path:
266266
body:
267267
- '{"index": {}}'
268268
- '{"@timestamp": "2021-04-28T18:50:04.467Z", "dim_kw": "dim", "dim": {"foo": "a"}}'
269-
- match: {items.0.index.error.reason: "All fields that match routing_path must be keywords with [time_series_dimension: true] and without the [script] parameter. [dim.foo] was a runtime [keyword]."}
269+
- match: {items.0.index.error.reason: "All fields that match routing_path must be keywords with [time_series_dimension: true] or flattened fields with a list of dimensions in [time_series_dimensions] and without the [script] parameter. [dim.foo] was a runtime [keyword]."}
270270

271271
---
272272
"dynamic: false matches routing_path":

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/tsdb/70_dimension_types.yml

Lines changed: 390 additions & 0 deletions
Large diffs are not rendered by default.

server/src/main/java/org/elasticsearch/index/mapper/AbstractScriptFieldType.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,11 @@ protected final LeafFactory leafFactory(SearchExecutionContext context) {
204204
}
205205

206206
@Override
207-
public void validateMatchedRoutingPath() {
207+
public void validateMatchedRoutingPath(final String routingPath) {
208208
throw new IllegalArgumentException(
209-
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
209+
"All fields that match routing_path "
210+
+ "must be keywords with [time_series_dimension: true] "
211+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] "
210212
+ "and without the [script] parameter. ["
211213
+ name()
212214
+ "] was a runtime ["

server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,13 +105,14 @@ public void validate(IndexSettings settings, boolean checkLimits) {
105105
List<String> routingPaths = settings.getIndexMetadata().getRoutingPaths();
106106
for (String path : routingPaths) {
107107
for (String match : mappingLookup.getMatchingFieldNames(path)) {
108-
mappingLookup.getFieldType(match).validateMatchedRoutingPath();
108+
mappingLookup.getFieldType(match).validateMatchedRoutingPath(path);
109109
}
110110
for (String objectName : mappingLookup.objectMappers().keySet()) {
111111
// object type is not allowed in the routing paths
112112
if (path.equals(objectName)) {
113113
throw new IllegalArgumentException(
114114
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
115+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] "
115116
+ "and without the [script] parameter. ["
116117
+ objectName
117118
+ "] was [object]."

server/src/main/java/org/elasticsearch/index/mapper/KeywordFieldMapper.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -779,19 +779,22 @@ public boolean isDimension() {
779779
}
780780

781781
@Override
782-
public void validateMatchedRoutingPath() {
782+
public void validateMatchedRoutingPath(final String routingPath) {
783783
if (false == isDimension) {
784784
throw new IllegalArgumentException(
785-
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
786-
+ "and without the [script] parameter. ["
785+
"All fields that match routing_path "
786+
+ "must be keywords with [time_series_dimension: true] "
787+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] and "
788+
+ "without the [script] parameter. ["
787789
+ name()
788-
+ "] was not [time_series_dimension: true]."
790+
+ "] was not a dimension."
789791
);
790792
}
791793
if (scriptValues != null) {
792794
throw new IllegalArgumentException(
793795
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
794-
+ "and without the [script] parameter. ["
796+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] and "
797+
+ "without the [script] parameter. ["
795798
+ name()
796799
+ "] has a [script] parameter."
797800
);

server/src/main/java/org/elasticsearch/index/mapper/MappedFieldType.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@
4646
import java.io.IOException;
4747
import java.time.ZoneId;
4848
import java.util.Collection;
49+
import java.util.Collections;
50+
import java.util.List;
4951
import java.util.Map;
5052
import java.util.Objects;
5153
import java.util.function.Function;
@@ -188,6 +190,15 @@ public boolean isDimension() {
188190
return false;
189191
}
190192

193+
/**
194+
* @return a list of dimension fields. Expected to be used by fields that have
195+
* nested fields or that, in some way, identify a collection of fields by means
196+
* of a top level field (like flattened fields).
197+
*/
198+
public List<String> dimensions() {
199+
return Collections.emptyList();
200+
}
201+
191202
/**
192203
* @return metric type or null if the field is not a metric field
193204
*/
@@ -604,10 +615,12 @@ public TermsEnum getTerms(IndexReader reader, String prefix, boolean caseInsensi
604615
/**
605616
* Validate that this field can be the target of {@link IndexMetadata#INDEX_ROUTING_PATH}.
606617
*/
607-
public void validateMatchedRoutingPath() {
618+
public void validateMatchedRoutingPath(String routingPath) {
608619
throw new IllegalArgumentException(
609-
"All fields that match routing_path must be keywords with [time_series_dimension: true] "
610-
+ "and without the [script] parameter. ["
620+
"All fields that match routing_path "
621+
+ "must be keywords with [time_series_dimension: true] "
622+
+ "or flattened fields with a list of dimensions in [time_series_dimensions] and "
623+
+ "without the [script] parameter. ["
611624
+ name()
612625
+ "] was ["
613626
+ typeName()

0 commit comments

Comments
 (0)