diff --git a/src/main/java/org/elasticsearch/index/mapper/core/TypeParsers.java b/src/main/java/org/elasticsearch/index/mapper/core/TypeParsers.java index 86bd62d10fb..c6cf49f2a8a 100644 --- a/src/main/java/org/elasticsearch/index/mapper/core/TypeParsers.java +++ b/src/main/java/org/elasticsearch/index/mapper/core/TypeParsers.java @@ -249,8 +249,18 @@ public class TypeParsers { if (propName.equals("path")) { builder.multiFieldPathType(parsePathType(name, propNode.toString())); } else if (propName.equals("fields")) { - @SuppressWarnings("unchecked") - Map multiFieldsPropNodes = (Map) propNode; + + final Map multiFieldsPropNodes; + + if (propNode instanceof List && ((List) propNode).isEmpty()) { + multiFieldsPropNodes = Collections.emptyMap(); + } else if (propNode instanceof Map) { + multiFieldsPropNodes = (Map) propNode; + } else { + throw new MapperParsingException("Expected map for property [fields] on field [" + propNode + "] or " + + "[" + propName + "] but got a " + propNode.getClass()); + } + for (Map.Entry multiFieldEntry : multiFieldsPropNodes.entrySet()) { String multiFieldName = multiFieldEntry.getKey(); if (!(multiFieldEntry.getValue() instanceof Map)) { diff --git a/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java b/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java index 1c5b2080820..e8ad3d454a3 100644 --- a/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java +++ b/src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java @@ -259,31 +259,38 @@ public class ObjectMapper implements Mapper, AllFieldMapper.IncludeInAll { protected static void parseProperties(ObjectMapper.Builder objBuilder, Map propsNode, ParserContext parserContext) { for (Map.Entry entry : propsNode.entrySet()) { String propName = entry.getKey(); - Map propNode = (Map) entry.getValue(); + //Should accept empty arrays, as a work around for when the user can't provide an empty Map. (PHP for example) + boolean isEmptyList = entry.getValue() instanceof List && ((List) entry.getValue()).isEmpty(); - String type; - Object typeNode = propNode.get("type"); - if (typeNode != null) { - type = typeNode.toString(); - } else { - // lets see if we can derive this... - if (propNode.get("properties") != null) { - type = ObjectMapper.CONTENT_TYPE; - } else if (propNode.size() == 1 && propNode.get("enabled") != null) { - // if there is a single property with the enabled flag on it, make it an object - // (usually, setting enabled to false to not index any type, including core values, which - // non enabled object type supports). - type = ObjectMapper.CONTENT_TYPE; + if (entry.getValue() instanceof Map) { + @SuppressWarnings("unchecked") + Map propNode = (Map) entry.getValue(); + String type; + Object typeNode = propNode.get("type"); + if (typeNode != null) { + type = typeNode.toString(); } else { - throw new MapperParsingException("No type specified for property [" + propName + "]"); + // lets see if we can derive this... + if (propNode.get("properties") != null) { + type = ObjectMapper.CONTENT_TYPE; + } else if (propNode.size() == 1 && propNode.get("enabled") != null) { + // if there is a single property with the enabled flag on it, make it an object + // (usually, setting enabled to false to not index any type, including core values, which + // non enabled object type supports). + type = ObjectMapper.CONTENT_TYPE; + } else { + throw new MapperParsingException("No type specified for property [" + propName + "]"); + } } - } - Mapper.TypeParser typeParser = parserContext.typeParser(type); - if (typeParser == null) { - throw new MapperParsingException("No handler for type [" + type + "] declared on field [" + propName + "]"); + Mapper.TypeParser typeParser = parserContext.typeParser(type); + if (typeParser == null) { + throw new MapperParsingException("No handler for type [" + type + "] declared on field [" + propName + "]"); + } + objBuilder.add(typeParser.parse(propName, propNode, parserContext)); + } else if (!isEmptyList) { + throw new MapperParsingException("Expected map for property [fields] on field [" + propName + "] but got a " + propName.getClass()); } - objBuilder.add(typeParser.parse(propName, propNode, parserContext)); } } diff --git a/src/test/java/org/elasticsearch/index/mapper/object/SimpleObjectMappingTests.java b/src/test/java/org/elasticsearch/index/mapper/object/SimpleObjectMappingTests.java index 189289b52a1..b1e71353b6c 100644 --- a/src/test/java/org/elasticsearch/index/mapper/object/SimpleObjectMappingTests.java +++ b/src/test/java/org/elasticsearch/index/mapper/object/SimpleObjectMappingTests.java @@ -63,4 +63,99 @@ public class SimpleObjectMappingTests extends ElasticsearchSingleNodeTest { .endObject().endObject().string(); createIndex("test").mapperService().documentMapperParser().parse(mapping); } + + @Test + public void emptyFieldsArrayMultiFieldsTest() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("tweet") + .startObject("properties") + .startObject("name") + .field("type", "string") + .field("index", "analyzed") + .startArray("fields") + .endArray() + .endObject() + .endObject() + .endObject() + .endObject() + .string(); + createIndex("test").mapperService().documentMapperParser().parse(mapping); + } + + @Test(expected = MapperParsingException.class) + public void fieldsArrayMultiFieldsShouldThrowExceptionTest() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("tweet") + .startObject("properties") + .startObject("name") + .field("type", "string") + .field("index", "analyzed") + .startArray("fields") + .field("test", "string") + .field("test2", "string") + .endArray() + .endObject() + .endObject() + .endObject() + .endObject() + .string(); + createIndex("test").mapperService().documentMapperParser().parse(mapping); + } + + @Test + public void emptyFieldsArrayTest() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("tweet") + .startObject("properties") + .startArray("fields") + .endArray() + .endObject() + .endObject() + .endObject() + .string(); + createIndex("test").mapperService().documentMapperParser().parse(mapping); + } + + @Test(expected = MapperParsingException.class) + public void fieldsWithFilledArrayShouldThrowExceptionTest() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("tweet") + .startObject("properties") + .startArray("fields") + .field("test", "string") + .field("test2", "string") + .endArray() + .endObject() + .endObject() + .endObject() + .string(); + createIndex("test").mapperService().documentMapperParser().parse(mapping); + } + + @Test + public void fieldPropertiesArrayTest() throws Exception { + String mapping = XContentFactory.jsonBuilder() + .startObject() + .startObject("tweet") + .startObject("properties") + .startObject("name") + .field("type", "string") + .field("index", "analyzed") + .startObject("fields") + .startObject("raw") + .field("type", "string") + .field("index","not_analyzed") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .endObject() + .string(); + createIndex("test").mapperService().documentMapperParser().parse(mapping); + } }