[7.x] Handle merging dotted object names when merging V2 template mappings (#55982) (#56041)

Backports the following commits to 7.x:
 - Handle merging dotted object names when merging V2 template mappings (#55982)
This commit is contained in:
Lee Hinman 2020-04-30 10:51:43 -06:00 committed by GitHub
parent c5b04311e0
commit 3dada1e2d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 88 additions and 2 deletions

View File

@ -88,6 +88,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@ -607,7 +608,7 @@ public class MetadataCreateIndexService {
nonProperties = innerTemplateNonProperties;
if (maybeProperties != null) {
properties.putAll(maybeProperties);
properties = mergeIgnoringDots(properties, maybeProperties);
}
}
}
@ -621,7 +622,7 @@ public class MetadataCreateIndexService {
nonProperties = innerRequestNonProperties;
if (maybeRequestProperties != null) {
properties.putAll(maybeRequestProperties);
properties = mergeIgnoringDots(properties, maybeRequestProperties);
}
}
@ -630,6 +631,27 @@ public class MetadataCreateIndexService {
return Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, finalMappings);
}
/**
* Add the objects in the second map to the first, where the keys in the {@code second} map have
* higher predecence and overwrite the keys in the {@code first} map. In the event of a key with
* a dot in it (ie, "foo.bar"), the keys are treated as only the prefix counting towards
* equality. If the {@code second} map has a key such as "foo", all keys starting from "foo." in
* the {@code first} map are discarded.
*/
static Map<String, Object> mergeIgnoringDots(Map<String, Object> first, Map<String, Object> second) {
Objects.requireNonNull(first, "merging requires two non-null maps but the first map was null");
Objects.requireNonNull(second, "merging requires two non-null maps but the second map was null");
Map<String, Object> results = new HashMap<>(first);
Set<String> prefixes = second.keySet().stream().map(MetadataCreateIndexService::prefix).collect(Collectors.toSet());
results.keySet().removeIf(k -> prefixes.contains(prefix(k)));
results.putAll(second);
return results;
}
private static String prefix(String s) {
return s.split("\\.", 2)[0];
}
/**
* Parses the provided mappings json and the inheritable mappings from the templates (if any)
* into a map.

View File

@ -1064,6 +1064,70 @@ public class MetadataCreateIndexServiceTests extends ESTestCase {
assertThat(innerInnerResolved.get("foo"), equalTo(fooMappings));
}
@SuppressWarnings("unchecked")
public void testMappingsMergingHandlesDots() throws Exception {
Template ctt1 = new Template(null,
new CompressedXContent("{\"_doc\":{\"properties\":{\"foo\":{\"properties\":{\"bar\":{\"type\": \"long\"}}}}}}"), null);
Template ctt2 = new Template(null,
new CompressedXContent("{\"_doc\":{\"properties\":{\"foo.bar\":{\"type\": \"text\",\"analyzer\":\"english\"}}}}"), null);
ComponentTemplate ct1 = new ComponentTemplate(ctt1, null, null);
ComponentTemplate ct2 = new ComponentTemplate(ctt2, null, null);
IndexTemplateV2 template = new IndexTemplateV2(Collections.singletonList("index"), null, Arrays.asList("ct2", "ct1"),
null, null, null);
ClusterState state = ClusterState.builder(ClusterState.EMPTY_STATE)
.metadata(Metadata.builder(Metadata.EMPTY_METADATA)
.put("ct1", ct1)
.put("ct2", ct2)
.put("index-template", template)
.build())
.build();
Map<String, Map<String, Object>> resolved =
MetadataCreateIndexService.resolveV2Mappings("{}", state,
"index-template", new NamedXContentRegistry(Collections.emptyList()));
assertThat("expected exactly one type but was: " + resolved, resolved.size(), equalTo(1));
Map<String, Object> innerResolved = (Map<String, Object>) resolved.get(MapperService.SINGLE_MAPPING_NAME);
assertThat("was: " + innerResolved, innerResolved.size(), equalTo(1));
Map<String, Object> innerInnerResolved = (Map<String, Object>) innerResolved.get("properties");
assertThat(innerInnerResolved.size(), equalTo(1));
assertThat(innerInnerResolved.get("foo"),
equalTo(Collections.singletonMap("properties", Collections.singletonMap("bar", Collections.singletonMap("type", "long")))));
}
public void testMergeIgnoringDots() throws Exception {
Map<String, Object> first = new HashMap<>();
first.put("foo", Collections.singletonMap("type", "long"));
Map<String, Object> second = new HashMap<>();
second.put("foo.bar", Collections.singletonMap("type", "long"));
Map<String, Object> results = MetadataCreateIndexService.mergeIgnoringDots(first, second);
assertThat(results, equalTo(second));
results = MetadataCreateIndexService.mergeIgnoringDots(second, first);
assertThat(results, equalTo(first));
second.clear();
Map<String, Object> inner = new HashMap<>();
inner.put("type", "text");
inner.put("analyzer", "english");
second.put("foo", inner);
results = MetadataCreateIndexService.mergeIgnoringDots(first, second);
assertThat(results, equalTo(second));
first.put("baz", 3);
second.put("egg", 7);
results = MetadataCreateIndexService.mergeIgnoringDots(first, second);
Map<String, Object> expected = new HashMap<>(second);
expected.put("baz", 3);
assertThat(results, equalTo(expected));
}
private IndexTemplateMetadata addMatchingTemplate(Consumer<IndexTemplateMetadata.Builder> configurator) {
IndexTemplateMetadata.Builder builder = templateMetadataBuilder("template1", "te*");
configurator.accept(builder);