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

Merge mappings for composable index templates #58709

Merged
merged 4 commits into from
Jun 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,89 @@
index: eggplant

- match: {eggplant.settings.index.number_of_shards: "3"}

---
"Index template mapping merging":
- skip:
version: " - 7.8.99"
reason: "index template v2 mapping merging not available before 7.9"
features: allowed_warnings

- do:
cluster.put_component_template:
name: red
body:
template:
mappings:
properties:
object1.red:
type: keyword
object2.red:
type: keyword

- do:
cluster.put_component_template:
name: blue
body:
template:
mappings:
properties:
object2.red:
type: text
object1.blue:
type: text
object2.blue:
type: text

- do:
allowed_warnings:
- "index template [my-template] has index patterns [baz*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation"
indices.put_index_template:
name: blue
body:
index_patterns: ["purple-index"]
composed_of: ["red", "blue"]
template:
mappings:
properties:
object2.blue:
type: integer
object1.purple:
type: integer
object2.purple:
type: integer
nested:
type: nested
include_in_root: true

- do:
indices.create:
index: purple-index
body:
mappings:
properties:
object2.purple:
type: double
object3.purple:
type: double
nested:
type: nested
include_in_root: false
include_in_parent: true

- do:
indices.get:
index: purple-index

- match: {purple-index.mappings.properties.object1.properties.red: {type: keyword}}
- match: {purple-index.mappings.properties.object1.properties.blue: {type: text}}
- match: {purple-index.mappings.properties.object1.properties.purple: {type: integer}}

- match: {purple-index.mappings.properties.object2.properties.red: {type: text}}
- match: {purple-index.mappings.properties.object2.properties.blue: {type: integer}}
- match: {purple-index.mappings.properties.object2.properties.purple: {type: double}}

- match: {purple-index.mappings.properties.object3.properties.purple: {type: double}}

- is_false: purple-index.mappings.properties.nested.include_in_root
- is_true: purple-index.mappings.properties.nested.include_in_parent
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,21 @@
import org.elasticsearch.cluster.block.ClusterBlockLevel;
import org.elasticsearch.cluster.metadata.AliasMetadata;
import org.elasticsearch.cluster.metadata.AliasValidator;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
import org.elasticsearch.cluster.metadata.Metadata;
import org.elasticsearch.cluster.metadata.MetadataCreateIndexService;
import org.elasticsearch.cluster.metadata.MetadataIndexTemplateService;
import org.elasticsearch.cluster.metadata.Template;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.compress.CompressedXContent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.indices.IndicesService;
import org.elasticsearch.threadpool.ThreadPool;
Expand All @@ -58,7 +57,6 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.cluster.metadata.MetadataCreateIndexService.resolveV2Mappings;
import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.findConflictingV1Templates;
import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.findConflictingV2Templates;
import static org.elasticsearch.cluster.metadata.MetadataIndexTemplateService.findV2Template;
Expand Down Expand Up @@ -172,13 +170,6 @@ public static Template resolveTemplate(final String matchingTemplate, final Stri
final AliasValidator aliasValidator) throws Exception {
Settings settings = resolveSettings(simulatedState.metadata(), matchingTemplate);

// empty request mapping as the user can't specify any explicit mappings via the simulate api
Map<String, Map<String, Object>> mappings = resolveV2Mappings("{}", simulatedState, matchingTemplate, xContentRegistry);
assert mappings.size() == 1 : "expected always to have 1 mapping type but there were " + mappings.size();
@SuppressWarnings("unchecked")
Map<String, Object> docMappings = (Map<String, Object>) mappings.get(MapperService.SINGLE_MAPPING_NAME);
String mappingsJson = Strings.toString(XContentFactory.jsonBuilder().map(docMappings));

List<Map<String, AliasMetadata>> resolvedAliases = MetadataIndexTemplateService.resolveAliases(simulatedState.metadata(),
matchingTemplate);

Expand All @@ -203,8 +194,28 @@ public static Template resolveTemplate(final String matchingTemplate, final Stri
// the context is only used for validation so it's fine to pass fake values for the
// shard id and the current timestamp
tempIndexService.newQueryShardContext(0, null, () -> 0L, null)));
Map<String, AliasMetadata> aliasesByName = aliases.stream().collect(
Collectors.toMap(AliasMetadata::getAlias, Function.identity()));

return new Template(settings, mappingsJson == null ? null : new CompressedXContent(mappingsJson),
aliases.stream().collect(Collectors.toMap(AliasMetadata::getAlias, Function.identity())));
// empty request mapping as the user can't specify any explicit mappings via the simulate api
List<Map<String, Map<String, Object>>> mappings = MetadataCreateIndexService.collectV2Mappings(
Collections.emptyMap(), simulatedState, matchingTemplate, xContentRegistry);

CompressedXContent mergedMapping = indicesService.<CompressedXContent, Exception>withTempIndexService(indexMetadata,
tempIndexService -> {
MapperService mapperService = tempIndexService.mapperService();
for (Map<String, Map<String, Object>> mapping : mappings) {
if (!mapping.isEmpty()) {
assert mapping.size() == 1 : mapping;
Map.Entry<String, Map<String, Object>> entry = mapping.entrySet().iterator().next();
mapperService.merge(entry.getKey(), entry.getValue(), MapperService.MergeReason.INDEX_TEMPLATE);
}
}

DocumentMapper documentMapper = mapperService.documentMapper();
return documentMapper != null ? documentMapper.mappingSource() : null;
});

return new Template(settings, mergedMapping, aliasesByName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.mapper.MapperService;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
Expand Down Expand Up @@ -244,28 +245,24 @@ public TimestampField(StreamInput in) throws IOException {
}

/**
* Force fully inserts the timestamp field mapping into the provided mapping.
* Existing mapping definitions for the timestamp field will be completely overwritten.
* Takes into account if the name of the timestamp field is nested.
*
* @param mappings The mapping to update
* Creates a map representing the full timestamp field mapping, taking into
* account if the timestamp field is nested under object mappers (its path
* contains dots).
*/
public void insertTimestampFieldMapping(Map<String, Object> mappings) {
assert mappings.containsKey("_doc");

public Map<String, Map<String, Object>> getTimestampFieldMapping() {
String mappingPath = convertFieldPathToMappingPath(name);
String parentObjectFieldPath = "_doc." + mappingPath.substring(0, mappingPath.lastIndexOf('.'));
String parentObjectFieldPath = mappingPath.substring(0, mappingPath.lastIndexOf('.'));
String leafFieldName = mappingPath.substring(mappingPath.lastIndexOf('.') + 1);

Map<String, Object> changes = new HashMap<>();
Map<String, Object> current = changes;
Map<String, Object> result = new HashMap<>();
Map<String, Object> current = result;
for (String key : parentObjectFieldPath.split("\\.")) {
Map<String, Object> map = new HashMap<>();
current.put(key, map);
current = map;
}
current.put(leafFieldName, fieldMapping);
XContentHelper.update(mappings, changes, false);
return Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, result);
}

@Override
Expand Down
Loading