Fix not Recognizing Disabled Object Mapper (#39862)

* Fixes not finding disabled object mapper when using dotted field name
notation
* Closes #39456
This commit is contained in:
Tamara Braun 2019-03-14 18:40:01 +01:00 committed by Julie Tibshirani
parent 52b0b9cb55
commit e2b60c7141
2 changed files with 92 additions and 23 deletions

View File

@ -83,6 +83,20 @@ final class DocumentParser {
return parsedDocument(source, context, createDynamicUpdate(mapping, docMapper, context.getDynamicMappers())); 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, private static void internalParseDocument(Mapping mapping, MetadataFieldMapper[] metadataFieldsMappers,
ParseContext.InternalParseContext context, XContentParser parser) throws IOException { ParseContext.InternalParseContext context, XContentParser parser) throws IOException {
final boolean emptyDoc = isEmptyDoc(mapping, parser); final boolean emptyDoc = isEmptyDoc(mapping, parser);
@ -387,24 +401,30 @@ final class DocumentParser {
private static void innerParseObject(ParseContext context, ObjectMapper mapper, XContentParser parser, private static void innerParseObject(ParseContext context, ObjectMapper mapper, XContentParser parser,
String currentFieldName, XContentParser.Token token) throws IOException { 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) { while (token != XContentParser.Token.END_OBJECT) {
if (token == XContentParser.Token.START_OBJECT) { if (token == XContentParser.Token.FIELD_NAME) {
parseObject(context, mapper, currentFieldName);
} else if (token == XContentParser.Token.START_ARRAY) {
parseArray(context, mapper, currentFieldName);
} else if (token == XContentParser.Token.FIELD_NAME) {
currentFieldName = parser.currentName(); currentFieldName = parser.currentName();
paths = splitAndValidatePath(currentFieldName);
if (MapperService.isMetadataField(context.path().pathAsText(currentFieldName))) { if (MapperService.isMetadataField(context.path().pathAsText(currentFieldName))) {
throw new MapperParsingException("Field [" + currentFieldName + "] is a metadata field and cannot be added inside" throw new MapperParsingException("Field [" + currentFieldName + "] is a metadata field and cannot be added inside"
+ " a document. Use the index API request parameters."); + " 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) { } else if (token == XContentParser.Token.VALUE_NULL) {
parseNullValue(context, mapper, currentFieldName); parseNullValue(context, mapper, currentFieldName, paths);
} else if (token == null) { } else if (token == null) {
throw new MapperParsingException("object mapping for [" + mapper.name() + "] tried to parse field [" + currentFieldName 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?"); + "] as object, but got EOF, has a concrete value been provided to it?");
} else if (token.isValue()) { } else if (token.isValue()) {
parseValue(context, mapper, currentFieldName, token); parseValue(context, mapper, currentFieldName, token, paths);
} }
token = parser.nextToken(); 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; assert currentFieldName != null;
final String[] paths = splitAndValidatePath(currentFieldName);
Mapper objectMapper = getMapper(mapper, currentFieldName, paths); Mapper objectMapper = getMapper(mapper, currentFieldName, paths);
if (objectMapper != null) { if (objectMapper != null) {
context.path().add(currentFieldName); 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; String arrayFieldName = lastFieldName;
final String[] paths = splitAndValidatePath(arrayFieldName);
Mapper mapper = getMapper(parentMapper, lastFieldName, paths); Mapper mapper = getMapper(parentMapper, lastFieldName, paths);
if (mapper != null) { if (mapper != null) {
// There is a concrete mapper for this field already. Need to check if the mapper // 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, 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 parser = context.parser();
XContentParser.Token token; XContentParser.Token token;
final String[] paths = splitAndValidatePath(lastFieldName);
while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) { while ((token = parser.nextToken()) != XContentParser.Token.END_ARRAY) {
if (token == XContentParser.Token.START_OBJECT) { if (token == XContentParser.Token.START_OBJECT) {
parseObject(context, mapper, lastFieldName); parseObject(context, mapper, lastFieldName, paths);
} else if (token == XContentParser.Token.START_ARRAY) { } else if (token == XContentParser.Token.START_ARRAY) {
parseArray(context, mapper, lastFieldName); parseArray(context, mapper, lastFieldName, paths);
} else if (token == XContentParser.Token.FIELD_NAME) {
lastFieldName = parser.currentName();
} else if (token == XContentParser.Token.VALUE_NULL) { } else if (token == XContentParser.Token.VALUE_NULL) {
parseNullValue(context, mapper, lastFieldName); parseNullValue(context, mapper, lastFieldName, paths);
} else if (token == null) { } else if (token == null) {
throw new MapperParsingException("object mapping for [" + mapper.name() + "] with array for [" + arrayFieldName 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?"); + "] tried to parse as array, but got EOF, is there a mismatch in types for the same field?");
} else { } else {
parseValue(context, mapper, lastFieldName, token); assert token.isValue();
parseValue(context, mapper, lastFieldName, token, paths);
} }
} }
} }
private static void parseValue(final ParseContext context, ObjectMapper parentMapper, 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) { if (currentFieldName == null) {
throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with" throw new MapperParsingException("object mapping [" + parentMapper.name() + "] trying to serialize a value with"
+ " no field associated with it, current value [" + context.parser().textOrNull() + "]"); + " no field associated with it, current value [" + context.parser().textOrNull() + "]");
} }
final String[] paths = splitAndValidatePath(currentFieldName);
Mapper mapper = getMapper(parentMapper, currentFieldName, paths); Mapper mapper = getMapper(parentMapper, currentFieldName, paths);
if (mapper != null) { if (mapper != null) {
parseObjectOrField(context, mapper); 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 // 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) { if (mapper != null) {
// TODO: passing null to an object seems bogus? // TODO: passing null to an object seems bogus?
parseObjectOrField(context, mapper); parseObjectOrField(context, mapper);

View File

@ -79,6 +79,56 @@ public class DocumentParserTests extends ESSingleNodeTestCase {
assertNotNull(doc.rootDoc().getField(IdFieldMapper.NAME)); 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 { public void testDotsWithExistingMapper() throws Exception {
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser(); DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties") String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type").startObject("properties")