From d68318fb6c603262744c4bcd7e0261c7f7278214 Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Tue, 19 Apr 2016 10:56:55 -0700 Subject: [PATCH] Mappings: Fix dynamic check to properly handle parents This change fixes the lookup during document parsing of whether an object field is dynamic to handle looking up through parent object mappers, and also handle when a parent object mapper was created during document parsing. closes #17854 --- .../index/mapper/DocumentParser.java | 28 +++++++-- .../index/mapper/DocumentParserTests.java | 59 +++++++++++++++++++ 2 files changed, 81 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index 34dce15dca5..17d2c9124f6 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -480,7 +480,7 @@ final class DocumentParser { if (objectMapper != null) { parseObjectOrField(context, objectMapper); } else { - ObjectMapper.Dynamic dynamic = dynamicOrDefault(mapper, context.root().dynamic()); + ObjectMapper.Dynamic dynamic = dynamicOrDefault(mapper, context); if (dynamic == ObjectMapper.Dynamic.STRICT) { throw new StrictDynamicMappingException(mapper.fullPath(), currentFieldName); } else if (dynamic == ObjectMapper.Dynamic.TRUE) { @@ -519,7 +519,7 @@ final class DocumentParser { } } else { - ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context.root().dynamic()); + ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context); if (dynamic == ObjectMapper.Dynamic.STRICT) { throw new StrictDynamicMappingException(parentMapper.fullPath(), arrayFieldName); } else if (dynamic == ObjectMapper.Dynamic.TRUE) { @@ -794,7 +794,7 @@ final class DocumentParser { } private static void parseDynamicValue(final ParseContext context, ObjectMapper parentMapper, String currentFieldName, XContentParser.Token token) throws IOException { - ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context.root().dynamic()); + ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context); if (dynamic == ObjectMapper.Dynamic.STRICT) { throw new StrictDynamicMappingException(parentMapper.fullPath(), currentFieldName); } @@ -867,7 +867,7 @@ final class DocumentParser { mapper = context.docMapper().objectMappers().get(context.path().pathAsText(paths[i])); if (mapper == null) { // One mapping is missing, check if we are allowed to create a dynamic one. - ObjectMapper.Dynamic dynamic = dynamicOrDefault(parent, context.root().dynamic()); + ObjectMapper.Dynamic dynamic = dynamicOrDefault(parent, context); switch (dynamic) { case STRICT: @@ -899,10 +899,26 @@ final class DocumentParser { } } - private static ObjectMapper.Dynamic dynamicOrDefault(ObjectMapper parentMapper, ObjectMapper.Dynamic dynamicDefault) { + // find what the dynamic setting is given the current parse context and parent + private static ObjectMapper.Dynamic dynamicOrDefault(ObjectMapper parentMapper, ParseContext context) { ObjectMapper.Dynamic dynamic = parentMapper.dynamic(); + while (dynamic == null) { + int lastDotNdx = parentMapper.name().lastIndexOf('.'); + if (lastDotNdx == -1) { + // no dot means we the parent is the root, so just delegate to the default outside the loop + break; + } + String parentName = parentMapper.name().substring(0, lastDotNdx); + parentMapper = context.docMapper().objectMappers().get(parentName); + if (parentMapper == null) { + // If parentMapper is ever null, it means the parent of the current mapper was dynamically created. + // But in order to be created dynamically, the dynamic setting of that parent was necessarily true + return ObjectMapper.Dynamic.TRUE; + } + dynamic = parentMapper.dynamic(); + } if (dynamic == null) { - return dynamicDefault == null ? ObjectMapper.Dynamic.TRUE : dynamicDefault; + return context.root().dynamic() == null ? ObjectMapper.Dynamic.TRUE : context.root().dynamic(); } return dynamic; } diff --git a/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java b/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java index 849c71fc7a8..9319294c31d 100644 --- a/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java +++ b/core/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java @@ -98,6 +98,65 @@ public class DocumentParserTests extends ESSingleNodeTestCase { assertEquals("789", values[2]); } + public void testPropagateDynamicWithExistingMapper() throws Exception { + DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .field("dynamic", false) + .startObject("properties") + .startObject("foo") + .field("type", "object") + .field("dynamic", true) + .startObject("properties") + .endObject().endObject().endObject().endObject().string(); + DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping)); + BytesReference bytes = XContentFactory.jsonBuilder() + .startObject().startObject("foo") + .field("bar", "something") + .endObject().endObject().bytes(); + ParsedDocument doc = mapper.parse("test", "type", "1", bytes); + assertNotNull(doc.dynamicMappingsUpdate()); + assertNotNull(doc.rootDoc().getField("foo.bar")); + } + + public void testPropagateDynamicWithDynamicMapper() throws Exception { + DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .field("dynamic", false) + .startObject("properties") + .startObject("foo") + .field("type", "object") + .field("dynamic", true) + .startObject("properties") + .endObject().endObject().endObject().endObject().string(); + DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping)); + BytesReference bytes = XContentFactory.jsonBuilder() + .startObject().startObject("foo").startObject("bar") + .field("baz", "something") + .endObject().endObject().endObject().bytes(); + ParsedDocument doc = mapper.parse("test", "type", "1", bytes); + assertNotNull(doc.dynamicMappingsUpdate()); + assertNotNull(doc.rootDoc().getField("foo.bar.baz")); + } + + public void testDynamicRootFallback() throws Exception { + DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); + String mapping = XContentFactory.jsonBuilder().startObject().startObject("type") + .field("dynamic", false) + .startObject("properties") + .startObject("foo") + .field("type", "object") + .startObject("properties") + .endObject().endObject().endObject().endObject().string(); + DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping)); + BytesReference bytes = XContentFactory.jsonBuilder() + .startObject().startObject("foo") + .field("bar", "something") + .endObject().endObject().bytes(); + ParsedDocument doc = mapper.parse("test", "type", "1", bytes); + assertNull(doc.dynamicMappingsUpdate()); + assertNull(doc.rootDoc().getField("foo.bar")); + } + DocumentMapper createDummyMapping(MapperService mapperService) throws Exception { String mapping = jsonBuilder().startObject().startObject("type").startObject("properties") .startObject("y").field("type", "object").endObject()