-
Notifications
You must be signed in to change notification settings - Fork 24.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Better validation of mapping JSON #7534
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,6 +28,8 @@ | |
import org.elasticsearch.common.collect.Tuple; | ||
import org.elasticsearch.common.compress.CompressedString; | ||
import org.elasticsearch.common.geo.ShapesAvailability; | ||
import org.elasticsearch.common.logging.ESLogger; | ||
import org.elasticsearch.common.logging.Loggers; | ||
import org.elasticsearch.common.settings.Settings; | ||
import org.elasticsearch.common.xcontent.XContentFactory; | ||
import org.elasticsearch.common.xcontent.XContentHelper; | ||
|
@@ -37,10 +39,37 @@ | |
import org.elasticsearch.index.analysis.NamedAnalyzer; | ||
import org.elasticsearch.index.codec.docvaluesformat.DocValuesFormatService; | ||
import org.elasticsearch.index.codec.postingsformat.PostingsFormatService; | ||
import org.elasticsearch.index.mapper.core.*; | ||
import org.elasticsearch.index.mapper.core.BinaryFieldMapper; | ||
import org.elasticsearch.index.mapper.core.BooleanFieldMapper; | ||
import org.elasticsearch.index.mapper.core.ByteFieldMapper; | ||
import org.elasticsearch.index.mapper.core.CompletionFieldMapper; | ||
import org.elasticsearch.index.mapper.core.DateFieldMapper; | ||
import org.elasticsearch.index.mapper.core.DoubleFieldMapper; | ||
import org.elasticsearch.index.mapper.core.FloatFieldMapper; | ||
import org.elasticsearch.index.mapper.core.IntegerFieldMapper; | ||
import org.elasticsearch.index.mapper.core.LongFieldMapper; | ||
import org.elasticsearch.index.mapper.core.Murmur3FieldMapper; | ||
import org.elasticsearch.index.mapper.core.ShortFieldMapper; | ||
import org.elasticsearch.index.mapper.core.StringFieldMapper; | ||
import org.elasticsearch.index.mapper.core.TokenCountFieldMapper; | ||
import org.elasticsearch.index.mapper.core.TypeParsers; | ||
import org.elasticsearch.index.mapper.geo.GeoPointFieldMapper; | ||
import org.elasticsearch.index.mapper.geo.GeoShapeFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.*; | ||
import org.elasticsearch.index.mapper.internal.AllFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.AnalyzerMapper; | ||
import org.elasticsearch.index.mapper.internal.BoostFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.FieldNamesFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.IdFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.IndexFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.RoutingFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.SizeFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.SourceFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.TTLFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.TimestampFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.TypeFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.UidFieldMapper; | ||
import org.elasticsearch.index.mapper.internal.VersionFieldMapper; | ||
import org.elasticsearch.index.mapper.ip.IpFieldMapper; | ||
import org.elasticsearch.index.mapper.object.ObjectMapper; | ||
import org.elasticsearch.index.mapper.object.RootObjectMapper; | ||
|
@@ -63,6 +92,7 @@ | |
public class DocumentMapperParser extends AbstractIndexComponent { | ||
|
||
final AnalysisService analysisService; | ||
private static final ESLogger logger = Loggers.getLogger(DocumentMapperParser.class); | ||
private final PostingsFormatService postingsFormatService; | ||
private final DocValuesFormatService docValuesFormatService; | ||
private final SimilarityLookupService similarityLookupService; | ||
|
@@ -248,13 +278,13 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de | |
} else if ("transform".equals(fieldName)) { | ||
iterator.remove(); | ||
if (fieldNode instanceof Map) { | ||
parseTransform(docBuilder, (Map<String, Object>) fieldNode); | ||
parseTransform(docBuilder, (Map<String, Object>) fieldNode, parserContext.indexVersionCreated()); | ||
} else if (fieldNode instanceof List) { | ||
for (Object transformItem: (List)fieldNode) { | ||
if (!(transformItem instanceof Map)) { | ||
throw new MapperParsingException("Elements of transform list must be objects but one was: " + fieldNode); | ||
} | ||
parseTransform(docBuilder, (Map<String, Object>) transformItem); | ||
parseTransform(docBuilder, (Map<String, Object>) transformItem, parserContext.indexVersionCreated()); | ||
} | ||
} else { | ||
throw new MapperParsingException("Transform must be an object or an array but was: " + fieldNode); | ||
|
@@ -263,7 +293,10 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de | |
Mapper.TypeParser typeParser = rootTypeParsers.get(fieldName); | ||
if (typeParser != null) { | ||
iterator.remove(); | ||
docBuilder.put(typeParser.parse(fieldName, (Map<String, Object>) fieldNode, parserContext)); | ||
Map<String, Object> fieldNodeMap = (Map<String, Object>) fieldNode; | ||
docBuilder.put(typeParser.parse(fieldName, fieldNodeMap, parserContext)); | ||
fieldNodeMap.remove("type"); | ||
checkNoRemainingFields(fieldName, fieldNodeMap, parserContext.indexVersionCreated()); | ||
} | ||
} | ||
} | ||
|
@@ -274,9 +307,8 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de | |
} | ||
docBuilder.meta(attributes); | ||
|
||
if (!mapping.isEmpty()) { | ||
throw new MapperParsingException("Root type mapping not empty after parsing! Remaining fields: " + getRemainingFields(mapping)); | ||
} | ||
checkNoRemainingFields(mapping, parserContext.indexVersionCreated(), "Root mapping definition has unsupported parameters: "); | ||
|
||
if (!docBuilder.hasIndexAnalyzer()) { | ||
docBuilder.indexAnalyzer(analysisService.defaultIndexAnalyzer()); | ||
} | ||
|
@@ -293,16 +325,30 @@ private DocumentMapper parse(String type, Map<String, Object> mapping, String de | |
return documentMapper; | ||
} | ||
|
||
private String getRemainingFields(Map<String, ?> map) { | ||
public static void checkNoRemainingFields(String fieldName, Map<String, Object> fieldNodeMap, Version indexVersionCreated) { | ||
checkNoRemainingFields(fieldNodeMap, indexVersionCreated, "Mapping definition for [" + fieldName + "] has unsupported parameters: "); | ||
} | ||
|
||
public static void checkNoRemainingFields(Map<String, Object> fieldNodeMap, Version indexVersionCreated, String message) { | ||
if (!fieldNodeMap.isEmpty()) { | ||
if (indexVersionCreated.onOrAfter(Version.V_2_0_0)) { | ||
throw new MapperParsingException(message + getRemainingFields(fieldNodeMap)); | ||
} else { | ||
logger.debug(message + "{}", getRemainingFields(fieldNodeMap)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or just |
||
} | ||
} | ||
} | ||
|
||
private static String getRemainingFields(Map<String, ?> map) { | ||
StringBuilder remainingFields = new StringBuilder(); | ||
for (String key : map.keySet()) { | ||
remainingFields.append(" [").append(key).append(" : ").append(map.get(key).toString()).append("]"); | ||
remainingFields.append(" [").append(key).append(" : ").append(map.get(key)).append("]"); | ||
} | ||
return remainingFields.toString(); | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private void parseTransform(DocumentMapper.Builder docBuilder, Map<String, Object> transformConfig) { | ||
private void parseTransform(DocumentMapper.Builder docBuilder, Map<String, Object> transformConfig, Version indexVersionCreated) { | ||
ScriptParameterParser scriptParameterParser = new ScriptParameterParser(); | ||
scriptParameterParser.parseConfig(transformConfig, true); | ||
|
||
|
@@ -319,9 +365,7 @@ private void parseTransform(DocumentMapper.Builder docBuilder, Map<String, Objec | |
Map<String, Object> params = (Map<String, Object>)transformConfig.remove("params"); | ||
docBuilder.transform(scriptService, script, scriptType, scriptLang, params); | ||
} | ||
if (!transformConfig.isEmpty()) { | ||
throw new MapperParsingException("Unrecognized parameter in transform config: " + getRemainingFields(transformConfig)); | ||
} | ||
checkNoRemainingFields(transformConfig, indexVersionCreated, "Transform config has unsupported parameters: "); | ||
} | ||
|
||
private Tuple<String, Map<String, Object>> extractMapping(String type, String source) throws MapperParsingException { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only
_field_names
actually builds the content type and I wonder if it would not be better to remove the"type"
parameter in_field_names
parser instead of doing it here for all root mappers. Else it might look like you can define a type for different root mappers:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type parameter is used in a few other places, including the nested mapping. My thinking was that it's the DocumentMapperParser that actually parses the type (since it uses it to work out the correct type parser to use, so it should remove it. Otherwise, every implementation of the Tye Mappers would need to remove it themselves since if its not removed from the map, the map won't be empty and we will throw an exception saying 'type' has not been parsed.
Hope that makes sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you extract this "type" string to a constant? (and replace other references to this string to a reference to the constant)