From 6a66882e460fc9260541ab32995ea52b05fc21fb Mon Sep 17 00:00:00 2001 From: Jason Tedor Date: Sat, 13 Feb 2016 08:55:59 -0500 Subject: [PATCH] Inline DocumentParser#parseDocument Relates #16725 --- .../index/mapper/DocumentParser.java | 200 +++++++++++------- 1 file changed, 124 insertions(+), 76 deletions(-) diff --git a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java index b0cdb993b78..98721b4358a 100644 --- a/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java +++ b/core/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java @@ -75,93 +75,118 @@ class DocumentParser implements Closeable { this.docMapper = docMapper; } - public ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException { + final ParsedDocument parseDocument(SourceToParse source) throws MapperParsingException { + validateType(source); + + source.type(docMapper.type()); + final Mapping mapping = docMapper.mapping(); + final ParseContext.InternalParseContext context = cache.get(); + XContentParser parser = null; + try { + parser = parser(source); + context.reset(parser, new ParseContext.Document(), source); + 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(); + } + } + + 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); + } + + if (mapping.root.isEnabled() == false) { + // entire type is disabled + parser.skipChildren(); + } else if (emptyDoc == false) { + Mapper update = parseObject(context, mapping.root, true); + if (update != null) { + context.addDynamicMappingsUpdate(update); + } + } + + 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 + "]"); } - 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(); - try { - if (parser == null) { - parser = XContentHelper.createParser(source.source()); - } - context.reset(parser, new ParseContext.Document(), source); + private static XContentParser parser(SourceToParse source) throws IOException { + return source.parser() == null ? XContentHelper.createParser(source.source()) : source.parser(); + } - // 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 boolean internalParser(SourceToParse source, XContentParser parser) { + return source.parser() == null && parser != null; + } - 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"); - } - } - - for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) { - metadataMapper.preParse(context); - } - - if (mapping.root.isEnabled() == false) { - // entire type is disabled - parser.skipChildren(); - } else if (emptyDoc == false) { - Mapper update = parseObject(context, mapping.root, true); - if (update != null) { - context.addDynamicMappingsUpdate(update); - } - } - - for (MetadataFieldMapper metadataMapper : mapping.metadataMappers) { - metadataMapper.postParse(context); - } + 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"); - } - - 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(); + token = parser.nextToken(); + if (token != null) { + throw new IllegalArgumentException("Malformed content, found extra data after parsing: " + token); } } + } + + 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; + } + + 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 encounteredFields = new HashSet<>(); @@ -177,18 +202,41 @@ class DocumentParser implements Closeable { } } } + } + 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(); - Mapping update = null; - if (rootDynamicUpdate != null) { - update = mapping.mappingUpdate(rootDynamicUpdate); + 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; } - 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; + // 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 {