Added parsing of erroneous field value (#42321)

This commit is contained in:
sandmannn 2019-06-20 19:19:19 +02:00 committed by Mayya Sharipova
parent 6e7a0e1b2a
commit cf610b5e81
4 changed files with 140 additions and 7 deletions

View File

@ -289,7 +289,7 @@ public abstract class AbstractXContentParser implements XContentParser {
return readListOrderedMap(this);
}
interface MapFactory {
public interface MapFactory {
Map<String, Object> newMap();
}
@ -391,7 +391,7 @@ public abstract class AbstractXContentParser implements XContentParser {
return list;
}
static Object readValue(XContentParser parser, MapFactory mapFactory, XContentParser.Token token) throws IOException {
public static Object readValue(XContentParser parser, MapFactory mapFactory, XContentParser.Token token) throws IOException {
if (token == XContentParser.Token.VALUE_NULL) {
return null;
} else if (token == XContentParser.Token.VALUE_STRING) {

View File

@ -33,6 +33,8 @@ import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.support.AbstractXContentParser;
import org.elasticsearch.index.analysis.NamedAnalyzer;
import org.elasticsearch.index.mapper.FieldNamesFieldMapper.FieldNamesFieldType;
import org.elasticsearch.index.similarity.SimilarityProvider;
@ -46,6 +48,7 @@ import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.util.Objects;
import java.util.stream.StreamSupport;
@ -276,14 +279,33 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
context.doc().add(field);
}
} catch (Exception e) {
throw new MapperParsingException("failed to parse field [{}] of type [{}] in document with id '{}'", e, fieldType().name(),
fieldType().typeName(), context.sourceToParse().id());
String valuePreview = "";
try {
XContentParser parser = context.parser();
Object complexValue = AbstractXContentParser.readValue(parser, HashMap::new, parser.currentToken());
if (complexValue == null) {
valuePreview = "null";
} else {
valuePreview = complexValue.toString();
}
} catch (Exception innerException) {
throw new MapperParsingException("failed to parse field [{}] of type [{}] in document with id '{}'. " +
"Could not parse field value preview,",
e, fieldType().name(), fieldType().typeName(), context.sourceToParse().id());
}
throw new MapperParsingException("failed to parse field [{}] of type [{}] in document with id '{}'. " +
"Preview of field's value: '{}'", e, fieldType().name(), fieldType().typeName(),
context.sourceToParse().id(), valuePreview);
}
multiFields.parse(this, context);
}
/**
* Parse the field value and populate <code>fields</code>.
*
* Implementations of this method should ensure that on failing to parse parser.currentToken() must be the
* current failing token
*/
protected abstract void parseCreateField(ParseContext context, List<IndexableField> fields) throws IOException;

View File

@ -135,16 +135,48 @@ public class BooleanFieldMapperTests extends ESSingleNodeTestCase {
.endObject()
.endObject());
DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
// omit "false"/"true" here as they should still be parsed correctly
String randomValue = randomFrom("off", "no", "0", "on", "yes", "1");
BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder()
.startObject()
// omit "false"/"true" here as they should still be parsed correctly
.field("field", randomFrom("off", "no", "0", "on", "yes", "1"))
.field("field", randomValue)
.endObject());
MapperParsingException ex = expectThrows(MapperParsingException.class,
() -> defaultMapper.parse(new SourceToParse("test", "type", "1", source, XContentType.JSON)));
assertEquals("failed to parse field [field] of type [boolean] in document with id '1'", ex.getMessage());
assertEquals("failed to parse field [field] of type [boolean] in document with id '1'. " +
"Preview of field's value: '" + randomValue + "'", ex.getMessage());
}
public void testParsesBooleansNestedStrict() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder()
.startObject()
.startObject("type")
.startObject("properties")
.startObject("field")
.field("type", "boolean")
.endObject()
.endObject()
.endObject()
.endObject());
DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
// omit "false"/"true" here as they should still be parsed correctly
String randomValue = "no";
BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("field")
.field("inner_field", randomValue)
.endObject()
.endObject());
MapperParsingException ex = expectThrows(MapperParsingException.class,
() -> defaultMapper.parse(new SourceToParse("test", "type", "1", source, XContentType.JSON)));
assertEquals("failed to parse field [field] of type [boolean] in document with id '1'. " +
"Preview of field's value: '{inner_field=" + randomValue + "}'", ex.getMessage());
}
public void testMultiFields() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties")

View File

@ -386,6 +386,85 @@ public class KeywordFieldMapperTests extends ESSingleNodeTestCase {
assertEquals(DocValuesType.SORTED_SET, fieldType.docValuesType());
}
public void testParsesKeywordNestedEmptyObjectStrict() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder()
.startObject()
.startObject("type")
.startObject("properties")
.startObject("field")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.endObject());
DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("field")
.endObject()
.endObject());
MapperParsingException ex = expectThrows(MapperParsingException.class,
() -> defaultMapper.parse(new SourceToParse("test", "type", "1", source, XContentType.JSON)));
assertEquals("failed to parse field [field] of type [keyword] in document with id '1'. " +
"Preview of field's value: '{}'", ex.getMessage());
}
public void testParsesKeywordNestedListStrict() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder()
.startObject()
.startObject("type")
.startObject("properties")
.startObject("field")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.endObject());
DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder()
.startObject()
.startArray("field")
.startObject()
.startArray("array_name")
.value("inner_field_first")
.value("inner_field_second")
.endArray()
.endObject()
.endArray()
.endObject());
MapperParsingException ex = expectThrows(MapperParsingException.class,
() -> defaultMapper.parse(new SourceToParse("test", "type", "1", source, XContentType.JSON)));
assertEquals("failed to parse field [field] of type [keyword] in document with id '1'. " +
"Preview of field's value: '{array_name=[inner_field_first, inner_field_second]}'", ex.getMessage());
}
public void testParsesKeywordNullStrict() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder()
.startObject()
.startObject("type")
.startObject("properties")
.startObject("field")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.endObject());
DocumentMapper defaultMapper = parser.parse("type", new CompressedXContent(mapping));
BytesReference source = BytesReference.bytes(XContentFactory.jsonBuilder()
.startObject()
.startObject("field")
.nullField("field_name")
.endObject()
.endObject());
MapperParsingException ex = expectThrows(MapperParsingException.class,
() -> defaultMapper.parse(new SourceToParse("test", "type", "1", source, XContentType.JSON)));
assertEquals("failed to parse field [field] of type [keyword] in document with id '1'. " +
"Preview of field's value: '{field_name=null}'", ex.getMessage());
}
public void testUpdateNormalizer() throws IOException {
String mapping = Strings.toString(XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field")