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
This commit is contained in:
Ryan Ernst 2016-04-19 10:56:55 -07:00
parent b8899cdb78
commit d68318fb6c
2 changed files with 81 additions and 6 deletions

View File

@ -480,7 +480,7 @@ final class DocumentParser {
if (objectMapper != null) { if (objectMapper != null) {
parseObjectOrField(context, objectMapper); parseObjectOrField(context, objectMapper);
} else { } else {
ObjectMapper.Dynamic dynamic = dynamicOrDefault(mapper, context.root().dynamic()); ObjectMapper.Dynamic dynamic = dynamicOrDefault(mapper, context);
if (dynamic == ObjectMapper.Dynamic.STRICT) { if (dynamic == ObjectMapper.Dynamic.STRICT) {
throw new StrictDynamicMappingException(mapper.fullPath(), currentFieldName); throw new StrictDynamicMappingException(mapper.fullPath(), currentFieldName);
} else if (dynamic == ObjectMapper.Dynamic.TRUE) { } else if (dynamic == ObjectMapper.Dynamic.TRUE) {
@ -519,7 +519,7 @@ final class DocumentParser {
} }
} else { } else {
ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context.root().dynamic()); ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context);
if (dynamic == ObjectMapper.Dynamic.STRICT) { if (dynamic == ObjectMapper.Dynamic.STRICT) {
throw new StrictDynamicMappingException(parentMapper.fullPath(), arrayFieldName); throw new StrictDynamicMappingException(parentMapper.fullPath(), arrayFieldName);
} else if (dynamic == ObjectMapper.Dynamic.TRUE) { } 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 { 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) { if (dynamic == ObjectMapper.Dynamic.STRICT) {
throw new StrictDynamicMappingException(parentMapper.fullPath(), currentFieldName); throw new StrictDynamicMappingException(parentMapper.fullPath(), currentFieldName);
} }
@ -867,7 +867,7 @@ final class DocumentParser {
mapper = context.docMapper().objectMappers().get(context.path().pathAsText(paths[i])); mapper = context.docMapper().objectMappers().get(context.path().pathAsText(paths[i]));
if (mapper == null) { if (mapper == null) {
// One mapping is missing, check if we are allowed to create a dynamic one. // 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) { switch (dynamic) {
case STRICT: 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(); 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) { if (dynamic == null) {
return dynamicDefault == null ? ObjectMapper.Dynamic.TRUE : dynamicDefault; return context.root().dynamic() == null ? ObjectMapper.Dynamic.TRUE : context.root().dynamic();
} }
return dynamic; return dynamic;
} }

View File

@ -98,6 +98,65 @@ public class DocumentParserTests extends ESSingleNodeTestCase {
assertEquals("789", values[2]); 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 { DocumentMapper createDummyMapping(MapperService mapperService) throws Exception {
String mapping = jsonBuilder().startObject().startObject("type").startObject("properties") String mapping = jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("y").field("type", "object").endObject() .startObject("y").field("type", "object").endObject()