Skip to content

Commit

Permalink
Check if root mapping is actually valid
Browse files Browse the repository at this point in the history
When a mapping is declared and the type is known from the uri
then the type can be skipped in the body (see elastic#4483). However,
there was no check if the given keys actually make a valid mapping.

closes elastic#5864
  • Loading branch information
brwe committed May 10, 2014
1 parent bac0627 commit df99d75
Show file tree
Hide file tree
Showing 14 changed files with 245 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Nullable;
Expand Down Expand Up @@ -48,7 +49,9 @@
import org.elasticsearch.index.settings.IndexSettings;
import org.elasticsearch.index.similarity.SimilarityLookupService;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import static org.elasticsearch.index.mapper.MapperBuilders.doc;

Expand Down Expand Up @@ -201,31 +204,38 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de


Mapper.TypeParser.ParserContext parserContext = parserContext();
// parse RootObjectMapper
DocumentMapper.Builder docBuilder = doc(index.name(), indexSettings, (RootObjectMapper.Builder) rootObjectTypeParser.parse(type, mapping, parserContext));

for (Map.Entry<String, Object> entry : mapping.entrySet()) {
Iterator<Map.Entry<String, Object>> iterator = mapping.entrySet().iterator();
// parse DocumentMapper
while(iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
String fieldName = Strings.toUnderscoreCase(entry.getKey());
Object fieldNode = entry.getValue();

if ("index_analyzer".equals(fieldName)) {
iterator.remove();
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for index_analyzer setting on root type [" + type + "]");
}
docBuilder.indexAnalyzer(analyzer);
} else if ("search_analyzer".equals(fieldName)) {
iterator.remove();
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for search_analyzer setting on root type [" + type + "]");
}
docBuilder.searchAnalyzer(analyzer);
} else if ("search_quote_analyzer".equals(fieldName)) {
iterator.remove();
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for search_analyzer setting on root type [" + type + "]");
}
docBuilder.searchQuoteAnalyzer(analyzer);
} else if ("analyzer".equals(fieldName)) {
iterator.remove();
NamedAnalyzer analyzer = analysisService.analyzer(fieldNode.toString());
if (analyzer == null) {
throw new MapperParsingException("Analyzer [" + fieldNode.toString() + "] not found for analyzer setting on root type [" + type + "]");
Expand All @@ -235,11 +245,26 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de
} else {
Mapper.TypeParser typeParser = rootTypeParsers.get(fieldName);
if (typeParser != null) {
iterator.remove();
docBuilder.put(typeParser.parse(fieldName, (Map<String, Object>) fieldNode, parserContext));
}
}
}

ImmutableMap<String, Object> attributes = ImmutableMap.of();
if (mapping.containsKey("_meta")) {
attributes = ImmutableMap.copyOf((Map<String, Object>) mapping.get("_meta"));
mapping.remove("_meta");
}
docBuilder.meta(attributes);

if (!mapping.isEmpty()) {
String remainingFields = "";
for (String key : mapping.keySet()) {
remainingFields += " [" + key + " : " + mapping.get(key).toString() + "]";
}
throw new MapperParsingException("Root type mapping not empty after parsing! Remaining fields:" + remainingFields);
}
if (!docBuilder.hasIndexAnalyzer()) {
docBuilder.indexAnalyzer(analysisService.defaultIndexAnalyzer());
}
Expand All @@ -250,12 +275,6 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de
docBuilder.searchAnalyzer(analysisService.defaultSearchQuoteAnalyzer());
}

ImmutableMap<String, Object> attributes = ImmutableMap.of();
if (mapping.containsKey("_meta")) {
attributes = ImmutableMap.copyOf((Map<String, Object>) mapping.get("_meta"));
}
docBuilder.meta(attributes);

DocumentMapper documentMapper = docBuilder.build(this);
// update the source with the generated one
documentMapper.refreshSource();
Expand All @@ -279,15 +298,13 @@ private Tuple<String, Map<String, Object>> extractMapping(String type, Map<Strin
// if we don't have any keys throw an exception
throw new MapperParsingException("malformed mapping no root object found");
}

String rootName = root.keySet().iterator().next();
Tuple<String, Map<String, Object>> mapping;
if (type == null || type.equals(rootName)) {
mapping = new Tuple<>(rootName, (Map<String, Object>) root.get(rootName));
} else {
mapping = new Tuple<>(type, root);
}

return mapping;
}
}
108 changes: 62 additions & 46 deletions src/main/java/org/elasticsearch/index/mapper/object/ObjectMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -180,63 +180,83 @@ protected ObjectMapper createMapper(String name, String fullPath, boolean enable
public static class TypeParser implements Mapper.TypeParser {
@Override
public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
Map<String, Object> objectNode = node;
ObjectMapper.Builder builder = createBuilder(name);
Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
while (iterator.hasNext()) {

boolean nested = false;
boolean nestedIncludeInParent = false;
boolean nestedIncludeInRoot = false;
for (Map.Entry<String, Object> entry : objectNode.entrySet()) {
Map.Entry<String, Object> entry = iterator.next();
String fieldName = Strings.toUnderscoreCase(entry.getKey());
Object fieldNode = entry.getValue();
parseObjectOrDocumentTyeProperties( fieldName, fieldNode, parserContext, builder);
parseObjectProperties(name, fieldName, fieldNode, builder);
}
parseNested(name, node, builder);
return builder;
}

if (fieldName.equals("dynamic")) {
String value = fieldNode.toString();
if (value.equalsIgnoreCase("strict")) {
builder.dynamic(Dynamic.STRICT);
} else {
builder.dynamic(nodeBooleanValue(fieldNode) ? Dynamic.TRUE : Dynamic.FALSE);
}
} else if (fieldName.equals("type")) {
String type = fieldNode.toString();
if (type.equals(CONTENT_TYPE)) {
builder.nested = Nested.NO;
} else if (type.equals(NESTED_CONTENT_TYPE)) {
nested = true;
} else {
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
}
} else if (fieldName.equals("include_in_parent")) {
nestedIncludeInParent = nodeBooleanValue(fieldNode);
} else if (fieldName.equals("include_in_root")) {
nestedIncludeInRoot = nodeBooleanValue(fieldNode);
} else if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode));
} else if (fieldName.equals("path")) {
builder.pathType(parsePathType(name, fieldNode.toString()));
} else if (fieldName.equals("properties")) {
if (fieldNode instanceof Collection && ((Collection) fieldNode).isEmpty()) {
// nothing to do here, empty (to support "properties: []" case)
} else if (!(fieldNode instanceof Map)) {
throw new ElasticsearchParseException("properties must be a map type");
} else {
parseProperties(builder, (Map<String, Object>) fieldNode, parserContext);
}
} else if (fieldName.equals("include_in_all")) {
builder.includeInAll(nodeBooleanValue(fieldNode));
protected static boolean parseObjectOrDocumentTyeProperties(String fieldName, Object fieldNode, ParserContext parserContext, ObjectMapper.Builder builder) {
if (fieldName.equals("dynamic")) {
String value = fieldNode.toString();
if (value.equalsIgnoreCase("strict")) {
builder.dynamic(Dynamic.STRICT);
} else {
processField(builder, fieldName, fieldNode);
builder.dynamic(nodeBooleanValue(fieldNode) ? Dynamic.TRUE : Dynamic.FALSE);
}
return true;
} else if (fieldName.equals("enabled")) {
builder.enabled(nodeBooleanValue(fieldNode));
return true;
} else if (fieldName.equals("properties")) {
if (fieldNode instanceof Collection && ((Collection) fieldNode).isEmpty()) {
// nothing to do here, empty (to support "properties: []" case)
} else if (!(fieldNode instanceof Map)) {
throw new ElasticsearchParseException("properties must be a map type");
} else {
parseProperties(builder, (Map<String, Object>) fieldNode, parserContext);
}
return true;
}
return false;
}

protected static void parseObjectProperties(String name, String fieldName, Object fieldNode, ObjectMapper.Builder builder) {
if (fieldName.equals("path")) {
builder.pathType(parsePathType(name, fieldNode.toString()));
} else if (fieldName.equals("include_in_all")) {
builder.includeInAll(nodeBooleanValue(fieldNode));
}
}

protected static void parseNested(String name, Map<String, Object> node, ObjectMapper.Builder builder) {
boolean nested = false;
boolean nestedIncludeInParent = false;
boolean nestedIncludeInRoot = false;
Object fieldNode = node.get("type");
if (fieldNode!=null) {
String type = fieldNode.toString();
if (type.equals(CONTENT_TYPE)) {
builder.nested = Nested.NO;
} else if (type.equals(NESTED_CONTENT_TYPE)) {
nested = true;
} else {
throw new MapperParsingException("Trying to parse an object but has a different type [" + type + "] for [" + name + "]");
}
}
fieldNode = node.get("include_in_parent");
if (fieldNode != null) {
nestedIncludeInParent = nodeBooleanValue(fieldNode);
}
fieldNode = node.get("include_in_root");
if (fieldNode != null) {
nestedIncludeInRoot = nodeBooleanValue(fieldNode);
}
if (nested) {
builder.nested = Nested.newNested(nestedIncludeInParent, nestedIncludeInRoot);
}

return builder;
}

private void parseProperties(ObjectMapper.Builder objBuilder, Map<String, Object> propsNode, ParserContext parserContext) {
protected static void parseProperties(ObjectMapper.Builder objBuilder, Map<String, Object> propsNode, ParserContext parserContext) {
for (Map.Entry<String, Object> entry : propsNode.entrySet()) {
String propName = entry.getKey();
Map<String, Object> propNode = (Map<String, Object>) entry.getValue();
Expand Down Expand Up @@ -270,10 +290,6 @@ private void parseProperties(ObjectMapper.Builder objBuilder, Map<String, Object
protected Builder createBuilder(String name) {
return object(name);
}

protected void processField(Builder builder, String fieldName, Object fieldNode) {

}
}

private final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@

import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
import org.elasticsearch.common.joda.Joda;
import org.elasticsearch.common.xcontent.ToXContent;
Expand All @@ -29,14 +31,12 @@
import org.elasticsearch.index.mapper.core.DateFieldMapper;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;

import static com.google.common.collect.Lists.newArrayList;
import static org.elasticsearch.common.xcontent.support.XContentMapValues.nodeBooleanValue;
import static org.elasticsearch.index.mapper.core.TypeParsers.parseDateTimeFormatter;
import static org.elasticsearch.index.mapper.core.TypeParsers.parsePathType;

/**
*
Expand Down Expand Up @@ -124,7 +124,25 @@ protected ObjectMapper.Builder createBuilder(String name) {
}

@Override
protected void processField(ObjectMapper.Builder builder, String fieldName, Object fieldNode) {
public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {

ObjectMapper.Builder builder = createBuilder(name);
Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> entry = iterator.next();
String fieldName = Strings.toUnderscoreCase(entry.getKey());
Object fieldNode = entry.getValue();
if (parseObjectOrDocumentTyeProperties( fieldName, fieldNode, parserContext, builder)) {
iterator.remove();
}
if (processField(builder,fieldName, fieldNode)) {
iterator.remove();
}
}
return builder;
}

protected boolean processField(ObjectMapper.Builder builder, String fieldName, Object fieldNode) {
if (fieldName.equals("date_formats") || fieldName.equals("dynamic_date_formats")) {
List<FormatDateTimeFormatter> dateTimeFormatters = newArrayList();
if (fieldNode instanceof List) {
Expand All @@ -141,6 +159,7 @@ protected void processField(ObjectMapper.Builder builder, String fieldName, Obje
} else {
((Builder) builder).dynamicDateTimeFormatter(dateTimeFormatters);
}
return true;
} else if (fieldName.equals("dynamic_templates")) {
// "dynamic_templates" : [
// {
Expand All @@ -160,11 +179,15 @@ protected void processField(ObjectMapper.Builder builder, String fieldName, Obje
Map.Entry<String, Object> entry = tmpl.entrySet().iterator().next();
((Builder) builder).add(DynamicTemplate.parse(entry.getKey(), (Map<String, Object>) entry.getValue()));
}
return true;
} else if (fieldName.equals("date_detection")) {
((Builder) builder).dateDetection = nodeBooleanValue(fieldNode);
return true;
} else if (fieldName.equals("numeric_detection")) {
((Builder) builder).numericDetection = nodeBooleanValue(fieldNode);
return true;
}
return false;
}
}

Expand Down
Loading

0 comments on commit df99d75

Please sign in to comment.