public void toXContent( XContentBuilder builder, Params params, ToXContent custom, Mapper... additionalMappers) throws IOException { builder.startObject(name); if (nested.isNested()) { builder.field("type", NESTED_CONTENT_TYPE); if (nested.isIncludeInParent()) { builder.field("include_in_parent", true); } if (nested.isIncludeInRoot()) { builder.field("include_in_root", true); } } else if (mappers .isEmpty()) { // only write the object content type if there are no properties, otherwise, // it is automatically detected builder.field("type", CONTENT_TYPE); } // grr, ugly! on root, dynamic defaults to TRUE, on children, it defaults to null to // inherit the root behavior if (this instanceof RootObjectMapper) { if (dynamic != Dynamic.TRUE) { builder.field("dynamic", dynamic.name().toLowerCase()); } } else { if (dynamic != Defaults.DYNAMIC) { builder.field("dynamic", dynamic.name().toLowerCase()); } } if (enabled != Defaults.ENABLED) { builder.field("enabled", enabled); } if (pathType != Defaults.PATH_TYPE) { builder.field("path", pathType.name().toLowerCase()); } if (includeInAll != null) { builder.field("include_in_all", includeInAll); } if (custom != null) { custom.toXContent(builder, params); } doXContent(builder, params); // sort the mappers so we get consistent serialization format TreeMap<String, Mapper> sortedMappers = new TreeMap<String, Mapper>(mappers); // check internal mappers first (this is only relevant for root object) for (Mapper mapper : sortedMappers.values()) { if (mapper instanceof InternalMapper) { mapper.toXContent(builder, params); } } if (additionalMappers != null && additionalMappers.length > 0) { TreeMap<String, Mapper> additionalSortedMappers = new TreeMap<String, Mapper>(); for (Mapper mapper : additionalMappers) { additionalSortedMappers.put(mapper.name(), mapper); } for (Mapper mapper : additionalSortedMappers.values()) { mapper.toXContent(builder, params); } } if (!mappers.isEmpty()) { builder.startObject("properties"); for (Mapper mapper : sortedMappers.values()) { if (!(mapper instanceof InternalMapper)) { mapper.toXContent(builder, params); } } builder.endObject(); } builder.endObject(); }
public void parse(ParseContext context) throws IOException { if (!enabled) { context.parser().skipChildren(); return; } XContentParser parser = context.parser(); String currentFieldName = parser.currentName(); XContentParser.Token token = parser.currentToken(); if (token == XContentParser.Token.VALUE_NULL) { // the object is null ("obj1" : null), simply bail return; } Document restoreDoc = null; if (nested.isNested()) { Document nestedDoc = new Document(); // pre add the uid field if possible (id was already provided) Fieldable uidField = context.doc().getFieldable(UidFieldMapper.NAME); if (uidField != null) { // we don't need to add it as a full uid field in nested docs, since we don't need // versioning // we also rely on this for UidField#loadVersion // this is a deeply nested field if (uidField.stringValue() != null) { nestedDoc.add( new Field( UidFieldMapper.NAME, uidField.stringValue(), Field.Store.NO, Field.Index.NOT_ANALYZED)); } else { nestedDoc.add( new Field( UidFieldMapper.NAME, ((UidField) uidField).uid(), Field.Store.NO, Field.Index.NOT_ANALYZED)); } } // the type of the nested doc starts with __, so we can identify that its a nested one in // filters // note, we don't prefix it with the type of the doc since it allows us to execute a nested // query // across types (for example, with similar nested objects) nestedDoc.add( new Field( TypeFieldMapper.NAME, nestedTypePath, Field.Store.NO, Field.Index.NOT_ANALYZED)); restoreDoc = context.switchDoc(nestedDoc); context.addDoc(nestedDoc); } ContentPath.Type origPathType = context.path().pathType(); context.path().pathType(pathType); // if we are at the end of the previous object, advance if (token == XContentParser.Token.END_OBJECT) { token = parser.nextToken(); } if (token == XContentParser.Token.START_OBJECT) { // if we are just starting an OBJECT, advance, this is the object we are parsing, we need the // name first token = parser.nextToken(); } while (token != XContentParser.Token.END_OBJECT) { if (token == XContentParser.Token.START_OBJECT) { serializeObject(context, currentFieldName); } else if (token == XContentParser.Token.START_ARRAY) { serializeArray(context, currentFieldName); } else if (token == XContentParser.Token.FIELD_NAME) { currentFieldName = parser.currentName(); } else if (token == XContentParser.Token.VALUE_NULL) { serializeNullValue(context, currentFieldName); } else if (token == null) { throw new MapperParsingException( "object mapping for [" + name + "] tried to parse as object, but got EOF, has a concrete value been provided to it?"); } else if (token.isValue()) { serializeValue(context, currentFieldName, token); } token = parser.nextToken(); } // restore the enable path flag context.path().pathType(origPathType); if (nested.isNested()) { Document nestedDoc = context.switchDoc(restoreDoc); if (nested.isIncludeInParent()) { for (Fieldable field : nestedDoc.getFields()) { if (field.name().equals(UidFieldMapper.NAME) || field.name().equals(TypeFieldMapper.NAME)) { continue; } else { context.doc().add(field); } } } if (nested.isIncludeInRoot()) { // don't add it twice, if its included in parent, and we are handling the master doc... if (!(nested.isIncludeInParent() && context.doc() == context.rootDoc())) { for (Fieldable field : nestedDoc.getFields()) { if (field.name().equals(UidFieldMapper.NAME) || field.name().equals(TypeFieldMapper.NAME)) { continue; } else { context.rootDoc().add(field); } } } } } }