parent
b9a7db554a
commit
6a66882e46
|
@ -75,43 +75,40 @@ class DocumentParser implements Closeable {
|
|||
this.docMapper = docMapper;
|
||||
}
|
||||
|
||||
public ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException {
|
||||
if (docMapper.type().equals(MapperService.DEFAULT_MAPPING)) {
|
||||
throw new IllegalArgumentException("It is forbidden to index into the default mapping [" + MapperService.DEFAULT_MAPPING + "]");
|
||||
}
|
||||
final ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException {
|
||||
validateType(source);
|
||||
|
||||
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());
|
||||
|
||||
XContentParser parser = source.parser();
|
||||
final Mapping mapping = docMapper.mapping();
|
||||
final ParseContext.InternalParseContext context = cache.get();
|
||||
XContentParser parser = null;
|
||||
try {
|
||||
if (parser == null) {
|
||||
parser = XContentHelper.createParser(source.source());
|
||||
}
|
||||
parser = parser(source);
|
||||
context.reset(parser, new ParseContext.Document(), source);
|
||||
|
||||
// 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");
|
||||
validateStart(parser);
|
||||
internalParseDocument(mapping, context, parser);
|
||||
validateEnd(source, parser);
|
||||
} catch (Throwable t) {
|
||||
throw wrapInMapperParsingException(source, t);
|
||||
} finally {
|
||||
// only close the parser when its not provided externally
|
||||
if (internalParser(source, parser)) {
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
|
||||
boolean emptyDoc = false;
|
||||
if (mapping.root.isEnabled()) {
|
||||
token = parser.nextToken();
|
||||
if (token == XContentParser.Token.END_OBJECT) {
|
||||
// empty doc, we can handle it...
|
||||
emptyDoc = 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");
|
||||
}
|
||||
reverseOrder(context);
|
||||
applyDocBoost(context);
|
||||
|
||||
ParsedDocument doc = parsedDocument(source, context, update(context, mapping));
|
||||
// reset the context to free up memory
|
||||
context.reset(null, null, null);
|
||||
return doc;
|
||||
}
|
||||
|
||||
private static void internalParseDocument(Mapping mapping, ParseContext.InternalParseContext context, XContentParser parser) throws IOException {
|
||||
final boolean emptyDoc = isEmptyDoc(mapping, parser);
|
||||
|
||||
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
||||
metadataMapper.preParse(context);
|
||||
}
|
||||
|
@ -129,39 +126,67 @@ class DocumentParser implements Closeable {
|
|||
for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) {
|
||||
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
|
||||
// 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();
|
||||
if (token != null) {
|
||||
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.
|
||||
if (source.source() != null && source.source().length() == 0) {
|
||||
throw new MapperParsingException("failed to parse, document is empty");
|
||||
private static boolean isEmptyDoc(Mapping mapping, XContentParser parser) throws IOException {
|
||||
if (mapping.root.isEnabled()) {
|
||||
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);
|
||||
} finally {
|
||||
// only close the parser when its not provided externally
|
||||
if (source.parser() == null && parser != null) {
|
||||
parser.close();
|
||||
}
|
||||
}
|
||||
private static void reverseOrder(ParseContext.InternalParseContext context) {
|
||||
// reverse the order of docs for nested docs support, parent should be last
|
||||
if (context.docs().size() > 1) {
|
||||
Collections.reverse(context.docs());
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyDocBoost(ParseContext.InternalParseContext context) {
|
||||
// apply doc boost
|
||||
if (context.docBoost() != 1.0f) {
|
||||
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(),
|
||||
context.source(), update).parent(source.parent());
|
||||
// reset the context to free up memory
|
||||
context.reset(null, null, null);
|
||||
return doc;
|
||||
private static ParsedDocument parsedDocument(SourceToParse source, ParseContext.InternalParseContext context, Mapping update) {
|
||||
return new ParsedDocument(
|
||||
context.uid(),
|
||||
context.version(),
|
||||
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 {
|
||||
|
|
Loading…
Reference in New Issue