diff --git a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index fe2bd6e9eed..b2f88c20b18 100644 --- a/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -83,6 +83,20 @@ final class DocumentParser { return parsedDocument(source, context, createDynamicUpdate(mapping, docMapper, context.getDynamicMappers())); } + private static boolean containsDisabledObjectMapper(ObjectMapper objectMapper, String[] subfields) { + for (int i = 0; i < subfields.length - 1; ++i) { + Mapper mapper = objectMapper.getMapper(subfields[i]); + if (mapper instanceof ObjectMapper == false) { + break; + } + objectMapper = (ObjectMapper) mapper; + if (objectMapper.isEnabled() == false) { + return true; + } + } + return false; + } + private static void internalParseDocument(Mapping mapping, MetadataFieldMapper[] metadataFieldsMappers, ParseContext.InternalParseContext context, XContentParser parser) throws IOException { final boolean emptyDoc = isEmptyDoc(mapping, parser); @@ -387,24 +401,30 @@ final class DocumentParser { private static void innerParseObject(ParseContext context, ObjectMapper mapper, XContentParser parser, String currentFieldName, XContentParser.Token token) throws IOException { + assert token == XContentParser.Token.FIELD_NAME || token == XContentParser.Token.END_OBJECT; + String[] paths = null; while (token != XContentParser.Token.END_OBJECT) { - if (token == XContentParser.Token.START_OBJECT) { - parseObject(context, mapper, currentFieldName); - } else if (token == XContentParser.Token.START_ARRAY) { - parseArray(context, mapper, currentFieldName); - } else if (token == XContentParser.Token.FIELD_NAME) { + if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); + paths = splitAndValidatePath(currentFieldName); if (MapperService.isMetadataField(context.path().pathAsText(currentFieldName))) { throw new MapperParsingException("Field [" + currentFieldName + "] is a metadata field and cannot be added inside" + " a document. Use the index API request parameters."); + } else if (containsDisabledObjectMapper(mapper, paths)) { + parser.nextToken(); + parser.skipChildren(); } + } else if (token == XContentParser.Token.START_OBJECT) { + parseObject(context, mapper, currentFieldName, paths); + } else if (token == XContentParser.Token.START_ARRAY) { + parseArray(context, mapper, currentFieldName, paths); } else if (token == XContentParser.Token.VALUE_NULL) { - parseNullValue(context, mapper, currentFieldName); + parseNullValue(context, mapper, currentFieldName, paths); } else if (token == null) { throw new MapperParsingException("object mapping for [" + mapper.name() + "] tried to parse field [" + currentFieldName + "] as object, but got EOF, has a concrete value been provided to it?"); } else if (token.isValue()) { - parseValue(context, mapper, currentFieldName, token); + parseValue(context, mapper, currentFieldName, token, paths); } token = parser.nextToken(); } @@ -475,10 +495,10 @@ final class DocumentParser { } } - private static void parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException { + private static void parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName, + String[] paths) throws IOException { assert currentFieldName != null; - final String[] paths = splitAndValidatePath(currentFieldName); Mapper objectMapper = getMapper(mapper, currentFieldName, paths); if (objectMapper != null) { context.path().add(currentFieldName); @@ -512,10 +532,10 @@ final class DocumentParser { } } - private static void parseArray(ParseContext context, ObjectMapper parentMapper, String lastFieldName) throws IOException { + private static void parseArray(ParseContext context, ObjectMapper parentMapper, String lastFieldName, + String[] paths) throws IOException { String arrayFieldName = lastFieldName; - final String[] paths = splitAndValidatePath(arrayFieldName); Mapper mapper = getMapper(parentMapper, lastFieldName, paths); if (mapper != null) { // There is a concrete mapper for this field already. Need to check if the mapper @@ -562,35 +582,33 @@ final class DocumentParser { } private static void parseNonDynamicArray(ParseContext context, ObjectMapper mapper, - String lastFieldName, String arrayFieldName) throws IOException { + final String lastFieldName, String arrayFieldName) throws IOException { XContentParser parser = context.parser(); XContentParser.Token token; + final String[] paths = splitAndValidatePath(lastFieldName); while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { if (token == XContentParser.Token.START_OBJECT) { - parseObject(context, mapper, lastFieldName); + parseObject(context, mapper, lastFieldName, paths); } else if (token == XContentParser.Token.START_ARRAY) { - parseArray(context, mapper, lastFieldName); - } else if (token == XContentParser.Token.FIELD_NAME) { - lastFieldName = parser.currentName(); + parseArray(context, mapper, lastFieldName, paths); } else if (token == XContentParser.Token.VALUE_NULL) { - parseNullValue(context, mapper, lastFieldName); + parseNullValue(context, mapper, lastFieldName, paths); } else if (token == null) { throw new MapperParsingException("object mapping for [" + mapper.name() + "] with array for [" + arrayFieldName + "] tried to parse as array, but got EOF, is there a mismatch in types for the same field?"); } else { - parseValue(context, mapper, lastFieldName, token); + assert token.isValue(); + parseValue(context, mapper, lastFieldName, token, paths); } } } private static void parseValue(final ParseContext context, ObjectMapper parentMapper, - String currentFieldName, XContentParser.Token token) throws IOException { + String currentFieldName, XContentParser.Token token, String[] paths) throws IOException { if (currentFieldName == null) { throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with" + " no field associated with it, current value [" + context.parser().textOrNull() + "]"); } - - final String[] paths = splitAndValidatePath(currentFieldName); Mapper mapper = getMapper(parentMapper, currentFieldName, paths); if (mapper != null) { parseObjectOrField(context, mapper); @@ -605,9 +623,10 @@ final class DocumentParser { } } - private static void parseNullValue(ParseContext context, ObjectMapper parentMapper, String lastFieldName) throws IOException { + private static void parseNullValue(ParseContext context, ObjectMapper parentMapper, String lastFieldName, + String[] paths) throws IOException { // we can only handle null values if we have mappings for them - Mapper mapper = getMapper(parentMapper, lastFieldName, splitAndValidatePath(lastFieldName)); + Mapper mapper = getMapper(parentMapper, lastFieldName, paths); if (mapper != null) { // TODO: passing null to an object seems bogus? parseObjectOrField(context, mapper); diff --git a/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java b/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java index 14ce254e7fe..f7f3432aa6a 100644 --- a/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java +++ b/server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java @@ -79,6 +79,56 @@ public class DocumentParserTests extends ESSingleNodeTestCase { assertNotNull(doc.rootDoc().getField(IdFieldMapper.NAME)); } + public void testDotsWithFieldDisabled() throws IOException { + DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); + String mapping = Strings.toString(jsonBuilder().startObject().startObject("type").startObject("properties") + .startObject("foo").field("enabled", false).endObject() + .endObject().endObject().endObject()); + DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping)); + { + BytesReference bytes = BytesReference.bytes(jsonBuilder() + .startObject() + .field("foo.bar", 111) + .endObject()); + ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON)); + assertNull(doc.rootDoc().getField("foo")); + assertNull(doc.rootDoc().getField("bar")); + assertNull(doc.rootDoc().getField("foo.bar")); + } + { + BytesReference bytes = BytesReference.bytes(jsonBuilder() + .startObject() + .field("foo.bar", new int[]{1, 2, 3}) + .endObject()); + ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON)); + assertNull(doc.rootDoc().getField("foo")); + assertNull(doc.rootDoc().getField("bar")); + assertNull(doc.rootDoc().getField("foo.bar")); + } + { + BytesReference bytes = BytesReference.bytes(jsonBuilder() + .startObject() + .field("foo.bar", Collections.singletonMap("key", "value")) + .endObject()); + ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON)); + assertNull(doc.rootDoc().getField("foo")); + assertNull(doc.rootDoc().getField("bar")); + assertNull(doc.rootDoc().getField("foo.bar")); + } + { + BytesReference bytes = BytesReference.bytes(jsonBuilder() + .startObject() + .field("foo.bar", "string value") + .field("blub", 222) + .endObject()); + ParsedDocument doc = mapper.parse(new SourceToParse("test", "type", "1", bytes, XContentType.JSON)); + assertNull(doc.rootDoc().getField("foo")); + assertNull(doc.rootDoc().getField("bar")); + assertNull(doc.rootDoc().getField("foo.bar")); + assertNotNull(doc.rootDoc().getField("blub")); + } + } + public void testDotsWithExistingMapper() throws Exception { DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")