Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce experimental pass-through field type #103648

Merged
merged 30 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3cb55c6
Introduce passthrough field type
kkrik-es Dec 21, 2023
c78ac7d
Update docs/changelog/103648.yaml
kkrik-es Dec 21, 2023
5d75fee
no subobjects
kkrik-es Dec 21, 2023
c963956
Merge remote-tracking branch 'origin/fix/103567' into fix/103567
kkrik-es Dec 21, 2023
2a09fbb
create dimensions dynamically
kkrik-es Dec 22, 2023
95934b0
remove unused method
kkrik-es Dec 22, 2023
81352d1
restore ignoreAbove incompatibility with dimension
kkrik-es Dec 22, 2023
7360729
fix test
kkrik-es Dec 22, 2023
c174890
Merge branch 'main' into fix/103567
kkrik-es Jan 9, 2024
38494bb
refactor, skip aliases on conflict
kkrik-es Jan 9, 2024
9fa98ae
fix branch
kkrik-es Jan 9, 2024
0700310
fix branch
kkrik-es Jan 9, 2024
5e1a996
add tests
kkrik-es Jan 11, 2024
db6afcb
Merge branch 'main' into fix/103567
kkrik-es Jan 11, 2024
6fd96ae
Merge branch 'main' into fix/103567
kkrik-es Jan 11, 2024
1bf0c87
update test
kkrik-es Jan 16, 2024
8204dc5
remove unused variable
kkrik-es Jan 16, 2024
997864a
Merge branch 'main' into fix/103567
kkrik-es Jan 16, 2024
5c125ad
add yaml test for subobject
kkrik-es Jan 16, 2024
698e281
Merge branch 'main' into fix/103567
kkrik-es Jan 17, 2024
7598e9b
minor refactoring
kkrik-es Jan 17, 2024
0e698bd
Merge branch 'main' into fix/103567
kkrik-es Jan 17, 2024
e5f58fe
add unittest for PassThroughObjectMapper
kkrik-es Jan 17, 2024
5bb6f20
Merge branch 'main' into fix/103567
kkrik-es Jan 30, 2024
95e2260
suggested fixes
kkrik-es Jan 30, 2024
25eb507
suggested fixes
kkrik-es Jan 30, 2024
45bb20c
update yaml with warning for duplicate alias
kkrik-es Jan 30, 2024
8a8f3ff
updates from review
kkrik-es Jan 31, 2024
2b78a0c
Merge branch 'main' into fix/103567
kkrik-es Feb 1, 2024
5ade2db
add withoutMappers()
kkrik-es Feb 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/103648.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 103648
summary: Introduce experimental pass-through field type
area: TSDB
type: enhancement
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.elasticsearch.index.mapper.MapperBuilderContext;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.MappingParserContext;
import org.elasticsearch.index.mapper.PassThroughObjectMapper;

import java.io.IOException;
import java.io.UncheckedIOException;
Expand Down Expand Up @@ -152,8 +153,9 @@ private List<String> findRoutingPaths(String indexName, Settings allSettings, Li
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, dummyShards)
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, shardReplicas)
.put(IndexMetadata.SETTING_INDEX_UUID, UUIDs.randomBase64UUID())
.put(IndexSettings.MODE.getKey(), IndexMode.TIME_SERIES)
kkrik-es marked this conversation as resolved.
Show resolved Hide resolved
// Avoid failing because index.routing_path is missing
.put(IndexSettings.MODE.getKey(), IndexMode.STANDARD)
.putList(INDEX_ROUTING_PATH.getKey(), List.of("path"))
felixbarny marked this conversation as resolved.
Show resolved Hide resolved
.build();

tmpIndexMetadata.settings(finalResolvedSettings);
Expand All @@ -164,6 +166,13 @@ private List<String> findRoutingPaths(String indexName, Settings allSettings, Li
for (var fieldMapper : mapperService.documentMapper().mappers().fieldMappers()) {
extractPath(routingPaths, fieldMapper);
}
for (var objectMapper : mapperService.documentMapper().mappers().objectMappers().values()) {
if (objectMapper instanceof PassThroughObjectMapper passThroughObjectMapper) {
if (passThroughObjectMapper.containsDimensions()) {
routingPaths.add(passThroughObjectMapper.fullPath() + ".*");
martijnvg marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
for (var template : mapperService.getAllDynamicTemplates()) {
if (template.pathMatch().isEmpty()) {
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,33 @@ public void testGenerateRoutingPathFromDynamicTemplate_nonKeywordTemplate() thro
assertEquals(2, IndexMetadata.INDEX_ROUTING_PATH.get(result).size());
}

public void testGenerateRoutingPathFromPassThroughObject() throws Exception {
Instant now = Instant.now().truncatedTo(ChronoUnit.SECONDS);
String mapping = """
{
"_doc": {
"properties": {
"labels": {
"type": "passthrough",
"time_series_dimension": true
},
"metrics": {
"type": "passthrough"
},
"another_field": {
"type": "keyword"
}
}
}
}
""";
Settings result = generateTsdbSettings(mapping, now);
assertThat(result.size(), equalTo(3));
assertThat(IndexSettings.TIME_SERIES_START_TIME.get(result), equalTo(now.minusMillis(DEFAULT_LOOK_BACK_TIME.getMillis())));
assertThat(IndexSettings.TIME_SERIES_END_TIME.get(result), equalTo(now.plusMillis(DEFAULT_LOOK_AHEAD_TIME.getMillis())));
assertThat(IndexMetadata.INDEX_ROUTING_PATH.get(result), containsInAnyOrder("labels.*"));
}

private Settings generateTsdbSettings(String mapping, Instant now) throws IOException {
Metadata metadata = Metadata.EMPTY_METADATA;
String dataStreamName = "logs-app1";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,262 @@ index without timestamp with pipeline:
pipeline: my_pipeline
body:
- '{"@timestamp": "wrong_format", "metricset": "pod", "k8s": {"pod": {"name": "cat", "uid":"947e4ced-1786-4e53-9e0c-5c447e959507", "ip": "10.10.55.1", "network": {"tx": 2001818691, "rx": 802133794}}}}'

---
dynamic templates:
- skip:
version: " - 8.12.99"
reason: "Support for dynamic fields was added in 8.13"
- do:
indices.put_index_template:
name: my-dynamic-template
body:
index_patterns: [k9s*]
data_stream: {}
template:
settings:
index:
number_of_shards: 1
mode: time_series
time_series:
start_time: 2023-08-31T13:03:08.138Z

mappings:
properties:
attributes:
type: passthrough
dynamic: true
time_series_dimension: true
dynamic_templates:
- counter_metric:
mapping:
type: integer
time_series_metric: counter

- do:
bulk:
index: k9s
felixbarny marked this conversation as resolved.
Show resolved Hide resolved
refresh: true
body:
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:08.138Z","data": "10", "attributes.dim": "A", "attributes.another.dim": "C" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:09.138Z","data": "20", "attributes.dim": "A", "attributes.another.dim": "C" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:10.138Z","data": "30", "attributes.dim": "B", "attributes.another.dim": "D" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:10.238Z","data": "40", "attributes.dim": "B", "attributes.another.dim": "D" }'

- do:
search:
index: k9s
body:
size: 0

- match: { hits.total.value: 4 }

- do:
search:
index: k9s
body:
size: 0
aggs:
filterA:
filter:
term:
dim: A
aggs:
tsids:
terms:
field: _tsid

- length: { aggregations.filterA.tsids.buckets: 1 }
- match: { aggregations.filterA.tsids.buckets.0.key: { "attributes.another.dim": "C", "attributes.dim": "A" } }
- match: { aggregations.filterA.tsids.buckets.0.doc_count: 2 }

- do:
search:
index: k9s
body:
size: 0
aggs:
filterA:
filter:
term:
another.dim: C
aggs:
tsids:
terms:
field: _tsid

- length: { aggregations.filterA.tsids.buckets: 1 }
- match: { aggregations.filterA.tsids.buckets.0.key: { "attributes.another.dim": "C", "attributes.dim": "A" } }
- match: { aggregations.filterA.tsids.buckets.0.doc_count: 2 }

---
dynamic templates - conflicting aliases:
- skip:
version: " - 8.12.99"
reason: "Support for dynamic fields was added in 8.13"
- do:
indices.put_index_template:
name: my-dynamic-template
body:
index_patterns: [k9s*]
data_stream: {}
template:
settings:
index:
number_of_shards: 1
mode: time_series
time_series:
start_time: 2023-08-31T13:03:08.138Z

mappings:
properties:
attributes:
type: passthrough
dynamic: true
time_series_dimension: true
resource_attributes:
type: passthrough
dynamic: true
time_series_dimension: true
dynamic_templates:
- counter_metric:
mapping:
type: integer
time_series_metric: counter

- do:
bulk:
index: k9s
refresh: true
body:
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:08.138Z","data": "10", "attributes.dim": "A", "resource_attributes.dim": "C" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:09.138Z","data": "20", "attributes.dim": "A", "resource_attributes.dim": "C" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:10.138Z","data": "30", "attributes.dim": "B", "resource_attributes.dim": "D" }'
- '{ "create": { "dynamic_templates": { "data": "counter_metric" } } }'
- '{ "@timestamp": "2023-09-01T13:03:10.238Z","data": "40", "attributes.dim": "B", "resource_attributes.dim": "D" }'

- do:
search:
index: k9s
body:
size: 0

- match: { hits.total.value: 4 }

- do:
search:
index: k9s
body:
size: 0
aggs:
filterA:
filter:
term:
dim: "C"
aggs:
tsids:
terms:
field: _tsid

- length: { aggregations.filterA.tsids.buckets: 1 }
- match: { aggregations.filterA.tsids.buckets.0.key: { "resource_attributes.dim": "C", "attributes.dim": "A" } }
- match: { aggregations.filterA.tsids.buckets.0.doc_count: 2 }

- do:
search:
index: k9s
body:
size: 0
aggs:
filterA:
filter:
term:
attributes.dim: A
aggs:
tsids:
terms:
field: _tsid

- length: { aggregations.filterA.tsids.buckets: 1 }
- match: { aggregations.filterA.tsids.buckets.0.key: { "resource_attributes.dim": "C", "attributes.dim": "A" } }
- match: { aggregations.filterA.tsids.buckets.0.doc_count: 2 }

---
dynamic templates - subobject in passthrough object error:
- skip:
version: " - 8.12.99"
reason: "Support for dynamic fields was added in 8.13"
- do:
catch: /Tried to add subobject \[subcategory\] to object \[attributes\] which does not support subobjects/
indices.put_index_template:
name: my-dynamic-template
body:
index_patterns: [k9s*]
data_stream: {}
template:
settings:
index:
mode: time_series

mappings:
properties:
attributes:
type: passthrough
properties:
subcategory:
type: object
properties:
dim:
type: keyword

- do:
catch: /Mapping definition for \[attributes\] has unsupported parameters:\ \[subobjects \:\ true\]/
indices.put_index_template:
name: my-dynamic-template
body:
index_patterns: [k9s*]
data_stream: {}
template:
settings:
index:
number_of_shards: 1
mode: time_series
time_series:
start_time: 2023-08-31T13:03:08.138Z

mappings:
properties:
attributes:
type: passthrough
subobjects: true

---
dynamic templates - passthrough not under root error:
- skip:
version: " - 8.12.99"
reason: "Support for dynamic fields was added in 8.13"
- do:
catch: /Tried to add passthrough subobject \[attributes\] to object \[resource\], passthrough is not supported as a subobject/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't object types have a property of type passthrough? I think we were discussing the opposite where properties of the passthrough field type can't be other objects i.e. subobjects would be implicitly set to false for passthrough field types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It complicates how aliases are added to the root object.

We can look into this in a follow-up change - unless Martijn knows of a simple way to navigate to the root object from the passthrough object builders.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Another alternative would be to recurse the object mappers.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given how we'd want to use the passthrough field type, it would be great if it was supported not only at the root level. See also #104455.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I'll prioritize supporting this and non-keyword dimension fields.

indices.put_index_template:
name: my-dynamic-template
body:
index_patterns: [k9s*]
data_stream: {}
template:
settings:
index:
mode: time_series

mappings:
properties:
"resource.attributes":
type: passthrough
dynamic: true
time_series_dimension: true
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,10 @@ public static class ExtractFromSource extends IndexRouting {
this.parserConfig = XContentParserConfiguration.EMPTY.withFiltering(Set.copyOf(routingPaths), null, true);
}

public boolean matchesField(String fieldName) {
return isRoutingPath.test(fieldName);
}

@Override
public void process(IndexRequest indexRequest) {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,12 @@ public final MapperBuilderContext createDynamicMapperBuilderContext() {
if (p.endsWith(".")) {
p = p.substring(0, p.length() - 1);
}
return new MapperBuilderContext(p, mappingLookup().isSourceSynthetic(), false);
boolean containsDimensions = false;
ObjectMapper objectMapper = mappingLookup.objectMappers().get(p);
if (objectMapper instanceof PassThroughObjectMapper passThroughObjectMapper) {
containsDimensions = passThroughObjectMapper.containsDimensions();
}
return new MapperBuilderContext(p, mappingLookup().isSourceSynthetic(), false, containsDimensions);
}

public abstract XContentParser parser();
Expand Down
Loading