Adds a methods to find (and dynamically create) the mappers for the parents of a field with dots in the field name
This commit is contained in:
parent
897fe9108a
commit
ab3121c871
|
@ -23,6 +23,7 @@ import org.apache.lucene.document.Field;
|
|||
import org.apache.lucene.index.IndexableField;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
|
||||
import org.elasticsearch.common.xcontent.XContentHelper;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
@ -454,19 +455,24 @@ final class DocumentParser {
|
|||
|
||||
private static ObjectMapper parseObject(final ParseContext context, ObjectMapper mapper, String currentFieldName) throws IOException {
|
||||
assert currentFieldName != null;
|
||||
context.path().add(currentFieldName);
|
||||
|
||||
ObjectMapper update = null;
|
||||
Mapper objectMapper = getMapper(mapper, currentFieldName);
|
||||
if (objectMapper != null) {
|
||||
context.path().add(currentFieldName);
|
||||
parseObjectOrField(context, objectMapper);
|
||||
context.path().remove();
|
||||
} else {
|
||||
ObjectMapper.Dynamic dynamic = dynamicOrDefault(mapper, context);
|
||||
|
||||
// TODO: why Strings.splitStringToArray instead of String.split?
|
||||
final String[] paths = Strings.splitStringToArray(currentFieldName, '.');
|
||||
currentFieldName = paths[paths.length - 1];
|
||||
Tuple<Integer, ObjectMapper> parentMapperTuple = getDynamicParentMapper(context, paths, mapper);
|
||||
ObjectMapper parentMapper = parentMapperTuple.v2();
|
||||
ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context);
|
||||
if (dynamic == ObjectMapper.Dynamic.STRICT) {
|
||||
throw new StrictDynamicMappingException(mapper.fullPath(), currentFieldName);
|
||||
} else if (dynamic == ObjectMapper.Dynamic.TRUE) {
|
||||
// remove the current field name from path, since template search and the object builder add it as well...
|
||||
context.path().remove();
|
||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, currentFieldName, "object");
|
||||
if (builder == null) {
|
||||
builder = new ObjectMapper.Builder(currentFieldName).enabled(true);
|
||||
|
@ -476,13 +482,16 @@ final class DocumentParser {
|
|||
context.addDynamicMapper(objectMapper);
|
||||
context.path().add(currentFieldName);
|
||||
parseObjectOrField(context, objectMapper);
|
||||
context.path().remove();
|
||||
} else {
|
||||
// not dynamic, read everything up to end object
|
||||
context.parser().skipChildren();
|
||||
}
|
||||
for (int i = 0; i < parentMapperTuple.v1(); i++) {
|
||||
context.path().remove();
|
||||
}
|
||||
}
|
||||
|
||||
context.path().remove();
|
||||
return update;
|
||||
}
|
||||
|
||||
|
@ -500,6 +509,12 @@ final class DocumentParser {
|
|||
}
|
||||
} else {
|
||||
|
||||
// TODO: why Strings.splitStringToArray instead of String.split?
|
||||
final String[] paths = Strings.splitStringToArray(arrayFieldName, '.');
|
||||
arrayFieldName = paths[paths.length - 1];
|
||||
lastFieldName = arrayFieldName;
|
||||
Tuple<Integer, ObjectMapper> parentMapperTuple = getDynamicParentMapper(context, paths, parentMapper);
|
||||
parentMapper = parentMapperTuple.v2();
|
||||
ObjectMapper.Dynamic dynamic = dynamicOrDefault(parentMapper, context);
|
||||
if (dynamic == ObjectMapper.Dynamic.STRICT) {
|
||||
throw new StrictDynamicMappingException(parentMapper.fullPath(), arrayFieldName);
|
||||
|
@ -507,23 +522,26 @@ final class DocumentParser {
|
|||
Mapper.Builder builder = context.root().findTemplateBuilder(context, arrayFieldName, "object");
|
||||
if (builder == null) {
|
||||
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||
return;
|
||||
}
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||
mapper = builder.build(builderContext);
|
||||
assert mapper != null;
|
||||
if (mapper instanceof ArrayValueMapperParser) {
|
||||
context.addDynamicMapper(mapper);
|
||||
context.path().add(arrayFieldName);
|
||||
parseObjectOrField(context, mapper);
|
||||
context.path().remove();
|
||||
} else {
|
||||
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||
mapper = builder.build(builderContext);
|
||||
assert mapper != null;
|
||||
if (mapper instanceof ArrayValueMapperParser) {
|
||||
context.addDynamicMapper(mapper);
|
||||
context.path().add(arrayFieldName);
|
||||
parseObjectOrField(context, mapper);
|
||||
context.path().remove();
|
||||
} else {
|
||||
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO: shouldn't this skip, not parse?
|
||||
parseNonDynamicArray(context, parentMapper, lastFieldName, arrayFieldName);
|
||||
}
|
||||
for (int i = 0; i < parentMapperTuple.v1(); i++) {
|
||||
context.path().remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -555,7 +573,16 @@ final class DocumentParser {
|
|||
if (mapper != null) {
|
||||
parseObjectOrField(context, mapper);
|
||||
} else {
|
||||
|
||||
// TODO: why Strings.splitStringToArray instead of String.split?
|
||||
final String[] paths = Strings.splitStringToArray(currentFieldName, '.');
|
||||
currentFieldName = paths[paths.length - 1];
|
||||
Tuple<Integer, ObjectMapper> parentMapperTuple = getDynamicParentMapper(context, paths, parentMapper);
|
||||
parentMapper = parentMapperTuple.v2();
|
||||
parseDynamicValue(context, parentMapper, currentFieldName, token);
|
||||
for (int i = 0; i < parentMapperTuple.v1(); i++) {
|
||||
context.path().remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -814,46 +841,58 @@ final class DocumentParser {
|
|||
|
||||
final String[] paths = field.split("\\.");
|
||||
final String fieldName = paths[paths.length-1];
|
||||
ObjectMapper mapper = context.root();
|
||||
ObjectMapper[] mappers = new ObjectMapper[paths.length-1];
|
||||
if (paths.length > 1) {
|
||||
ObjectMapper parent = context.root();
|
||||
for (int i = 0; i < paths.length-1; i++) {
|
||||
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);
|
||||
|
||||
switch (dynamic) {
|
||||
case STRICT:
|
||||
throw new StrictDynamicMappingException(parent.fullPath(), paths[i]);
|
||||
case TRUE:
|
||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, paths[i], "object");
|
||||
if (builder == null) {
|
||||
builder = new ObjectMapper.Builder(paths[i]).enabled(true);
|
||||
}
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||
mapper = (ObjectMapper) builder.build(builderContext);
|
||||
if (mapper.nested() != ObjectMapper.Nested.NO) {
|
||||
throw new MapperParsingException("It is forbidden to create dynamic nested objects ([" + context.path().pathAsText(paths[i]) + "]) through `copy_to`");
|
||||
}
|
||||
context.addDynamicMapper(mapper);
|
||||
break;
|
||||
case FALSE:
|
||||
// Maybe we should log something to tell the user that the copy_to is ignored in this case.
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
context.path().add(paths[i]);
|
||||
mappers[i] = mapper;
|
||||
parent = mapper;
|
||||
}
|
||||
}
|
||||
Tuple<Integer, ObjectMapper> parentMapperTuple = getDynamicParentMapper(context, paths, null);
|
||||
ObjectMapper mapper = parentMapperTuple.v2();
|
||||
parseDynamicValue(context, mapper, fieldName, context.parser().currentToken());
|
||||
for (int i = 0; i < parentMapperTuple.v1(); i++) {
|
||||
context.path().remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Tuple<Integer, ObjectMapper> getDynamicParentMapper(ParseContext context, final String[] paths,
|
||||
ObjectMapper currentParent) {
|
||||
ObjectMapper mapper = currentParent == null ? context.root() : currentParent;
|
||||
ObjectMapper[] mappers = new ObjectMapper[paths.length-1];
|
||||
int pathsAdded = 0;
|
||||
if (paths.length > 1) {
|
||||
ObjectMapper parent = mapper;
|
||||
for (int i = 0; i < paths.length-1; i++) {
|
||||
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);
|
||||
|
||||
switch (dynamic) {
|
||||
case STRICT:
|
||||
throw new StrictDynamicMappingException(parent.fullPath(), paths[i]);
|
||||
case TRUE:
|
||||
Mapper.Builder builder = context.root().findTemplateBuilder(context, paths[i], "object");
|
||||
if (builder == null) {
|
||||
builder = new ObjectMapper.Builder(paths[i]).enabled(true);
|
||||
}
|
||||
Mapper.BuilderContext builderContext = new Mapper.BuilderContext(context.indexSettings(), context.path());
|
||||
mapper = (ObjectMapper) builder.build(builderContext);
|
||||
if (mapper.nested() != ObjectMapper.Nested.NO) {
|
||||
throw new MapperParsingException("It is forbidden to create dynamic nested objects ([" + context.path().pathAsText(paths[i]) + "]) through `copy_to`");
|
||||
}
|
||||
context.addDynamicMapper(mapper);
|
||||
break;
|
||||
case FALSE:
|
||||
// Should not dynamically create any more mappers so return the last mapper
|
||||
return new Tuple<Integer, ObjectMapper>(pathsAdded, parent);
|
||||
|
||||
}
|
||||
}
|
||||
context.path().add(paths[i]);
|
||||
pathsAdded++;
|
||||
mappers[i] = mapper;
|
||||
parent = mapper;
|
||||
}
|
||||
}
|
||||
return new Tuple<Integer, ObjectMapper>(pathsAdded, mapper);
|
||||
}
|
||||
|
||||
// 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();
|
||||
|
|
|
@ -497,4 +497,184 @@ public class DocumentParserTests extends ESSingleNodeTestCase {
|
|||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(0, doc.rootDoc().getFields("foo").length);
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameLongArray() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startArray("foo.bar.baz")
|
||||
.value(0)
|
||||
.value(1)
|
||||
.endArray().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(4, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameLongArrayWithParentTemplate() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startArray("dynamic_templates").startObject().startObject("georule")
|
||||
.field("match", "foo*")
|
||||
.startObject("mapping").field("type", "object").endObject()
|
||||
.endObject().endObject().endArray().endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startArray("foo.bar.baz")
|
||||
.value(0)
|
||||
.value(1)
|
||||
.endArray().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(4, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicFalseDottedFieldNameLongArray() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "false")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startArray("foo.bar.baz")
|
||||
.value(0)
|
||||
.value(1)
|
||||
.endArray().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(0, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicStrictDottedFieldNameLongArray() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "strict")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startArray("foo.bar.baz")
|
||||
.value(0)
|
||||
.value(1)
|
||||
.endArray().endObject().bytes();
|
||||
StrictDynamicMappingException exception = expectThrows(StrictDynamicMappingException.class,
|
||||
() -> mapper.parse("test", "type", "1", bytes));
|
||||
assertEquals("mapping set to strict, dynamic introduction of [foo] within [type] is not allowed", exception.getMessage());
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameLong() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().field("foo.bar.baz", 0)
|
||||
.endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(2, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameLongWithParentTemplate() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startArray("dynamic_templates").startObject().startObject("georule")
|
||||
.field("match", "foo*")
|
||||
.startObject("mapping").field("type", "object").endObject()
|
||||
.endObject().endObject().endArray().endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().field("foo.bar.baz", 0)
|
||||
.endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(2, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicFalseDottedFieldNameLong() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "false")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().field("foo.bar.baz", 0)
|
||||
.endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(0, doc.rootDoc().getFields("foo.bar.baz").length);
|
||||
}
|
||||
|
||||
public void testDynamicStrictDottedFieldNameLong() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "strict")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().field("foo.bar.baz", 0)
|
||||
.endObject().bytes();
|
||||
StrictDynamicMappingException exception = expectThrows(StrictDynamicMappingException.class,
|
||||
() -> mapper.parse("test", "type", "1", bytes));
|
||||
assertEquals("mapping set to strict, dynamic introduction of [foo] within [type] is not allowed", exception.getMessage());
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameObject() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startObject("foo.bar.baz")
|
||||
.field("a", 0)
|
||||
.endObject().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(2, doc.rootDoc().getFields("foo.bar.baz.a").length);
|
||||
}
|
||||
|
||||
public void testDynamicDottedFieldNameObjectWithParentTemplate() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
|
||||
.startArray("dynamic_templates").startObject().startObject("georule")
|
||||
.field("match", "foo*")
|
||||
.startObject("mapping").field("type", "object").endObject()
|
||||
.endObject().endObject().endArray().endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startObject("foo.bar.baz")
|
||||
.field("a", 0)
|
||||
.endObject().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(2, doc.rootDoc().getFields("foo.bar.baz.a").length);
|
||||
}
|
||||
|
||||
public void testDynamicFalseDottedFieldNameObject() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "false")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startObject("foo.bar.baz")
|
||||
.field("a", 0)
|
||||
.endObject().endObject().bytes();
|
||||
ParsedDocument doc = mapper.parse("test", "type", "1", bytes);
|
||||
assertEquals(0, doc.rootDoc().getFields("foo.bar.baz.a").length);
|
||||
}
|
||||
|
||||
public void testDynamicStrictDottedFieldNameObject() throws Exception {
|
||||
DocumentMapperParser mapperParser = createIndex("test").mapperService().documentMapperParser();
|
||||
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type").field("dynamic", "strict")
|
||||
.endObject().endObject().string();
|
||||
DocumentMapper mapper = mapperParser.parse("type", new CompressedXContent(mapping));
|
||||
|
||||
BytesReference bytes = XContentFactory.jsonBuilder()
|
||||
.startObject().startObject("foo.bar.baz")
|
||||
.field("a", 0)
|
||||
.endObject().endObject().bytes();
|
||||
StrictDynamicMappingException exception = expectThrows(StrictDynamicMappingException.class,
|
||||
() -> mapper.parse("test", "type", "1", bytes));
|
||||
assertEquals("mapping set to strict, dynamic introduction of [foo] within [type] is not allowed", exception.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue