parent
b9a7db554a
commit
6a66882e46
|
@ -75,43 +75,40 @@ class DocumentParser implements Closeable {
|
||||||
this.docMapper = docMapper;
|
this.docMapper = docMapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException {
|
final ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException {
|
||||||
if (docMapper.type().equals(MapperService.DEFAULT_MAPPING)) {
|
validateType(source);
|
||||||
throw new IllegalArgumentException("It is forbidden to index into the default mapping [" + MapperService.DEFAULT_MAPPING + "]");
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseContext.InternalParseContext context = cache.get();
|
|
||||||
|
|
||||||
final Mapping mapping = docMapper.mapping();
|
|
||||||
if (source.type() != null && !source.type().equals(docMapper.type())) {
|
|
||||||
throw new MapperParsingException("Type mismatch, provide type [" + source.type() + "] but mapper is of type [" + docMapper.type() + "]");
|
|
||||||
}
|
|
||||||
source.type(docMapper.type());
|
source.type(docMapper.type());
|
||||||
|
final Mapping mapping = docMapper.mapping();
|
||||||
XContentParser parser = source.parser();
|
final ParseContext.InternalParseContext context = cache.get();
|
||||||
|
XContentParser parser = null;
|
||||||
try {
|
try {
|
||||||
if (parser == null) {
|
parser = parser(source);
|
||||||
parser = XContentHelper.createParser(source.source());
|
|
||||||
}
|
|
||||||
context.reset(parser, new ParseContext.Document(), source);
|
context.reset(parser, new ParseContext.Document(), source);
|
||||||
|
validateStart(parser);
|
||||||
// will result in START_OBJECT
|
internalParseDocument(mapping, context, parser);
|
||||||
XContentParser.Token token = parser.nextToken();
|
validateEnd(source, parser);
|
||||||
if (token != XContentParser.Token.START_OBJECT) {
|
} catch (Throwable t) {
|
||||||
throw new MapperParsingException("Malformed content, must start with an object");
|
throw wrapInMapperParsingException(source, t);
|
||||||
|
} finally {
|
||||||
|
// only close the parser when its not provided externally
|
||||||
|
if (internalParser(source, parser)) {
|
||||||
|
parser.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean emptyDoc = false;
|
reverseOrder(context);
|
||||||
if (mapping.root.isEnabled()) {
|
applyDocBoost(context);
|
||||||
token = parser.nextToken();
|
|
||||||
if (token == XContentParser.Token.END_OBJECT) {
|
ParsedDocument doc = parsedDocument(source, context, update(context, mapping));
|
||||||
// empty doc, we can handle it...
|
// reset the context to free up memory
|
||||||
emptyDoc = true;
|
context.reset(null, null, null);
|
||||||
} else if (token != XContentParser.Token.FIELD_NAME) {
|
return doc;
|
||||||
throw new MapperParsingException("Malformed content, after first object, either the type field or the actual properties should exist");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void internalParseDocument(Mapping mapping, ParseContext.InternalParseContext context, XContentParser parser) throws IOException {
|
||||||
|
final boolean emptyDoc = isEmptyDoc(mapping, parser);
|
||||||
|
|
||||||
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
||||||
metadataMapper.preParse(context);
|
metadataMapper.preParse(context);
|
||||||
}
|
}
|
||||||
|
@ -129,39 +126,67 @@ class DocumentParser implements Closeable {
|
||||||
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
||||||
metadataMapper.postParse(context);
|
metadataMapper.postParse(context);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateType(SourceToParse source) {
|
||||||
|
if (docMapper.type().equals(MapperService.DEFAULT_MAPPING)) {
|
||||||
|
throw new IllegalArgumentException("It is forbidden to index into the default mapping [" + MapperService.DEFAULT_MAPPING + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.type() != null && !source.type().equals(docMapper.type())) {
|
||||||
|
throw new MapperParsingException("Type mismatch, provide type [" + source.type() + "] but mapper is of type [" + docMapper.type() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static XContentParser parser(SourceToParse source) throws IOException {
|
||||||
|
return source.parser() == null ? XContentHelper.createParser(source.source()) : source.parser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean internalParser(SourceToParse source, XContentParser parser) {
|
||||||
|
return source.parser() == null && parser != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateStart(XContentParser parser) throws IOException {
|
||||||
|
// will result in START_OBJECT
|
||||||
|
XContentParser.Token token = parser.nextToken();
|
||||||
|
if (token != XContentParser.Token.START_OBJECT) {
|
||||||
|
throw new MapperParsingException("Malformed content, must start with an object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateEnd(SourceToParse source, XContentParser parser) throws IOException {
|
||||||
|
XContentParser.Token token;// only check for end of tokens if we created the parser here
|
||||||
|
if (internalParser(source, parser)) {
|
||||||
// try to parse the next token, this should be null if the object is ended properly
|
// try to parse the next token, this should be null if the object is ended properly
|
||||||
// but will throw a JSON exception if the extra tokens is not valid JSON (this will be handled by the catch)
|
// but will throw a JSON exception if the extra tokens is not valid JSON (this will be handled by the catch)
|
||||||
if (source.parser() == null && parser != null) {
|
|
||||||
// only check for end of tokens if we created the parser here
|
|
||||||
token = parser.nextToken();
|
token = parser.nextToken();
|
||||||
if (token != null) {
|
if (token != null) {
|
||||||
throw new IllegalArgumentException("Malformed content, found extra data after parsing: " + token);
|
throw new IllegalArgumentException("Malformed content, found extra data after parsing: " + token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Throwable e) {
|
|
||||||
// if its already a mapper parsing exception, no need to wrap it...
|
|
||||||
if (e instanceof MapperParsingException) {
|
|
||||||
throw (MapperParsingException) e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throw a more meaningful message if the document is empty.
|
private static boolean isEmptyDoc(Mapping mapping, XContentParser parser) throws IOException {
|
||||||
if (source.source() != null && source.source().length() == 0) {
|
if (mapping.root.isEnabled()) {
|
||||||
throw new MapperParsingException("failed to parse, document is empty");
|
final XContentParser.Token token = parser.nextToken();
|
||||||
|
if (token == XContentParser.Token.END_OBJECT) {
|
||||||
|
// empty doc, we can handle it...
|
||||||
|
return true;
|
||||||
|
} else if (token != XContentParser.Token.FIELD_NAME) {
|
||||||
|
throw new MapperParsingException("Malformed content, after first object, either the type field or the actual properties should exist");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MapperParsingException("failed to parse", e);
|
private static void reverseOrder(ParseContext.InternalParseContext context) {
|
||||||
} finally {
|
|
||||||
// only close the parser when its not provided externally
|
|
||||||
if (source.parser() == null && parser != null) {
|
|
||||||
parser.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// reverse the order of docs for nested docs support, parent should be last
|
// reverse the order of docs for nested docs support, parent should be last
|
||||||
if (context.docs().size() > 1) {
|
if (context.docs().size() > 1) {
|
||||||
Collections.reverse(context.docs());
|
Collections.reverse(context.docs());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyDocBoost(ParseContext.InternalParseContext context) {
|
||||||
// apply doc boost
|
// apply doc boost
|
||||||
if (context.docBoost() != 1.0f) {
|
if (context.docBoost() != 1.0f) {
|
||||||
Set<String> encounteredFields = new HashSet<>();
|
Set<String> encounteredFields = new HashSet<>();
|
||||||
|
@ -177,18 +202,41 @@ class DocumentParser implements Closeable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mapper rootDynamicUpdate = context.dynamicMappingsUpdate();
|
|
||||||
Mapping update = null;
|
|
||||||
if (rootDynamicUpdate != null) {
|
|
||||||
update = mapping.mappingUpdate(rootDynamicUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ParsedDocument doc = new ParsedDocument(context.uid(), context.version(), context.id(), context.type(), source.routing(), source.timestamp(), source.ttl(), context.docs(),
|
private static ParsedDocument parsedDocument(SourceToParse source, ParseContext.InternalParseContext context, Mapping update) {
|
||||||
context.source(), update).parent(source.parent());
|
return new ParsedDocument(
|
||||||
// reset the context to free up memory
|
context.uid(),
|
||||||
context.reset(null, null, null);
|
context.version(),
|
||||||
return doc;
|
context.id(),
|
||||||
|
context.type(),
|
||||||
|
source.routing(),
|
||||||
|
source.timestamp(),
|
||||||
|
source.ttl(),
|
||||||
|
context.docs(),
|
||||||
|
context.source(),
|
||||||
|
update
|
||||||
|
).parent(source.parent());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Mapping update(ParseContext.InternalParseContext context, Mapping mapping) {
|
||||||
|
Mapper rootDynamicUpdate = context.dynamicMappingsUpdate();
|
||||||
|
return rootDynamicUpdate != null ? mapping.mappingUpdate(rootDynamicUpdate) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MapperParsingException wrapInMapperParsingException(SourceToParse source, Throwable e) {
|
||||||
|
// if its already a mapper parsing exception, no need to wrap it...
|
||||||
|
if (e instanceof MapperParsingException) {
|
||||||
|
return (MapperParsingException) e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw a more meaningful message if the document is empty.
|
||||||
|
if (source.source() != null && source.source().length() == 0) {
|
||||||
|
return new MapperParsingException("failed to parse, document is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MapperParsingException("failed to parse", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ObjectMapper parseObject(ParseContext context, ObjectMapper mapper, boolean atRoot) throws IOException {
|
static ObjectMapper parseObject(ParseContext context, ObjectMapper mapper, boolean atRoot) throws IOException {
|
||||||
|
|
Loading…
Reference in New Issue