Bläddra i källkod

Revert "Make NestedObjectMapper it's own class (#73058)" (#74069)

This commit contains a bug when merging deeply-nested mappers which
was causing errors downstream. Reverting to unblock while the bug
is fixed.

This reverts commit 29ee4202a2a8e60ebbb9d3f9f47a777fd074ec9c.
Alan Woodward 4 år sedan
förälder
incheckning
6d52cd6b2a
41 ändrade filer med 403 tillägg och 496 borttagningar
  1. 1 1
      server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java
  2. 2 2
      server/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java
  3. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java
  4. 9 8
      server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java
  5. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/DynamicFieldsBuilder.java
  6. 1 0
      server/src/main/java/org/elasticsearch/index/mapper/MapperService.java
  7. 2 1
      server/src/main/java/org/elasticsearch/index/mapper/Mapping.java
  8. 12 12
      server/src/main/java/org/elasticsearch/index/mapper/MappingLookup.java
  9. 0 220
      server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java
  10. 171 29
      server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java
  11. 45 10
      server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java
  12. 10 17
      server/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java
  13. 1 2
      server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java
  14. 4 5
      server/src/main/java/org/elasticsearch/index/query/support/NestedScope.java
  15. 3 5
      server/src/main/java/org/elasticsearch/index/search/NestedHelper.java
  16. 1 2
      server/src/main/java/org/elasticsearch/indices/IndicesModule.java
  17. 5 5
      server/src/main/java/org/elasticsearch/search/NestedDocuments.java
  18. 3 4
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java
  19. 5 11
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregator.java
  20. 4 4
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorFactory.java
  21. 3 5
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java
  22. 3 2
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregator.java
  23. 3 3
      server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorFactory.java
  24. 9 6
      server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java
  25. 8 10
      server/src/main/java/org/elasticsearch/search/sort/SortBuilder.java
  26. 2 2
      server/src/test/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverServiceTests.java
  27. 1 1
      server/src/test/java/org/elasticsearch/cluster/action/index/MappingUpdatedActionTests.java
  28. 3 3
      server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java
  29. 2 1
      server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java
  30. 8 4
      server/src/test/java/org/elasticsearch/index/mapper/FieldAliasMapperValidationTests.java
  31. 4 3
      server/src/test/java/org/elasticsearch/index/mapper/MappingLookupTests.java
  32. 37 79
      server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java
  33. 20 17
      server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java
  34. 1 1
      server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java
  35. 2 3
      server/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java
  36. 3 3
      server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java
  37. 1 2
      test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java
  38. 4 4
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/Classification.java
  39. 2 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/OutlierDetection.java
  40. 2 2
      x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/Regression.java
  41. 2 3
      x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/extractor/ExtractedFieldsDetector.java

+ 1 - 1
server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesIndexAction.java

@@ -140,7 +140,7 @@ public class TransportFieldCapabilitiesIndexAction
                         if (searchExecutionContext.getFieldType(parentField) == null) {
                             // no field type, it must be an object field
                             ObjectMapper mapper = searchExecutionContext.getObjectMapper(parentField);
-                            String type = mapper.isNested() ? "nested" : "object";
+                            String type = mapper.nested().isNested() ? "nested" : "object";
                             IndexFieldCapabilities fieldCap = new IndexFieldCapabilities(parentField, type,
                                 false, false, false, Collections.emptyMap());
                             responseMap.put(parentField, fieldCap);

+ 2 - 2
server/src/main/java/org/elasticsearch/index/cache/bitset/BitsetFilterCache.java

@@ -38,7 +38,7 @@ import org.elasticsearch.index.IndexWarmer;
 import org.elasticsearch.index.IndexWarmer.TerminationHandle;
 import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.mapper.MappingLookup;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.shard.IndexShard;
 import org.elasticsearch.index.shard.ShardId;
 import org.elasticsearch.index.shard.ShardUtils;
@@ -232,7 +232,7 @@ public final class BitsetFilterCache extends AbstractIndexComponent
             MappingLookup lookup = mapperService.mappingLookup();
             if (lookup.hasNested()) {
                 warmUp.add(Queries.newNonNestedFilter());
-                lookup.getNestedParentMappers().stream().map(NestedObjectMapper::nestedTypeFilter).forEach(warmUp::add);
+                lookup.getNestedParentMappers().stream().map(ObjectMapper::nestedTypeFilter).forEach(warmUp::add);
             }
 
             final CountDownLatch latch = new CountDownLatch(reader.leaves().size() * warmUp.size());

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/DocumentMapper.java

@@ -8,6 +8,7 @@
 
 package org.elasticsearch.index.mapper;
 
+import org.elasticsearch.Version;
 import org.elasticsearch.common.compress.CompressedXContent;
 import org.elasticsearch.index.IndexSettings;
 
@@ -23,7 +24,7 @@ public class DocumentMapper {
      * @return the newly created document mapper
      */
     public static DocumentMapper createEmpty(MapperService mapperService) {
-        RootObjectMapper root = new RootObjectMapper.Builder(MapperService.SINGLE_MAPPING_NAME).build(new ContentPath(1));
+        RootObjectMapper root = new RootObjectMapper.Builder(MapperService.SINGLE_MAPPING_NAME, Version.CURRENT).build(new ContentPath(1));
         MetadataFieldMapper[] metadata = mapperService.getMetadataMappers().values().toArray(new MetadataFieldMapper[0]);
         Mapping mapping = new Mapping(root, metadata, null);
         return new DocumentMapper(mapperService.documentParser(), mapping);

+ 9 - 8
server/src/main/java/org/elasticsearch/index/mapper/DocumentParser.java

@@ -430,8 +430,9 @@ public final class DocumentParser {
                 + "] as object, but found a concrete value");
         }
 
-        if (mapper.isNested()) {
-            context = nestedContext(context, (NestedObjectMapper) mapper);
+        ObjectMapper.Nested nested = mapper.nested();
+        if (nested.isNested()) {
+            context = nestedContext(context, mapper);
         }
 
         // if we are at the end of the previous object, advance
@@ -445,8 +446,8 @@ public final class DocumentParser {
 
         innerParseObject(context, mapper, parser, currentFieldName, token);
         // restore the enable path flag
-        if (mapper.isNested()) {
-            nested(context, (NestedObjectMapper) mapper);
+        if (nested.isNested()) {
+            nested(context, nested);
         }
     }
 
@@ -478,7 +479,7 @@ public final class DocumentParser {
         }
     }
 
-    private static void nested(ParseContext context, NestedObjectMapper nested) {
+    private static void nested(ParseContext context, ObjectMapper.Nested nested) {
         ParseContext.Document nestedDoc = context.doc();
         ParseContext.Document parentDoc = nestedDoc.getParent();
         Version indexVersion = context.indexSettings().getIndexVersionCreated();
@@ -503,7 +504,7 @@ public final class DocumentParser {
         }
     }
 
-    private static ParseContext nestedContext(ParseContext context, NestedObjectMapper mapper) {
+    private static ParseContext nestedContext(ParseContext context, ObjectMapper mapper) {
         context = context.createNestedContext(mapper.fullPath());
         ParseContext.Document nestedDoc = context.doc();
         ParseContext.Document parentDoc = nestedDoc.getParent();
@@ -785,7 +786,7 @@ public final class DocumentParser {
                             context.sourceToParse().dynamicTemplates().get(currentPath) + "]");
                     }
                     mapper = (ObjectMapper) fieldMapper;
-                    if (mapper.isNested()) {
+                    if (mapper.nested() != ObjectMapper.Nested.NO) {
                         throw new MapperParsingException("It is forbidden to create dynamic nested objects (["
                             + currentPath + "]) through `copy_to` or dots in field names");
                     }
@@ -847,7 +848,7 @@ public final class DocumentParser {
                 return null;
             }
             objectMapper = (ObjectMapper)mapper;
-            if (objectMapper.isNested()) {
+            if (objectMapper.nested().isNested()) {
                 throw new MapperParsingException("Cannot add a value for field ["
                         + fieldName + "] since one of the intermediate objects is mapped as a nested object: ["
                         + mapper.name() + "]");

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/DynamicFieldsBuilder.java

@@ -126,7 +126,8 @@ final class DynamicFieldsBuilder {
     Mapper createDynamicObjectMapper(ParseContext context, String name) {
         //dynamic:runtime maps objects under properties, exactly like dynamic:true
         Mapper mapper = createObjectMapperFromTemplate(context, name);
-        return mapper != null ? mapper : new ObjectMapper.Builder(name).enabled(true).build(context.path());
+        return mapper != null ? mapper :
+            new ObjectMapper.Builder(name, context.indexSettings().getIndexVersionCreated()).enabled(true).build(context.path());
     }
 
     /**

+ 1 - 0
server/src/main/java/org/elasticsearch/index/mapper/MapperService.java

@@ -280,6 +280,7 @@ public class MapperService extends AbstractIndexComponent implements Closeable {
 
     private DocumentMapper newDocumentMapper(Mapping mapping, MergeReason reason) {
         DocumentMapper newMapper = new DocumentMapper(documentParser, mapping);
+        newMapper.mapping().getRoot().fixRedundantIncludes();
         newMapper.validate(indexSettings, reason != MergeReason.MAPPING_RECOVERY);
         return newMapper;
     }

+ 2 - 1
server/src/main/java/org/elasticsearch/index/mapper/Mapping.java

@@ -9,6 +9,7 @@
 package org.elasticsearch.index.mapper;
 
 import org.elasticsearch.ElasticsearchGenerationException;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.compress.CompressedXContent;
 import org.elasticsearch.common.xcontent.ToXContent;
@@ -35,7 +36,7 @@ import static java.util.Collections.unmodifiableMap;
 public final class Mapping implements ToXContentFragment {
 
     public static final Mapping EMPTY = new Mapping(
-        new RootObjectMapper.Builder("_doc").build(new ContentPath()), new MetadataFieldMapper[0], null);
+        new RootObjectMapper.Builder("_doc", Version.CURRENT).build(new ContentPath()), new MetadataFieldMapper[0], null);
 
     private final RootObjectMapper root;
     private final Map<String, Object> meta;

+ 12 - 12
server/src/main/java/org/elasticsearch/index/mapper/MappingLookup.java

@@ -129,7 +129,7 @@ public final class MappingLookup {
             if (objects.put(mapper.fullPath(), mapper) != null) {
                 throw new MapperParsingException("Object mapper [" + mapper.fullPath() + "] is defined more than once");
             }
-            if (mapper.isNested()) {
+            if (mapper.nested().isNested()) {
                 hasNested = true;
             }
         }
@@ -248,7 +248,7 @@ public final class MappingLookup {
     private void checkNestedLimit(long limit) {
         long actualNestedFields = 0;
         for (ObjectMapper objectMapper : objectMappers.values()) {
-            if (objectMapper.isNested()) {
+            if (objectMapper.nested().isNested()) {
                 actualNestedFields++;
             }
         }
@@ -277,7 +277,7 @@ public final class MappingLookup {
     public String getNestedScope(String path) {
         for (String parentPath = parentObject(path); parentPath != null; parentPath = parentObject(parentPath)) {
             ObjectMapper objectMapper = objectMappers.get(parentPath);
-            if (objectMapper != null && objectMapper.isNested()) {
+            if (objectMapper != null && objectMapper.nested().isNested()) {
                 return parentPath;
             }
         }
@@ -358,13 +358,13 @@ public final class MappingLookup {
     /**
      * Returns all nested object mappers
      */
-    public List<NestedObjectMapper> getNestedMappers() {
-        List<NestedObjectMapper> childMappers = new ArrayList<>();
+    public List<ObjectMapper> getNestedMappers() {
+        List<ObjectMapper> childMappers = new ArrayList<>();
         for (ObjectMapper mapper : objectMappers().values()) {
-            if (mapper.isNested() == false) {
+            if (mapper.nested().isNested() == false) {
                 continue;
             }
-            childMappers.add((NestedObjectMapper) mapper);
+            childMappers.add(mapper);
         }
         return childMappers;
     }
@@ -374,16 +374,16 @@ public final class MappingLookup {
      *
      * Used by BitSetProducerWarmer
      */
-    public List<NestedObjectMapper> getNestedParentMappers() {
-        List<NestedObjectMapper> parents = new ArrayList<>();
+    public List<ObjectMapper> getNestedParentMappers() {
+        List<ObjectMapper> parents = new ArrayList<>();
         for (ObjectMapper mapper : objectMappers().values()) {
             String nestedParentPath = getNestedParent(mapper.fullPath());
             if (nestedParentPath == null) {
                 continue;
             }
             ObjectMapper parent = objectMappers().get(nestedParentPath);
-            if (parent.isNested()) {
-                parents.add((NestedObjectMapper)parent);
+            if (parent.nested().isNested()) {
+                parents.add(parent);
             }
         }
         return parents;
@@ -410,7 +410,7 @@ public final class MappingLookup {
             if (mapper == null) {
                 return null;
             }
-            if (mapper.isNested()) {
+            if (mapper.nested().isNested()) {
                 return path;
             }
             if (path.contains(".") == false) {

+ 0 - 220
server/src/main/java/org/elasticsearch/index/mapper/NestedObjectMapper.java

@@ -1,220 +0,0 @@
-/*
- * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
- * or more contributor license agreements. Licensed under the Elastic License
- * 2.0 and the Server Side Public License, v 1; you may not use this file except
- * in compliance with, at your election, the Elastic License 2.0 or the Server
- * Side Public License, v 1.
- */
-
-package org.elasticsearch.index.mapper;
-
-import org.apache.lucene.search.Query;
-import org.elasticsearch.Version;
-import org.elasticsearch.common.Explicit;
-import org.elasticsearch.common.xcontent.XContentBuilder;
-import org.elasticsearch.common.xcontent.support.XContentMapValues;
-
-import java.io.IOException;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Locale;
-import java.util.Map;
-
-public class NestedObjectMapper extends ObjectMapper {
-
-    public static final String CONTENT_TYPE = "nested";
-
-    public static class Builder extends ObjectMapper.Builder {
-
-        private Explicit<Boolean> includeInRoot = new Explicit<>(false, false);
-        private Explicit<Boolean> includeInParent = new Explicit<>(false, false);
-        private final Version indexCreatedVersion;
-
-        public Builder(String name, Version indexCreatedVersion) {
-            super(name);
-            this.indexCreatedVersion = indexCreatedVersion;
-        }
-
-        Builder includeInRoot(boolean includeInRoot) {
-            this.includeInRoot = new Explicit<>(includeInRoot, true);
-            return this;
-        }
-
-        void includeInRoot(Explicit<Boolean> includeInRoot) {
-            this.includeInRoot = includeInRoot;
-        }
-
-        Builder includeInParent(boolean includeInParent) {
-            this.includeInParent = new Explicit<>(includeInParent, true);
-            return this;
-        }
-
-        void includeInParent(Explicit<Boolean> includeInParent) {
-            this.includeInParent = includeInParent;
-        }
-
-        @Override
-        public NestedObjectMapper build(ContentPath contentPath) {
-            fixRedundantIncludes(this, true);
-            return new NestedObjectMapper(name, contentPath.pathAsText(name), buildMappers(contentPath), this);
-        }
-
-        private static void fixRedundantIncludes(ObjectMapper.Builder objectMapper, boolean parentIncluded) {
-            for (Mapper.Builder builder : objectMapper.builders()) {
-                if (builder instanceof NestedObjectMapper.Builder) {
-                    NestedObjectMapper.Builder child = (NestedObjectMapper.Builder) builder;
-                    boolean includeInRootViaParent = parentIncluded && child.includeInParent.value();
-                    boolean includedInRoot = child.includeInRoot.value();
-                    if (includeInRootViaParent && includedInRoot) {
-                        child.includeInParent(true);
-                        child.includeInRoot(false);
-                    }
-                    fixRedundantIncludes(child, includeInRootViaParent || includedInRoot);
-                }
-            }
-        }
-    }
-
-    public static class TypeParser extends ObjectMapper.TypeParser {
-        @Override
-        public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
-            NestedObjectMapper.Builder builder = new NestedObjectMapper.Builder(name, parserContext.indexVersionCreated());
-            parseNested(name, node, builder);
-            for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
-                Map.Entry<String, Object> entry = iterator.next();
-                String fieldName = entry.getKey();
-                Object fieldNode = entry.getValue();
-                if (parseObjectOrDocumentTypeProperties(fieldName, fieldNode, parserContext, builder)) {
-                    iterator.remove();
-                }
-            }
-            return builder;
-        }
-
-        protected static void parseNested(String name, Map<String, Object> node, NestedObjectMapper.Builder builder) {
-            Object fieldNode = node.get("include_in_parent");
-            if (fieldNode != null) {
-                boolean includeInParent = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_parent");
-                builder.includeInParent(includeInParent);
-                node.remove("include_in_parent");
-            }
-            fieldNode = node.get("include_in_root");
-            if (fieldNode != null) {
-                boolean includeInRoot = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_root");
-                builder.includeInRoot(includeInRoot);
-                node.remove("include_in_root");
-            }
-        }
-    }
-
-    private final Explicit<Boolean> includeInRoot;
-    private final Explicit<Boolean> includeInParent;
-    private final String nestedTypePath;
-    private final Query nestedTypeFilter;
-    private final Version indexCreatedVersion;
-
-    NestedObjectMapper(
-        String name,
-        String fullPath,
-        Map<String, Mapper> mappers,
-        Builder builder
-    ) {
-        super(name, fullPath, builder.enabled, builder.dynamic, mappers);
-        if (builder.indexCreatedVersion.before(Version.V_8_0_0)) {
-            this.nestedTypePath = "__" + fullPath;
-        } else {
-            this.nestedTypePath = fullPath;
-        }
-        this.nestedTypeFilter = NestedPathFieldMapper.filter(builder.indexCreatedVersion, nestedTypePath);
-        this.includeInParent = builder.includeInParent;
-        this.includeInRoot = builder.includeInRoot;
-        this.indexCreatedVersion = builder.indexCreatedVersion;
-    }
-
-    public Query nestedTypeFilter() {
-        return this.nestedTypeFilter;
-    }
-
-    public String nestedTypePath() {
-        return this.nestedTypePath;
-    }
-
-    @Override
-    public boolean isNested() {
-        return true;
-    }
-
-    public boolean isIncludeInParent() {
-        return this.includeInParent.value();
-    }
-
-    public boolean isIncludeInRoot() {
-        return this.includeInRoot.value();
-    }
-
-    public Map<String, Mapper> getChildren() {
-        return Collections.unmodifiableMap(this.mappers);
-    }
-
-    @Override
-    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
-        builder.startObject(simpleName());
-        builder.field("type", CONTENT_TYPE);
-        if (includeInParent.explicit()) {
-            builder.field("include_in_parent", includeInParent.value());
-        }
-        if (includeInRoot.explicit()) {
-            builder.field("include_in_root", includeInRoot.value());
-        }
-        if (dynamic != null) {
-            builder.field("dynamic", dynamic.name().toLowerCase(Locale.ROOT));
-        }
-        if (isEnabled() != Defaults.ENABLED) {
-            builder.field("enabled", enabled.value());
-        }
-        serializeMappers(builder, params);
-        return builder.endObject();
-    }
-
-    @Override
-    public ObjectMapper merge(Mapper mergeWith, MapperService.MergeReason reason) {
-        if ((mergeWith instanceof NestedObjectMapper) == false) {
-            throw new IllegalArgumentException("can't merge a non nested mapping [" + mergeWith.name() + "] with a nested mapping");
-        }
-        NestedObjectMapper mergeWithObject = (NestedObjectMapper) mergeWith;
-        NestedObjectMapper.Builder builder = new NestedObjectMapper.Builder(simpleName(), indexCreatedVersion);
-        if (reason == MapperService.MergeReason.INDEX_TEMPLATE) {
-            if (mergeWithObject.includeInParent.explicit()) {
-                builder.includeInParent(mergeWithObject.includeInParent);
-            }
-            if (mergeWithObject.includeInRoot.explicit()) {
-                builder.includeInRoot(mergeWithObject.includeInRoot);
-            }
-        } else {
-            if (includeInParent.value() != mergeWithObject.includeInParent.value()) {
-                throw new MapperException("the [include_in_parent] parameter can't be updated on a nested object mapping");
-            }
-            builder.includeInParent(includeInParent);
-            if (includeInRoot.value() != mergeWithObject.includeInRoot.value()) {
-                throw new MapperException("the [include_in_root] parameter can't be updated on a nested object mapping");
-            }
-            builder.includeInRoot(includeInRoot);
-        }
-        for (Mapper child : mappers.values()) {
-            builder.add(new Mapper.Builder(child.name()) {
-                @Override
-                public Mapper build(ContentPath contentPath) {
-                    return child;
-                }
-            });
-        }
-        ContentPath contentPath = new ContentPath();
-        int lastDot = this.name().lastIndexOf(".");
-        if (lastDot != -1) {
-            contentPath.add(this.name().substring(0, lastDot));
-        }
-        NestedObjectMapper toMerge = builder.build(contentPath);
-        toMerge.doMerge(mergeWithObject, reason);
-        return toMerge;
-    }
-}

+ 171 - 29
server/src/main/java/org/elasticsearch/index/mapper/ObjectMapper.java

@@ -8,7 +8,9 @@
 
 package org.elasticsearch.index.mapper;
 
+import org.apache.lucene.search.Query;
 import org.elasticsearch.ElasticsearchParseException;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
 import org.elasticsearch.common.collect.CopyOnWriteHashMap;
 import org.elasticsearch.common.logging.DeprecationCategory;
@@ -33,9 +35,11 @@ public class ObjectMapper extends Mapper implements Cloneable {
     private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(ObjectMapper.class);
 
     public static final String CONTENT_TYPE = "object";
+    public static final String NESTED_CONTENT_TYPE = "nested";
 
     public static class Defaults {
         public static final boolean ENABLED = true;
+        public static final Nested NESTED = Nested.NO;
     }
 
     public enum Dynamic {
@@ -59,16 +63,91 @@ public class ObjectMapper extends Mapper implements Cloneable {
         };
     }
 
+    public static class Nested {
+
+        public static final Nested NO = new Nested(false, new Explicit<>(false, false), new Explicit<>(false, false));
+
+        public static Nested newNested() {
+            return new Nested(true, new Explicit<>(false, false), new Explicit<>(false, false));
+        }
+
+        public static Nested newNested(Explicit<Boolean> includeInParent, Explicit<Boolean> includeInRoot) {
+            return new Nested(true, includeInParent, includeInRoot);
+        }
+
+        private final boolean nested;
+        private Explicit<Boolean> includeInParent;
+        private Explicit<Boolean> includeInRoot;
+
+        private Nested(boolean nested, Explicit<Boolean> includeInParent, Explicit<Boolean> includeInRoot) {
+            this.nested = nested;
+            this.includeInParent = includeInParent;
+            this.includeInRoot = includeInRoot;
+        }
+
+        public void merge(Nested mergeWith, MergeReason reason) {
+            if (isNested()) {
+                if (mergeWith.isNested() == false) {
+                    throw new IllegalArgumentException("cannot change object mapping from nested to non-nested");
+                }
+            } else {
+                if (mergeWith.isNested()) {
+                    throw new IllegalArgumentException("cannot change object mapping from non-nested to nested");
+                }
+            }
+
+            if (reason == MergeReason.INDEX_TEMPLATE) {
+                if (mergeWith.includeInParent.explicit()) {
+                    includeInParent = mergeWith.includeInParent;
+                }
+                if (mergeWith.includeInRoot.explicit()) {
+                    includeInRoot = mergeWith.includeInRoot;
+                }
+            } else {
+                if (includeInParent.value() != mergeWith.includeInParent.value()) {
+                    throw new MapperException("the [include_in_parent] parameter can't be updated on a nested object mapping");
+                }
+                if (includeInRoot.value() != mergeWith.includeInRoot.value()) {
+                    throw new MapperException("the [include_in_root] parameter can't be updated on a nested object mapping");
+                }
+            }
+        }
+
+        public boolean isNested() {
+            return nested;
+        }
+
+        public boolean isIncludeInParent() {
+            return includeInParent.value();
+        }
+
+        public boolean isIncludeInRoot() {
+            return includeInRoot.value();
+        }
+
+        public void setIncludeInParent(boolean value) {
+            includeInParent = new Explicit<>(value, true);
+        }
+
+        public void setIncludeInRoot(boolean value) {
+            includeInRoot = new Explicit<>(value, true);
+        }
+    }
+
     public static class Builder extends Mapper.Builder {
 
         protected Explicit<Boolean> enabled = new Explicit<>(true, false);
 
+        protected Nested nested = Defaults.NESTED;
+
         protected Dynamic dynamic;
 
         protected final List<Mapper.Builder> mappersBuilders = new ArrayList<>();
+        protected final Version indexCreatedVersion;
 
-        public Builder(String name) {
+        public Builder(String name, Version indexCreatedVersion) {
             super(name);
+            this.indexCreatedVersion = indexCreatedVersion;
         }
 
         public Builder enabled(boolean enabled) {
@@ -81,17 +160,20 @@ public class ObjectMapper extends Mapper implements Cloneable {
             return this;
         }
 
-        public Builder add(Mapper.Builder builder) {
-            mappersBuilders.add(builder);
+        public Builder nested(Nested nested) {
+            this.nested = nested;
             return this;
         }
 
-        public List<Mapper.Builder> builders() {
-            return mappersBuilders;
+        public Builder add(Mapper.Builder builder) {
+            mappersBuilders.add(builder);
+            return this;
         }
 
-        protected Map<String, Mapper> buildMappers(ContentPath contentPath) {
+        @Override
+        public ObjectMapper build(ContentPath contentPath) {
             contentPath.add(name);
+
             Map<String, Mapper> mappers = new HashMap<>();
             for (Mapper.Builder builder : mappersBuilders) {
                 Mapper mapper = builder.build(contentPath);
@@ -102,19 +184,22 @@ public class ObjectMapper extends Mapper implements Cloneable {
                 mappers.put(mapper.simpleName(), mapper);
             }
             contentPath.remove();
-            return mappers;
+
+            return createMapper(name, contentPath.pathAsText(name), enabled, nested, dynamic,
+                mappers, indexCreatedVersion);
         }
 
-        @Override
-        public ObjectMapper build(ContentPath contentPath) {
-            return new ObjectMapper(name, contentPath.pathAsText(name), enabled, dynamic, buildMappers(contentPath));
+        protected ObjectMapper createMapper(String name, String fullPath, Explicit<Boolean> enabled, Nested nested, Dynamic dynamic,
+                Map<String, Mapper> mappers, Version indexCreatedVersion) {
+            return new ObjectMapper(name, fullPath, enabled, nested, dynamic, mappers, indexCreatedVersion);
         }
     }
 
     public static class TypeParser implements Mapper.TypeParser {
         @Override
         public Mapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
-            ObjectMapper.Builder builder = new Builder(name);
+            ObjectMapper.Builder builder = new Builder(name, parserContext.indexVersionCreated());
+            parseNested(name, node, builder);
             for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
                 Map.Entry<String, Object> entry = iterator.next();
                 String fieldName = entry.getKey();
@@ -160,6 +245,39 @@ public class ObjectMapper extends Mapper implements Cloneable {
             return false;
         }
 
+        protected static void parseNested(String name, Map<String, Object> node, ObjectMapper.Builder builder) {
+            boolean nested = false;
+            Explicit<Boolean> nestedIncludeInParent = new Explicit<>(false, false);
+            Explicit<Boolean> nestedIncludeInRoot = new Explicit<>(false, false);
+            Object fieldNode = node.get("type");
+            if (fieldNode!=null) {
+                String type = fieldNode.toString();
+                if (type.equals(CONTENT_TYPE)) {
+                    builder.nested = Nested.NO;
+                } else if (type.equals(NESTED_CONTENT_TYPE)) {
+                    nested = true;
+                } else {
+                    throw new MapperParsingException("Trying to parse an object but has a different type [" + type
+                        + "] for [" + name + "]");
+                }
+            }
+            fieldNode = node.get("include_in_parent");
+            if (fieldNode != null) {
+                boolean includeInParent = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_parent");
+                nestedIncludeInParent = new Explicit<>(includeInParent, true);
+                node.remove("include_in_parent");
+            }
+            fieldNode = node.get("include_in_root");
+            if (fieldNode != null) {
+                boolean includeInRoot = XContentMapValues.nodeBooleanValue(fieldNode, name + ".include_in_root");
+                nestedIncludeInRoot = new Explicit<>(includeInRoot, true);
+                node.remove("include_in_root");
+            }
+            if (nested) {
+                builder.nested = Nested.newNested(nestedIncludeInParent, nestedIncludeInRoot);
+            }
+        }
+
         protected static void parseProperties(ObjectMapper.Builder objBuilder, Map<String, Object> propsNode, ParserContext parserContext) {
             Iterator<Map.Entry<String, Object>> iterator = propsNode.entrySet().iterator();
             while (iterator.hasNext()) {
@@ -200,7 +318,7 @@ public class ObjectMapper extends Mapper implements Cloneable {
                     Mapper.Builder fieldBuilder = typeParser.parse(realFieldName, propNode, parserContext);
                     for (int i = fieldNameParts.length - 2; i >= 0; --i) {
                         ObjectMapper.Builder intermediate
-                            = new ObjectMapper.Builder(fieldNameParts[i]);
+                            = new ObjectMapper.Builder(fieldNameParts[i], parserContext.indexVersionCreated());
                         intermediate.add(fieldBuilder);
                         fieldBuilder = intermediate;
                     }
@@ -222,25 +340,39 @@ public class ObjectMapper extends Mapper implements Cloneable {
 
     private final String fullPath;
 
-    protected Explicit<Boolean> enabled;
+    private Explicit<Boolean> enabled;
+
+    private final Nested nested;
 
-    protected volatile Dynamic dynamic;
+    private final String nestedTypePath;
 
-    protected volatile CopyOnWriteHashMap<String, Mapper> mappers;
+    private final Query nestedTypeFilter;
 
-    ObjectMapper(String name, String fullPath, Explicit<Boolean> enabled, Dynamic dynamic, Map<String, Mapper> mappers) {
+    private volatile Dynamic dynamic;
+
+    private volatile CopyOnWriteHashMap<String, Mapper> mappers;
+
+    ObjectMapper(String name, String fullPath, Explicit<Boolean> enabled, Nested nested, Dynamic dynamic,
+            Map<String, Mapper> mappers, Version indexCreatedVersion) {
         super(name);
         if (name.isEmpty()) {
             throw new IllegalArgumentException("name cannot be empty string");
         }
         this.fullPath = fullPath;
         this.enabled = enabled;
+        this.nested = nested;
         this.dynamic = dynamic;
         if (mappers == null) {
             this.mappers = new CopyOnWriteHashMap<>();
         } else {
             this.mappers = CopyOnWriteHashMap.copyOf(mappers);
         }
+        if (indexCreatedVersion.before(Version.V_8_0_0)) {
+            this.nestedTypePath = "__" + fullPath;
+        } else {
+            this.nestedTypePath = fullPath;
+        }
+        this.nestedTypeFilter = NestedPathFieldMapper.filter(indexCreatedVersion, nestedTypePath);
     }
 
     @Override
@@ -284,14 +416,18 @@ public class ObjectMapper extends Mapper implements Cloneable {
         return this.enabled.value();
     }
 
-    public boolean isNested() {
-        return false;
-    }
-
     public Mapper getMapper(String field) {
         return mappers.get(field);
     }
 
+    public Nested nested() {
+        return this.nested;
+    }
+
+    public Query nestedTypeFilter() {
+        return this.nestedTypeFilter;
+    }
+
     protected void putMapper(Mapper mapper) {
         mappers = mappers.copyAndPut(mapper.simpleName(), mapper);
     }
@@ -305,6 +441,10 @@ public class ObjectMapper extends Mapper implements Cloneable {
         return this.fullPath;
     }
 
+    public String nestedTypePath() {
+        return this.nestedTypePath;
+    }
+
     public final Dynamic dynamic() {
         return dynamic;
     }
@@ -325,10 +465,6 @@ public class ObjectMapper extends Mapper implements Cloneable {
         if ((mergeWith instanceof ObjectMapper) == false) {
             throw new IllegalArgumentException("can't merge a non object mapping [" + mergeWith.name() + "] with an object mapping");
         }
-        if (mergeWith instanceof NestedObjectMapper) {
-            // TODO stop NestedObjectMapper extending ObjectMapper?
-            throw new IllegalArgumentException("can't merge a nested mapping [" + mergeWith.name() + "] with a non-nested mapping");
-        }
         ObjectMapper mergeWithObject = (ObjectMapper) mergeWith;
         ObjectMapper merged = clone();
         merged.doMerge(mergeWithObject, reason);
@@ -336,6 +472,7 @@ public class ObjectMapper extends Mapper implements Cloneable {
     }
 
     protected void doMerge(final ObjectMapper mergeWith, MergeReason reason) {
+        nested().merge(mergeWith.nested(), reason);
 
         if (mergeWith.dynamic != null) {
             this.dynamic = mergeWith.dynamic;
@@ -385,7 +522,15 @@ public class ObjectMapper extends Mapper implements Cloneable {
 
     void toXContent(XContentBuilder builder, Params params, ToXContent custom) throws IOException {
         builder.startObject(simpleName());
-        if (mappers.isEmpty() && custom == null) {
+        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() && custom == null) {
             // only write the object content type if there are no properties, otherwise, it is automatically detected
             builder.field("type", CONTENT_TYPE);
         }
@@ -401,11 +546,7 @@ public class ObjectMapper extends Mapper implements Cloneable {
         }
 
         doXContent(builder, params);
-        serializeMappers(builder, params);
-        builder.endObject();
-    }
 
-    protected void serializeMappers(XContentBuilder builder, Params params) throws IOException {
         // sort the mappers so we get consistent serialization format
         Mapper[] sortedMappers = mappers.values().toArray(Mapper[]::new);
         Arrays.sort(sortedMappers, Comparator.comparing(Mapper::name));
@@ -422,6 +563,7 @@ public class ObjectMapper extends Mapper implements Cloneable {
         if (count > 0) {
             builder.endObject();
         }
+        builder.endObject();
     }
 
     protected void doXContent(XContentBuilder builder, Params params) throws IOException {

+ 45 - 10
server/src/main/java/org/elasticsearch/index/mapper/RootObjectMapper.java

@@ -69,8 +69,8 @@ public class RootObjectMapper extends ObjectMapper {
         protected Explicit<Boolean> numericDetection = new Explicit<>(Defaults.NUMERIC_DETECTION, false);
         protected Map<String, RuntimeField> runtimeFields;
 
-        public Builder(String name) {
-            super(name);
+        public Builder(String name, Version indexCreatedVersion) {
+            super(name, indexCreatedVersion);
         }
 
         public Builder dynamicDateTimeFormatter(Collection<DateFormatter> dateTimeFormatters) {
@@ -96,11 +96,46 @@ public class RootObjectMapper extends ObjectMapper {
 
         @Override
         public RootObjectMapper build(ContentPath contentPath) {
-            return new RootObjectMapper(name, enabled, dynamic, buildMappers(contentPath),
-                runtimeFields == null ? Collections.emptyMap() : runtimeFields,
-                dynamicDateTimeFormatters,
-                dynamicTemplates,
-                dateDetection, numericDetection);
+            return (RootObjectMapper) super.build(contentPath);
+        }
+
+        @Override
+        protected ObjectMapper createMapper(String name, String fullPath, Explicit<Boolean> enabled, Nested nested, Dynamic dynamic,
+                Map<String, Mapper> mappers, Version indexCreatedVersion) {
+            assert nested.isNested() == false;
+            return new RootObjectMapper(name, enabled, dynamic, mappers,
+                    runtimeFields == null ? Collections.emptyMap() : runtimeFields,
+                    dynamicDateTimeFormatters,
+                    dynamicTemplates,
+                    dateDetection, numericDetection, indexCreatedVersion);
+        }
+    }
+
+    /**
+     * Removes redundant root includes in {@link ObjectMapper.Nested} trees to avoid duplicate
+     * fields on the root mapper when {@code isIncludeInRoot} is {@code true} for a node that is
+     * itself included into a parent node, for which either {@code isIncludeInRoot} is
+     * {@code true} or which is transitively included in root by a chain of nodes with
+     * {@code isIncludeInParent} returning {@code true}.
+     */
+    public void fixRedundantIncludes() {
+       fixRedundantIncludes(this, true);
+    }
+
+    private static void fixRedundantIncludes(ObjectMapper objectMapper, boolean parentIncluded) {
+        for (Mapper mapper : objectMapper) {
+            if (mapper instanceof ObjectMapper) {
+                ObjectMapper child = (ObjectMapper) mapper;
+                Nested nested = child.nested();
+                boolean isNested = nested.isNested();
+                boolean includeInRootViaParent = parentIncluded && isNested && nested.isIncludeInParent();
+                boolean includedInRoot = isNested && nested.isIncludeInRoot();
+                if (includeInRootViaParent && includedInRoot) {
+                    nested.setIncludeInParent(true);
+                    nested.setIncludeInRoot(false);
+                }
+                fixRedundantIncludes(child, includeInRootViaParent || includedInRoot);
+            }
         }
     }
 
@@ -109,7 +144,7 @@ public class RootObjectMapper extends ObjectMapper {
         @Override
         public RootObjectMapper.Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
             throws MapperParsingException {
-            RootObjectMapper.Builder builder = new Builder(name);
+            RootObjectMapper.Builder builder = new Builder(name, parserContext.indexVersionCreated());
             Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator();
             while (iterator.hasNext()) {
                 Map.Entry<String, Object> entry = iterator.next();
@@ -199,8 +234,8 @@ public class RootObjectMapper extends ObjectMapper {
     RootObjectMapper(String name, Explicit<Boolean> enabled, Dynamic dynamic, Map<String, Mapper> mappers,
                      Map<String, RuntimeField> runtimeFields,
                      Explicit<DateFormatter[]> dynamicDateTimeFormatters, Explicit<DynamicTemplate[]> dynamicTemplates,
-                     Explicit<Boolean> dateDetection, Explicit<Boolean> numericDetection) {
-        super(name, name, enabled, dynamic, mappers);
+                     Explicit<Boolean> dateDetection, Explicit<Boolean> numericDetection, Version indexCreatedVersion) {
+        super(name, name, enabled, Nested.NO, dynamic, mappers, indexCreatedVersion);
         this.runtimeFields = runtimeFields;
         this.dynamicTemplates = dynamicTemplates;
         this.dynamicDateTimeFormatters = dynamicDateTimeFormatters;

+ 10 - 17
server/src/main/java/org/elasticsearch/index/query/NestedQueryBuilder.java

@@ -34,7 +34,6 @@ import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.common.lucene.search.TopDocsAndMaxScore;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.search.ESToParentBlockJoinQuery;
 import org.elasticsearch.index.search.NestedHelper;
@@ -271,12 +270,12 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
                 throw new IllegalStateException("[" + NAME + "] failed to find nested object under path [" + path + "]");
             }
         }
-        if (nestedObjectMapper.isNested() == false) {
+        if (nestedObjectMapper.nested().isNested() == false) {
             throw new IllegalStateException("[" + NAME + "] nested object under path [" + path + "] is not of nested type");
         }
         final BitSetProducer parentFilter;
         Query innerQuery;
-        NestedObjectMapper objectMapper = context.nestedScope().getObjectMapper();
+        ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
         if (objectMapper == null) {
             parentFilter = context.bitsetFilter(Queries.newNonNestedFilter());
         } else {
@@ -284,7 +283,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
         }
 
         try {
-            context.nestedScope().nextLevel((NestedObjectMapper) nestedObjectMapper);
+            context.nestedScope().nextLevel(nestedObjectMapper);
             innerQuery = this.query.toQuery(context);
         } finally {
             context.nestedScope().previousLevel();
@@ -294,7 +293,7 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
         // in its child space
         NestedHelper nestedHelper = new NestedHelper(context::getObjectMapper, context::isFieldMapped);
         if (nestedHelper.mightMatchNonNestedDocs(innerQuery, path)) {
-            innerQuery = Queries.filtered(innerQuery, ((NestedObjectMapper)nestedObjectMapper).nestedTypeFilter());
+            innerQuery = Queries.filtered(innerQuery, nestedObjectMapper.nestedTypeFilter());
         }
 
         return new ESToParentBlockJoinQuery(innerQuery, parentFilter, scoreMode,
@@ -340,17 +339,16 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
         protected void doBuild(SearchContext parentSearchContext,
                           InnerHitsContext innerHitsContext) throws IOException {
             SearchExecutionContext searchExecutionContext = parentSearchContext.getSearchExecutionContext();
-            ObjectMapper objectMapper = searchExecutionContext.getObjectMapper(path);
-            if (objectMapper == null || objectMapper.isNested() == false) {
+            ObjectMapper nestedObjectMapper = searchExecutionContext.getObjectMapper(path);
+            if (nestedObjectMapper == null) {
                 if (innerHitBuilder.isIgnoreUnmapped() == false) {
                     throw new IllegalStateException("[" + query.getName() + "] no mapping found for type [" + path + "]");
                 } else {
                     return;
                 }
             }
-            NestedObjectMapper nestedObjectMapper = (NestedObjectMapper) objectMapper;
             String name =  innerHitBuilder.getName() != null ? innerHitBuilder.getName() : nestedObjectMapper.fullPath();
-            NestedObjectMapper parentObjectMapper = searchExecutionContext.nestedScope().nextLevel(nestedObjectMapper);
+            ObjectMapper parentObjectMapper = searchExecutionContext.nestedScope().nextLevel(nestedObjectMapper);
             NestedInnerHitSubContext nestedInnerHits = new NestedInnerHitSubContext(
                 name, parentSearchContext, parentObjectMapper, nestedObjectMapper
             );
@@ -362,15 +360,10 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
 
     static final class NestedInnerHitSubContext extends InnerHitsContext.InnerHitSubContext {
 
-        private final NestedObjectMapper parentObjectMapper;
-        private final NestedObjectMapper childObjectMapper;
+        private final ObjectMapper parentObjectMapper;
+        private final ObjectMapper childObjectMapper;
 
-        NestedInnerHitSubContext(
-            String name,
-            SearchContext context,
-            NestedObjectMapper parentObjectMapper,
-            NestedObjectMapper childObjectMapper
-        ) {
+        NestedInnerHitSubContext(String name, SearchContext context, ObjectMapper parentObjectMapper, ObjectMapper childObjectMapper) {
             super(name, context);
             this.parentObjectMapper = parentObjectMapper;
             this.childObjectMapper = childObjectMapper;

+ 1 - 2
server/src/main/java/org/elasticsearch/index/query/SearchExecutionContext.java

@@ -41,7 +41,6 @@ import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.index.mapper.MapperParsingException;
 import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.index.mapper.MappingLookup;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.mapper.ParsedDocument;
 import org.elasticsearch.index.mapper.RuntimeField;
@@ -296,7 +295,7 @@ public class SearchExecutionContext extends QueryRewriteContext {
         return mappingLookup.hasMappings();
     }
 
-    public List<NestedObjectMapper> nestedMappings() {
+    public List<ObjectMapper> nestedMappings() {
         return mappingLookup.getNestedMappers();
     }
 

+ 4 - 5
server/src/main/java/org/elasticsearch/index/query/support/NestedScope.java

@@ -8,7 +8,6 @@
 
 package org.elasticsearch.index.query.support;
 
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 
 import java.util.Deque;
@@ -19,20 +18,20 @@ import java.util.LinkedList;
  */
 public final class NestedScope {
 
-    private final Deque<NestedObjectMapper> levelStack = new LinkedList<>();
+    private final Deque<ObjectMapper> levelStack = new LinkedList<>();
 
     /**
      * @return For the current nested level returns the object mapper that belongs to that
      */
-    public NestedObjectMapper getObjectMapper() {
+    public ObjectMapper getObjectMapper() {
         return levelStack.peek();
     }
 
     /**
      * Sets the new current nested level and pushes old current nested level down the stack returns that level.
      */
-    public NestedObjectMapper nextLevel(NestedObjectMapper level) {
-        NestedObjectMapper previous = levelStack.peek();
+    public ObjectMapper nextLevel(ObjectMapper level) {
+        ObjectMapper previous = levelStack.peek();
         levelStack.push(level);
         return previous;
     }

+ 3 - 5
server/src/main/java/org/elasticsearch/index/search/NestedHelper.java

@@ -21,7 +21,6 @@ import org.apache.lucene.search.PointRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermInSetQuery;
 import org.apache.lucene.search.TermQuery;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 
 import java.util.function.Function;
@@ -103,7 +102,7 @@ public final class NestedHelper {
         }
         for (String parent = parentObject(field); parent != null; parent = parentObject(parent)) {
             ObjectMapper mapper = objectMapperLookup.apply(parent);
-            if (mapper != null && mapper.isNested()) {
+            if (mapper != null && mapper.nested().isNested()) {
                 return true;
             }
         }
@@ -171,12 +170,11 @@ public final class NestedHelper {
         }
         for (String parent = parentObject(field); parent != null; parent = parentObject(parent)) {
             ObjectMapper mapper = objectMapperLookup.apply(parent);
-            if (mapper != null && mapper.isNested()) {
-                NestedObjectMapper nestedMapper = (NestedObjectMapper) mapper;
+            if (mapper!= null && mapper.nested().isNested()) {
                 if (mapper.fullPath().equals(nestedPath)) {
                     // If the mapper does not include in its parent or in the root object then
                     // the query might only match nested documents with the given path
-                    return nestedMapper.isIncludeInParent() || nestedMapper.isIncludeInRoot();
+                    return mapper.nested().isIncludeInParent() || mapper.nested().isIncludeInRoot();
                 } else {
                     // the first parent nested mapper does not have the expected path
                     // It might be misconfiguration or a sub nested mapper

+ 1 - 2
server/src/main/java/org/elasticsearch/indices/IndicesModule.java

@@ -34,7 +34,6 @@ import org.elasticsearch.index.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.Mapper;
 import org.elasticsearch.index.mapper.MapperRegistry;
 import org.elasticsearch.index.mapper.MetadataFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NestedPathFieldMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
@@ -128,7 +127,7 @@ public class IndicesModule extends AbstractModule {
         mappers.put(IpFieldMapper.CONTENT_TYPE, IpFieldMapper.PARSER);
         mappers.put(KeywordFieldMapper.CONTENT_TYPE, KeywordFieldMapper.PARSER);
         mappers.put(ObjectMapper.CONTENT_TYPE, new ObjectMapper.TypeParser());
-        mappers.put(NestedObjectMapper.CONTENT_TYPE, new NestedObjectMapper.TypeParser());
+        mappers.put(ObjectMapper.NESTED_CONTENT_TYPE, new ObjectMapper.TypeParser());
         mappers.put(TextFieldMapper.CONTENT_TYPE, TextFieldMapper.PARSER);
 
         for (MapperPlugin mapperPlugin : mapperPlugins) {

+ 5 - 5
server/src/main/java/org/elasticsearch/search/NestedDocuments.java

@@ -20,7 +20,7 @@ import org.apache.lucene.search.join.BitSetProducer;
 import org.apache.lucene.util.BitSet;
 import org.elasticsearch.common.lucene.search.Queries;
 import org.elasticsearch.index.mapper.MappingLookup;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -34,7 +34,7 @@ public class NestedDocuments {
 
     private final Map<String, BitSetProducer> parentObjectFilters = new HashMap<>();
     private final Map<String, Weight> childObjectFilters = new HashMap<>();
-    private final Map<String, NestedObjectMapper> childObjectMappers = new HashMap<>();
+    private final Map<String, ObjectMapper> childObjectMappers = new HashMap<>();
     private final BitSetProducer parentDocumentFilter;
     private final MappingLookup mappingLookup;
 
@@ -49,11 +49,11 @@ public class NestedDocuments {
             this.parentDocumentFilter = null;
         } else {
             this.parentDocumentFilter = filterProducer.apply(Queries.newNonNestedFilter());
-            for (NestedObjectMapper mapper : mappingLookup.getNestedParentMappers()) {
+            for (ObjectMapper mapper : mappingLookup.getNestedParentMappers()) {
                 parentObjectFilters.put(mapper.name(),
                     filterProducer.apply(mapper.nestedTypeFilter()));
             }
-            for (NestedObjectMapper mapper : mappingLookup.getNestedMappers()) {
+            for (ObjectMapper mapper : mappingLookup.getNestedMappers()) {
                 childObjectFilters.put(mapper.name(), null);
                 childObjectMappers.put(mapper.name(), mapper);
             }
@@ -76,7 +76,7 @@ public class NestedDocuments {
         }
         if (childObjectFilters.get(path) == null) {
             IndexSearcher searcher = new IndexSearcher(ReaderUtil.getTopLevelContext(ctx));
-            NestedObjectMapper childMapper = childObjectMappers.get(path);
+            ObjectMapper childMapper = childObjectMappers.get(path);
             childObjectFilters.put(path,
                 searcher.createWeight(searcher.rewrite(childMapper.nestedTypeFilter()), ScoreMode.COMPLETE_NO_SCORES, 1));
         }

+ 3 - 4
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregationBuilder.java

@@ -13,7 +13,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
 import org.elasticsearch.search.aggregations.AggregationBuilder;
@@ -92,12 +91,12 @@ public class NestedAggregationBuilder extends AbstractAggregationBuilder<NestedA
                 parent, subFactoriesBuilder, metadata);
         }
 
-        if (childObjectMapper.isNested() == false) {
+        if (childObjectMapper.nested().isNested() == false) {
             throw new AggregationExecutionException("[nested] nested path [" + path + "] is not nested");
         }
         try {
-            NestedObjectMapper parentObjectMapper = context.nestedScope().nextLevel((NestedObjectMapper) childObjectMapper);
-            return new NestedAggregatorFactory(name, parentObjectMapper, (NestedObjectMapper) childObjectMapper, context,
+            ObjectMapper parentObjectMapper = context.nestedScope().nextLevel(childObjectMapper);
+            return new NestedAggregatorFactory(name, parentObjectMapper, childObjectMapper, context,
                 parent, subFactoriesBuilder, metadata);
         } finally {
             context.nestedScope().previousLevel();

+ 5 - 11
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregator.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.search.aggregations.bucket.nested;
 
 import com.carrotsearch.hppc.LongArrayList;
+
 import org.apache.lucene.index.IndexReaderContext;
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.index.ReaderUtil;
@@ -22,7 +23,7 @@ import org.apache.lucene.search.join.BitSetProducer;
 import org.apache.lucene.util.BitSet;
 import org.elasticsearch.common.xcontent.ParseField;
 import org.elasticsearch.common.lucene.search.Queries;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.aggregations.Aggregator;
 import org.elasticsearch.search.aggregations.AggregatorFactories;
 import org.elasticsearch.search.aggregations.CardinalityUpperBound;
@@ -46,16 +47,9 @@ public class NestedAggregator extends BucketsAggregator implements SingleBucketA
 
     private BufferingNestedLeafBucketCollector bufferingNestedLeafBucketCollector;
 
-    NestedAggregator(
-        String name,
-        AggregatorFactories factories,
-        NestedObjectMapper parentObjectMapper,
-        NestedObjectMapper childObjectMapper,
-        AggregationContext context,
-        Aggregator parent,
-        CardinalityUpperBound cardinality,
-        Map<String, Object> metadata
-    ) throws IOException {
+    NestedAggregator(String name, AggregatorFactories factories, ObjectMapper parentObjectMapper, ObjectMapper childObjectMapper,
+                     AggregationContext context, Aggregator parent, CardinalityUpperBound cardinality,
+                     Map<String, Object> metadata) throws IOException {
         super(name, factories, context, parent, cardinality, metadata);
 
         Query parentFilter = parentObjectMapper != null ? parentObjectMapper.nestedTypeFilter()

+ 4 - 4
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorFactory.java

@@ -8,7 +8,7 @@
 
 package org.elasticsearch.search.aggregations.bucket.nested;
 
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.aggregations.Aggregator;
 import org.elasticsearch.search.aggregations.AggregatorFactories;
 import org.elasticsearch.search.aggregations.AggregatorFactory;
@@ -22,10 +22,10 @@ import java.util.Map;
 
 public class NestedAggregatorFactory extends AggregatorFactory {
 
-    private final NestedObjectMapper parentObjectMapper;
-    private final NestedObjectMapper childObjectMapper;
+    private final ObjectMapper parentObjectMapper;
+    private final ObjectMapper childObjectMapper;
 
-    NestedAggregatorFactory(String name, NestedObjectMapper parentObjectMapper, NestedObjectMapper childObjectMapper,
+    NestedAggregatorFactory(String name, ObjectMapper parentObjectMapper, ObjectMapper childObjectMapper,
                             AggregationContext context, AggregatorFactory parent, AggregatorFactories.Builder subFactories,
                             Map<String, Object> metadata) throws IOException {
         super(name, context, parent, subFactories, metadata);

+ 3 - 5
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregationBuilder.java

@@ -13,7 +13,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
 import org.elasticsearch.common.io.stream.StreamOutput;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.query.support.NestedScope;
 import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
@@ -98,16 +97,15 @@ public class ReverseNestedAggregationBuilder extends AbstractAggregationBuilder<
             if (parentObjectMapper == null) {
                 return new ReverseNestedAggregatorFactory(name, true, null, context, parent, subFactoriesBuilder, metadata);
             }
-            if (parentObjectMapper.isNested() == false) {
+            if (parentObjectMapper.nested().isNested() == false) {
                 throw new AggregationExecutionException("[reverse_nested] nested path [" + path + "] is not nested");
             }
         }
 
         NestedScope nestedScope = context.nestedScope();
-        NestedObjectMapper nestedMapper = (NestedObjectMapper) parentObjectMapper;
         try {
-            nestedScope.nextLevel(nestedMapper);
-            return new ReverseNestedAggregatorFactory(name, false, nestedMapper, context, parent, subFactoriesBuilder,
+            nestedScope.nextLevel(parentObjectMapper);
+            return new ReverseNestedAggregatorFactory(name, false, parentObjectMapper, context, parent, subFactoriesBuilder,
                     metadata);
         } finally {
             nestedScope.previousLevel();

+ 3 - 2
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregator.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.search.aggregations.bucket.nested;
 
 import com.carrotsearch.hppc.LongIntHashMap;
+
 import org.apache.lucene.index.LeafReaderContext;
 import org.apache.lucene.search.DocIdSetIterator;
 import org.apache.lucene.search.Query;
@@ -15,7 +16,7 @@ import org.apache.lucene.search.join.BitSetProducer;
 import org.apache.lucene.util.BitSet;
 import org.elasticsearch.common.xcontent.ParseField;
 import org.elasticsearch.common.lucene.search.Queries;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.aggregations.Aggregator;
 import org.elasticsearch.search.aggregations.AggregatorFactories;
 import org.elasticsearch.search.aggregations.CardinalityUpperBound;
@@ -36,7 +37,7 @@ public class ReverseNestedAggregator extends BucketsAggregator implements Single
     private final Query parentFilter;
     private final BitSetProducer parentBitsetProducer;
 
-    public ReverseNestedAggregator(String name, AggregatorFactories factories, NestedObjectMapper objectMapper,
+    public ReverseNestedAggregator(String name, AggregatorFactories factories, ObjectMapper objectMapper,
             AggregationContext context, Aggregator parent, CardinalityUpperBound cardinality, Map<String, Object> metadata)
             throws IOException {
         super(name, factories, context, parent, cardinality, metadata);

+ 3 - 3
server/src/main/java/org/elasticsearch/search/aggregations/bucket/nested/ReverseNestedAggregatorFactory.java

@@ -8,7 +8,7 @@
 
 package org.elasticsearch.search.aggregations.bucket.nested;
 
-import org.elasticsearch.index.mapper.NestedObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.aggregations.Aggregator;
 import org.elasticsearch.search.aggregations.AggregatorFactories;
 import org.elasticsearch.search.aggregations.AggregatorFactory;
@@ -23,9 +23,9 @@ import java.util.Map;
 public class ReverseNestedAggregatorFactory extends AggregatorFactory {
 
     private final boolean unmapped;
-    private final NestedObjectMapper parentObjectMapper;
+    private final ObjectMapper parentObjectMapper;
 
-    public ReverseNestedAggregatorFactory(String name, boolean unmapped, NestedObjectMapper parentObjectMapper,
+    public ReverseNestedAggregatorFactory(String name, boolean unmapped, ObjectMapper parentObjectMapper,
                                           AggregationContext context, AggregatorFactory parent,
                                           AggregatorFactories.Builder subFactories,
                                           Map<String, Object> metadata) throws IOException {

+ 9 - 6
server/src/main/java/org/elasticsearch/search/sort/FieldSortBuilder.java

@@ -34,7 +34,6 @@ import org.elasticsearch.index.fielddata.IndexNumericFieldData.NumericType;
 import org.elasticsearch.index.mapper.DateFieldMapper.DateFieldType;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.query.QueryBuilder;
@@ -623,16 +622,20 @@ public class FieldSortBuilder extends SortBuilder<FieldSortBuilder> {
      * Throws an exception if the provided <code>field</code> requires a nested context.
      */
     static void validateMissingNestedPath(SearchExecutionContext context, String field) {
-        NestedObjectMapper contextMapper = context.nestedScope().getObjectMapper();
-        if (contextMapper != null) {
+        ObjectMapper contextMapper = context.nestedScope().getObjectMapper();
+        if (contextMapper != null && contextMapper.nested().isNested() == false) {
             // already in nested context
             return;
         }
         for (String parent = parentObject(field); parent != null; parent = parentObject(parent)) {
             ObjectMapper parentMapper = context.getObjectMapper(parent);
-            if (parentMapper != null && parentMapper.isNested()) {
-                NestedObjectMapper parentNested = (NestedObjectMapper) parentMapper;
-                if (parentNested.isIncludeInRoot() == false) {
+            if (parentMapper != null && parentMapper.nested().isNested()) {
+                if (contextMapper != null && contextMapper.fullPath().equals(parentMapper.fullPath())) {
+                    // we are in a nested context that matches the path of the provided field so the nested path
+                    // is not required
+                    return ;
+                }
+                if (parentMapper.nested().isIncludeInRoot() == false) {
                     throw new QueryShardException(context,
                         "it is mandatory to set the [nested] context on the nested sort field: [" + field + "].");
                 }

+ 8 - 10
server/src/main/java/org/elasticsearch/search/sort/SortBuilder.java

@@ -21,12 +21,11 @@ import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.common.xcontent.ToXContentObject;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.index.query.QueryShardException;
 import org.elasticsearch.index.query.Rewriteable;
-import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.search.DocValueFormat;
 
 import java.io.IOException;
@@ -176,7 +175,7 @@ public abstract class SortBuilder<T extends SortBuilder<T>> implements NamedWrit
         if (childQuery == null) {
             return null;
         }
-        final NestedObjectMapper objectMapper = context.nestedScope().getObjectMapper();
+        final ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
         final Query parentQuery;
         if (objectMapper == null) {
             parentQuery = Queries.newNonNestedFilter();
@@ -198,16 +197,15 @@ public abstract class SortBuilder<T extends SortBuilder<T>> implements NamedWrit
         NestedSortBuilder nestedNestedSort = nestedSort.getNestedSort();
 
         // verify our nested path
-        ObjectMapper objectMapper = context.getObjectMapper(nestedPath);
+        ObjectMapper nestedObjectMapper = context.getObjectMapper(nestedPath);
 
-        if (objectMapper == null) {
+        if (nestedObjectMapper == null) {
             throw new QueryShardException(context, "[nested] failed to find nested object under path [" + nestedPath + "]");
         }
-        if (objectMapper.isNested() == false) {
+        if (nestedObjectMapper.nested().isNested() == false) {
             throw new QueryShardException(context, "[nested] nested object under path [" + nestedPath + "] is not of nested type");
         }
-        NestedObjectMapper nestedObjectMapper = (NestedObjectMapper) objectMapper;
-        NestedObjectMapper parentMapper = context.nestedScope().getObjectMapper();
+        ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
 
         // get our child query, potentially applying a users filter
         Query childQuery;
@@ -230,9 +228,9 @@ public abstract class SortBuilder<T extends SortBuilder<T>> implements NamedWrit
 
         // apply filters from the previous nested level
         if (parentQuery != null) {
-            if (parentMapper != null) {
+            if (objectMapper != null) {
                 childQuery = Queries.filtered(childQuery,
-                    new ToChildBlockJoinQuery(parentQuery, context.bitsetFilter(parentMapper.nestedTypeFilter())));
+                    new ToChildBlockJoinQuery(parentQuery, context.bitsetFilter(objectMapper.nestedTypeFilter())));
             }
         }
 

+ 2 - 2
server/src/test/java/org/elasticsearch/action/admin/indices/rollover/MetadataRolloverServiceTests.java

@@ -578,7 +578,7 @@ public class MetadataRolloverServiceTests extends ESTestCase {
             AllocationService allocationService = mock(AllocationService.class);
             when(allocationService.reroute(any(ClusterState.class), any(String.class))).then(i -> i.getArguments()[0]);
             MetadataFieldMapper[] metadataFieldMappers = {new MetadataIndexTemplateServiceTests.MetadataTimestampFieldMapper(true)};
-            RootObjectMapper.Builder root = new RootObjectMapper.Builder("_doc");
+            RootObjectMapper.Builder root = new RootObjectMapper.Builder("_doc", Version.CURRENT);
             root.add(new DateFieldMapper.Builder(dataStream.getTimeStampField().getName(), DateFieldMapper.Resolution.MILLISECONDS,
                 DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER, ScriptCompiler.NONE, true, Version.CURRENT));
             Mapping mapping = new Mapping(root.build(new ContentPath("")), metadataFieldMappers, Collections.emptyMap());
@@ -668,7 +668,7 @@ public class MetadataRolloverServiceTests extends ESTestCase {
                 }
             };
             MetadataFieldMapper[] metadataFieldMappers = {new MetadataIndexTemplateServiceTests.MetadataTimestampFieldMapper(true)};
-            RootObjectMapper.Builder root = new RootObjectMapper.Builder("_doc");
+            RootObjectMapper.Builder root = new RootObjectMapper.Builder("_doc", Version.CURRENT);
             root.add(new DateFieldMapper.Builder(dataStream.getTimeStampField().getName(), DateFieldMapper.Resolution.MILLISECONDS,
                 DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER, ScriptCompiler.NONE, true, Version.CURRENT));
             Mapping mapping = new Mapping(root.build(new ContentPath("")), metadataFieldMappers, Collections.emptyMap());

+ 1 - 1
server/src/test/java/org/elasticsearch/cluster/action/index/MappingUpdatedActionTests.java

@@ -143,7 +143,7 @@ public class MappingUpdatedActionTests extends ESTestCase {
             new ClusterSettings(Settings.EMPTY, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS));
         mua.setClient(client);
 
-        RootObjectMapper rootObjectMapper = new RootObjectMapper.Builder("name").build(new ContentPath());
+        RootObjectMapper rootObjectMapper = new RootObjectMapper.Builder("name", Version.CURRENT).build(new ContentPath());
         Mapping update = new Mapping(rootObjectMapper, new MetadataFieldMapper[0], Map.of());
 
         mua.sendUpdateMapping(new Index("name", "uuid"), update, ActionListener.wrap(() -> {}));

+ 3 - 3
server/src/test/java/org/elasticsearch/index/mapper/DocumentMapperTests.java

@@ -76,18 +76,18 @@ public class DocumentMapperTests extends MapperServiceTestCase {
 
     public void testMergeObjectAndNested() throws Exception {
         DocumentMapper objectMapper = createDocumentMapper(mapping(b -> b.startObject("obj").field("type", "object").endObject()));
-        DocumentMapper nestedMapper = createDocumentMapper(mapping(b -> b.startObject("obj").field("type", "nested").endObject()));
+        DocumentMapper nestedMapper = createDocumentMapper((mapping(b -> b.startObject("obj").field("type", "nested").endObject())));
         MergeReason reason = randomFrom(MergeReason.MAPPING_UPDATE, MergeReason.INDEX_TEMPLATE);
 
         {
             IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
                 () -> MapperService.mergeMappings(objectMapper, nestedMapper.mapping(), reason));
-            assertThat(e.getMessage(), containsString("can't merge a nested mapping [obj] with a non-nested mapping"));
+            assertThat(e.getMessage(), containsString("cannot change object mapping from non-nested to nested"));
         }
         {
             IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
                 () -> MapperService.mergeMappings(nestedMapper, objectMapper.mapping(), reason));
-            assertThat(e.getMessage(), containsString("can't merge a non nested mapping [obj] with a nested mapping"));
+            assertThat(e.getMessage(), containsString("cannot change object mapping from nested to non-nested"));
         }
     }
 

+ 2 - 1
server/src/test/java/org/elasticsearch/index/mapper/DocumentParserTests.java

@@ -14,6 +14,7 @@ import org.apache.lucene.document.LatLonPoint;
 import org.apache.lucene.document.StringField;
 import org.apache.lucene.index.IndexableField;
 import org.apache.lucene.util.BytesRef;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.bytes.BytesArray;
 import org.elasticsearch.common.bytes.BytesReference;
@@ -616,7 +617,7 @@ public class DocumentParserTests extends MapperServiceTestCase {
         for (int i = 0; i < nameParts.length - 1; ++i) {
             context.path().add(nameParts[i]);
         }
-        Mapper.Builder builder = new ObjectMapper.Builder(nameParts[nameParts.length - 1]).enabled(true);
+        Mapper.Builder builder = new ObjectMapper.Builder(nameParts[nameParts.length - 1], Version.CURRENT).enabled(true);
         return (ObjectMapper)builder.build(context.path());
     }
 

+ 8 - 4
server/src/test/java/org/elasticsearch/index/mapper/FieldAliasMapperValidationTests.java

@@ -184,18 +184,22 @@ public class FieldAliasMapperValidationTests extends ESTestCase {
     private static ObjectMapper createObjectMapper(String name) {
         return new ObjectMapper(name, name,
             new Explicit<>(true, false),
-            ObjectMapper.Dynamic.FALSE, emptyMap());
+            ObjectMapper.Nested.NO,
+            ObjectMapper.Dynamic.FALSE, emptyMap(), Version.CURRENT);
     }
 
-    private static NestedObjectMapper createNestedObjectMapper(String name) {
-        return new NestedObjectMapper.Builder(name, Version.CURRENT).build(new ContentPath());
+    private static ObjectMapper createNestedObjectMapper(String name) {
+        return new ObjectMapper(name, name,
+            new Explicit<>(true, false),
+            ObjectMapper.Nested.newNested(),
+            ObjectMapper.Dynamic.FALSE, emptyMap(), Version.CURRENT);
     }
 
     private static MappingLookup createMappingLookup(List<FieldMapper> fieldMappers,
                                                      List<ObjectMapper> objectMappers,
                                                      List<FieldAliasMapper> fieldAliasMappers,
                                                      List<RuntimeField> runtimeFields) {
-        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc");
+        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc", Version.CURRENT);
         Map<String, RuntimeField> runtimeFieldTypes = runtimeFields.stream().collect(Collectors.toMap(RuntimeField::name, r -> r));
         builder.setRuntime(runtimeFieldTypes);
         Mapping mapping = new Mapping(builder.build(new ContentPath()), new MetadataFieldMapper[0], Collections.emptyMap());

+ 4 - 3
server/src/test/java/org/elasticsearch/index/mapper/MappingLookupTests.java

@@ -12,6 +12,7 @@ import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.TokenStream;
 import org.apache.lucene.analysis.Tokenizer;
 import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
+import org.elasticsearch.Version;
 import org.elasticsearch.common.Explicit;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.index.analysis.AnalyzerScope;
@@ -35,7 +36,7 @@ public class MappingLookupTests extends ESTestCase {
     private static MappingLookup createMappingLookup(List<FieldMapper> fieldMappers,
                                                      List<ObjectMapper> objectMappers,
                                                      List<RuntimeField> runtimeFields) {
-        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc");
+        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc", Version.CURRENT);
         Map<String, RuntimeField> runtimeFieldTypes = runtimeFields.stream().collect(Collectors.toMap(RuntimeField::name, r -> r));
         builder.setRuntime(runtimeFieldTypes);
         Mapping mapping = new Mapping(builder.build(new ContentPath()), new MetadataFieldMapper[0], Collections.emptyMap());
@@ -63,8 +64,8 @@ public class MappingLookupTests extends ESTestCase {
 
     public void testSubfieldOverride() {
         MockFieldMapper fieldMapper = new MockFieldMapper("object.subfield");
-        ObjectMapper objectMapper = new ObjectMapper("object", "object", new Explicit<>(true, true),
-            ObjectMapper.Dynamic.TRUE, Collections.singletonMap("object.subfield", fieldMapper));
+        ObjectMapper objectMapper = new ObjectMapper("object", "object", new Explicit<>(true, true), ObjectMapper.Nested.NO,
+            ObjectMapper.Dynamic.TRUE, Collections.singletonMap("object.subfield", fieldMapper), Version.CURRENT);
         MappingLookup mappingLookup = createMappingLookup(
             Collections.singletonList(fieldMapper),
             Collections.singletonList(objectMapper),

+ 37 - 79
server/src/test/java/org/elasticsearch/index/mapper/NestedObjectMapperTests.java

@@ -29,8 +29,6 @@ import java.util.function.Function;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.nullValue;
 
 public class NestedObjectMapperTests extends MapperServiceTestCase {
@@ -58,9 +56,8 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         DocumentMapper docMapper = createDocumentMapper(mapping(b -> b.startObject("nested1").field("type", "nested").endObject()));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper;
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1", BytesReference
                 .bytes(XContentFactory.jsonBuilder()
@@ -115,16 +112,14 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         }));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper1 = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper1, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper1;
-        assertThat(nested1Mapper.isIncludeInParent(), equalTo(false));
-        assertThat(nested1Mapper.isIncludeInRoot(), equalTo(false));
-        ObjectMapper mapper2 = docMapper.mappers().objectMappers().get("nested1.nested2");
-        assertThat(mapper2, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested2Mapper = (NestedObjectMapper) mapper2;
-        assertThat(nested2Mapper.isIncludeInParent(), equalTo(false));
-        assertThat(nested2Mapper.isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
+        assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested2Mapper = docMapper.mappers().objectMappers().get("nested1.nested2");
+        assertThat(nested2Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(false));
+        assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
             BytesReference.bytes(XContentFactory.jsonBuilder()
@@ -187,16 +182,14 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         }));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper1 = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper1, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper1;
-        assertThat(nested1Mapper.isIncludeInParent(), equalTo(false));
-        assertThat(nested1Mapper.isIncludeInRoot(), equalTo(false));
-        ObjectMapper mapper2 = docMapper.mappers().objectMappers().get("nested1.nested2");
-        assertThat(mapper2, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested2Mapper = (NestedObjectMapper) mapper2;
-        assertThat(nested2Mapper.isIncludeInParent(), equalTo(true));
-        assertThat(nested2Mapper.isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
+        assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested2Mapper = docMapper.mappers().objectMappers().get("nested1.nested2");
+        assertThat(nested2Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
             BytesReference.bytes(XContentFactory.jsonBuilder()
@@ -260,16 +253,14 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         }));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper1 = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper1, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper1;
-        assertThat(nested1Mapper.isIncludeInParent(), equalTo(true));
-        assertThat(nested1Mapper.isIncludeInRoot(), equalTo(false));
-        ObjectMapper mapper2 = docMapper.mappers().objectMappers().get("nested1.nested2");
-        assertThat(mapper2, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested2Mapper = (NestedObjectMapper) mapper2;
-        assertThat(nested2Mapper.isIncludeInParent(), equalTo(true));
-        assertThat(nested2Mapper.isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(true));
+        assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested2Mapper = docMapper.mappers().objectMappers().get("nested1.nested2");
+        assertThat(nested2Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(false));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
             BytesReference.bytes(XContentFactory.jsonBuilder()
@@ -336,16 +327,14 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         assertNull(docMapper.mappers().getNestedParent("nested1"));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper1 = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper1, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper1;
-        assertThat(nested1Mapper.isIncludeInParent(), equalTo(false));
-        assertThat(nested1Mapper.isIncludeInRoot(), equalTo(false));
-        ObjectMapper mapper2 = docMapper.mappers().objectMappers().get("nested1.nested2");
-        assertThat(mapper2, instanceOf(NestedObjectMapper.class));
-        NestedObjectMapper nested2Mapper = (NestedObjectMapper) mapper2;
-        assertThat(nested2Mapper.isIncludeInParent(), equalTo(false));
-        assertThat(nested2Mapper.isIncludeInRoot(), equalTo(true));
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested1Mapper.nested().isIncludeInParent(), equalTo(false));
+        assertThat(nested1Mapper.nested().isIncludeInRoot(), equalTo(false));
+        ObjectMapper nested2Mapper = docMapper.mappers().objectMappers().get("nested1.nested2");
+        assertThat(nested2Mapper.nested().isNested(), equalTo(true));
+        assertThat(nested2Mapper.nested().isIncludeInParent(), equalTo(false));
+        assertThat(nested2Mapper.nested().isIncludeInRoot(), equalTo(true));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
             BytesReference.bytes(XContentFactory.jsonBuilder()
@@ -541,7 +530,7 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
         ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(nested1Mapper, instanceOf(NestedObjectMapper.class));
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
         assertThat(nested1Mapper.dynamic(), equalTo(Dynamic.STRICT));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
@@ -754,8 +743,8 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
             = createDocumentMapper(version, mapping(b -> b.startObject("nested1").field("type", "nested").endObject()));
 
         assertThat(docMapper.mappers().hasNested(), equalTo(true));
-        ObjectMapper mapper = docMapper.mappers().objectMappers().get("nested1");
-        assertThat(mapper, instanceOf(NestedObjectMapper.class));
+        ObjectMapper nested1Mapper = docMapper.mappers().objectMappers().get("nested1");
+        assertThat(nested1Mapper.nested().isNested(), equalTo(true));
 
         ParsedDocument doc = docMapper.parse(new SourceToParse("test", "1",
             BytesReference.bytes(XContentFactory.jsonBuilder()
@@ -775,7 +764,6 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
             XContentType.JSON));
 
         assertThat(doc.docs().size(), equalTo(3));
-        NestedObjectMapper nested1Mapper = (NestedObjectMapper) mapper;
         if (version.before(Version.V_8_0_0)) {
             assertThat(doc.docs().get(0).get("_type"), equalTo(nested1Mapper.nestedTypePath()));
         } else {
@@ -788,36 +776,6 @@ public class NestedObjectMapperTests extends MapperServiceTestCase {
         assertThat(doc.docs().get(2).get("field"), equalTo("value"));
     }
 
-    public void testMergeChildMappings() throws IOException {
-        MapperService mapperService = createMapperService(mapping(b -> {
-            b.startObject("nested1");
-            b.field("type", "nested");
-            b.startObject("properties");
-            b.startObject("field1").field("type", "keyword").endObject();
-            b.startObject("field2").field("type", "keyword").endObject();
-            b.startObject("nested2").field("type", "nested").field("include_in_root", true).endObject();
-            b.endObject();
-            b.endObject();
-        }));
-
-        merge(mapperService, mapping(b -> {
-            b.startObject("nested1");
-            b.field("type", "nested");
-            b.startObject("properties");
-            b.startObject("field2").field("type", "keyword").endObject();
-            b.startObject("field3").field("type", "keyword").endObject();
-            b.startObject("nested2").field("type", "nested").field("include_in_root", true).endObject();
-            b.endObject();
-            b.endObject();
-        }));
-
-        NestedObjectMapper nested1 = (NestedObjectMapper) mapperService.mappingLookup().objectMappers().get("nested1");
-        assertThat(nested1.getChildren().values(), hasSize(4));
-
-        NestedObjectMapper nested2 = (NestedObjectMapper) nested1.getChildren().get("nested2");
-        assertTrue(nested2.isIncludeInRoot());
-    }
-
     public void testMergeNestedMappings() throws IOException {
         MapperService mapperService = createMapperService(mapping(b -> b.startObject("nested1").field("type", "nested").endObject()));
 

+ 20 - 17
server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java

@@ -8,6 +8,7 @@
 package org.elasticsearch.index.mapper;
 
 import org.elasticsearch.Version;
+import org.elasticsearch.common.Explicit;
 import org.elasticsearch.test.ESTestCase;
 
 import java.util.Collections;
@@ -67,7 +68,7 @@ public class ObjectMapperMergeTests extends ESTestCase {
         // GIVEN a mapping with "foo" field disabled
         Map<String, Mapper> mappers = new HashMap<>();
         //the field is disabled, and we are not trying to re-enable it, hence merge should work
-        mappers.put("disabled", new ObjectMapper.Builder("disabled").build(new ContentPath()));
+        mappers.put("disabled", new ObjectMapper.Builder("disabled", Version.CURRENT).build(new ContentPath()));
         RootObjectMapper mergeWith = createRootObjectMapper("type1", true, Collections.unmodifiableMap(mappers));
 
         RootObjectMapper merged = (RootObjectMapper)rootObjectMapper.merge(mergeWith);
@@ -99,10 +100,10 @@ public class ObjectMapperMergeTests extends ESTestCase {
     public void testMergeDisabledRootMapper() {
         String type = MapperService.SINGLE_MAPPING_NAME;
         final RootObjectMapper rootObjectMapper =
-            (RootObjectMapper) new RootObjectMapper.Builder(type).enabled(false).build(new ContentPath());
+            (RootObjectMapper) new RootObjectMapper.Builder(type, Version.CURRENT).enabled(false).build(new ContentPath());
         //the root is disabled, and we are not trying to re-enable it, but we do want to be able to add runtime fields
         final RootObjectMapper mergeWith =
-            new RootObjectMapper.Builder(type).setRuntime(
+            new RootObjectMapper.Builder(type, Version.CURRENT).setRuntime(
                 Collections.singletonMap("test", new TestRuntimeField("test", "long"))).build(new ContentPath());
 
         RootObjectMapper merged = (RootObjectMapper) rootObjectMapper.merge(mergeWith);
@@ -112,27 +113,23 @@ public class ObjectMapperMergeTests extends ESTestCase {
     }
 
     public void testMergeNested() {
-
-        NestedObjectMapper firstMapper = new NestedObjectMapper.Builder("nested1", Version.CURRENT)
-            .includeInParent(true)
-            .includeInRoot(true)
-            .build(new ContentPath());
-        NestedObjectMapper secondMapper = new NestedObjectMapper.Builder("nested1", Version.CURRENT)
-            .includeInParent(false)
-            .includeInRoot(true)
-            .build(new ContentPath());
+        String type = MapperService.SINGLE_MAPPING_NAME;
+        ObjectMapper firstMapper = createNestedMapper(type,
+            ObjectMapper.Nested.newNested(new Explicit<>(true, true), new Explicit<>(true, true)));
+        ObjectMapper secondMapper = createNestedMapper(type,
+            ObjectMapper.Nested.newNested(new Explicit<>(false, true), new Explicit<>(false, false)));
 
         MapperException e = expectThrows(MapperException.class, () -> firstMapper.merge(secondMapper));
         assertThat(e.getMessage(), containsString("[include_in_parent] parameter can't be updated on a nested object mapping"));
 
-        NestedObjectMapper result = (NestedObjectMapper) firstMapper.merge(secondMapper, MapperService.MergeReason.INDEX_TEMPLATE);
-        assertFalse(result.isIncludeInParent());
-        assertTrue(result.isIncludeInRoot());
+        ObjectMapper result = firstMapper.merge(secondMapper, MapperService.MergeReason.INDEX_TEMPLATE);
+        assertFalse(result.nested().isIncludeInParent());
+        assertTrue(result.nested().isIncludeInRoot());
     }
 
     private static RootObjectMapper createRootObjectMapper(String name, boolean enabled, Map<String, Mapper> mappers) {
         final RootObjectMapper rootObjectMapper
-            = (RootObjectMapper) new RootObjectMapper.Builder(name).enabled(enabled).build(new ContentPath());
+            = (RootObjectMapper) new RootObjectMapper.Builder(name, Version.CURRENT).enabled(enabled).build(new ContentPath());
 
         mappers.values().forEach(rootObjectMapper::putMapper);
 
@@ -140,13 +137,19 @@ public class ObjectMapperMergeTests extends ESTestCase {
     }
 
     private static ObjectMapper createObjectMapper(String name, boolean enabled, Map<String, Mapper> mappers) {
-        final ObjectMapper mapper = new ObjectMapper.Builder(name).enabled(enabled).build(new ContentPath());
+        final ObjectMapper mapper = new ObjectMapper.Builder(name, Version.CURRENT).enabled(enabled).build(new ContentPath());
 
         mappers.values().forEach(mapper::putMapper);
 
         return mapper;
     }
 
+    private static ObjectMapper createNestedMapper(String name, ObjectMapper.Nested nested) {
+        return new ObjectMapper.Builder(name, Version.CURRENT)
+            .nested(nested)
+            .build(new ContentPath());
+    }
+
     private TextFieldMapper createTextFieldMapper(String name) {
         return new TextFieldMapper.Builder(name, createDefaultIndexAnalyzers()).build(new ContentPath());
     }

+ 1 - 1
server/src/test/java/org/elasticsearch/index/query/SearchExecutionContextTests.java

@@ -324,7 +324,7 @@ public class SearchExecutionContextTests extends ESTestCase {
 
     private static MappingLookup createMappingLookup(List<MappedFieldType> concreteFields, List<RuntimeField> runtimeFields) {
         List<FieldMapper> mappers = concreteFields.stream().map(MockFieldMapper::new).collect(Collectors.toList());
-        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc");
+        RootObjectMapper.Builder builder = new RootObjectMapper.Builder("_doc", Version.CURRENT);
         Map<String, RuntimeField> runtimeFieldTypes = runtimeFields.stream().collect(Collectors.toMap(RuntimeField::name, r -> r));
         builder.setRuntime(runtimeFieldTypes);
         Mapping mapping = new Mapping(builder.build(new ContentPath()), new MetadataFieldMapper[0], Collections.emptyMap());

+ 2 - 3
server/src/test/java/org/elasticsearch/search/aggregations/bucket/nested/NestedAggregatorTests.java

@@ -35,7 +35,6 @@ import org.elasticsearch.index.mapper.ContentPath;
 import org.elasticsearch.index.mapper.IdFieldMapper;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
 import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NestedPathFieldMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
@@ -900,7 +899,7 @@ public class NestedAggregatorTests extends AggregatorTestCase {
         nestedObject("nested_field")
     );
 
-    public static NestedObjectMapper nestedObject(String path) {
-        return new NestedObjectMapper.Builder(path, Version.CURRENT).build(new ContentPath());
+    public static ObjectMapper nestedObject(String path) {
+        return new ObjectMapper.Builder(path, Version.CURRENT).nested(ObjectMapper.Nested.newNested()).build(new ContentPath());
     }
 }

+ 3 - 3
server/src/test/java/org/elasticsearch/search/sort/AbstractSortTestCase.java

@@ -29,14 +29,14 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldDataCache;
 import org.elasticsearch.index.mapper.ContentPath;
 import org.elasticsearch.index.mapper.MappedFieldType;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
+import org.elasticsearch.index.mapper.ObjectMapper.Nested;
 import org.elasticsearch.index.query.IdsQueryBuilder;
 import org.elasticsearch.index.query.MatchAllQueryBuilder;
 import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.Rewriteable;
 import org.elasticsearch.index.query.SearchExecutionContext;
+import org.elasticsearch.index.query.Rewriteable;
 import org.elasticsearch.index.query.TermQueryBuilder;
 import org.elasticsearch.script.MockScriptEngine;
 import org.elasticsearch.script.ScriptEngine;
@@ -200,7 +200,7 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
 
             @Override
             public ObjectMapper getObjectMapper(String name) {
-                return new NestedObjectMapper.Builder(name, Version.CURRENT).build(new ContentPath());
+                return new ObjectMapper.Builder(name, Version.CURRENT).nested(Nested.newNested()).build(new ContentPath());
             }
         };
     }

+ 1 - 2
test/framework/src/main/java/org/elasticsearch/search/aggregations/AggregatorTestCase.java

@@ -81,7 +81,6 @@ import org.elasticsearch.index.mapper.MapperRegistry;
 import org.elasticsearch.index.mapper.Mapping;
 import org.elasticsearch.index.mapper.MappingLookup;
 import org.elasticsearch.index.mapper.MockFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.index.mapper.RangeFieldMapper;
@@ -164,7 +163,7 @@ public abstract class AggregatorTestCase extends ESTestCase {
         ObjectMapper.CONTENT_TYPE, // Cannot aggregate objects
         GeoShapeFieldMapper.CONTENT_TYPE, // Cannot aggregate geoshapes (yet)
 
-        NestedObjectMapper.CONTENT_TYPE, // TODO support for nested
+        ObjectMapper.NESTED_CONTENT_TYPE, // TODO support for nested
         CompletionFieldMapper.CONTENT_TYPE, // TODO support completion
         FieldAliasMapper.CONTENT_TYPE // TODO support alias
     );

+ 4 - 4
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/Classification.java

@@ -19,8 +19,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.BooleanFieldMapper;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.LenientlyParsedPreProcessor;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.PreProcessor;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.StrictlyParsedPreProcessor;
@@ -140,7 +140,7 @@ public class Classification implements DataFrameAnalysis {
 
         Map<String, Object> classesMapping = new HashMap<>();
         classesMapping.put("dynamic", false);
-        classesMapping.put("type", NestedObjectMapper.CONTENT_TYPE);
+        classesMapping.put("type", ObjectMapper.NESTED_CONTENT_TYPE);
         classesMapping.put("properties", classesProperties);
 
         Map<String, Object> properties = new HashMap<>();
@@ -149,7 +149,7 @@ public class Classification implements DataFrameAnalysis {
 
         Map<String, Object> mapping = new HashMap<>();
         mapping.put("dynamic", false);
-        mapping.put("type", NestedObjectMapper.CONTENT_TYPE);
+        mapping.put("type", ObjectMapper.NESTED_CONTENT_TYPE);
         mapping.put("properties", properties);
 
         FEATURE_IMPORTANCE_MAPPING = Collections.unmodifiableMap(mapping);
@@ -414,7 +414,7 @@ public class Classification implements DataFrameAnalysis {
         topClassesProperties.put("class_score", Collections.singletonMap("type", NumberFieldMapper.NumberType.DOUBLE.typeName()));
 
         Map<String, Object> topClassesMapping = new HashMap<>();
-        topClassesMapping.put("type", NestedObjectMapper.CONTENT_TYPE);
+        topClassesMapping.put("type", ObjectMapper.NESTED_CONTENT_TYPE);
         topClassesMapping.put("properties", topClassesProperties);
 
         additionalProperties.put(resultsFieldName + ".top_classes", topClassesMapping);

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/OutlierDetection.java

@@ -16,8 +16,8 @@ import org.elasticsearch.common.xcontent.ObjectParser;
 import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.xpack.core.ml.inference.trainedmodel.InferenceConfig;
 import org.elasticsearch.xpack.core.ml.utils.ExceptionsHelper;
 
@@ -69,7 +69,7 @@ public class OutlierDetection implements DataFrameAnalysis {
 
         Map<String, Object> mapping = new HashMap<>();
         mapping.put("dynamic", false);
-        mapping.put("type", NestedObjectMapper.CONTENT_TYPE);
+        mapping.put("type", ObjectMapper.NESTED_CONTENT_TYPE);
         mapping.put("properties", properties);
 
         FEATURE_INFLUENCE_MAPPING = Collections.unmodifiableMap(mapping);

+ 2 - 2
x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/ml/dataframe/analyses/Regression.java

@@ -18,8 +18,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.common.xcontent.XContentParser;
 import org.elasticsearch.index.mapper.BooleanFieldMapper;
 import org.elasticsearch.index.mapper.KeywordFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.NumberFieldMapper;
+import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.LenientlyParsedPreProcessor;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.PreProcessor;
 import org.elasticsearch.xpack.core.ml.inference.preprocessing.StrictlyParsedPreProcessor;
@@ -114,7 +114,7 @@ public class Regression implements DataFrameAnalysis {
 
         Map<String, Object> mapping = new HashMap<>();
         mapping.put("dynamic", false);
-        mapping.put("type", NestedObjectMapper.CONTENT_TYPE);
+        mapping.put("type", ObjectMapper.NESTED_CONTENT_TYPE);
         mapping.put("properties", properties);
 
         FEATURE_IMPORTANCE_MAPPING = Collections.unmodifiableMap(mapping);

+ 2 - 3
x-pack/plugin/ml/src/main/java/org/elasticsearch/xpack/ml/dataframe/extractor/ExtractedFieldsDetector.java

@@ -17,7 +17,6 @@ import org.elasticsearch.common.regex.Regex;
 import org.elasticsearch.common.util.set.Sets;
 import org.elasticsearch.index.IndexSettings;
 import org.elasticsearch.index.mapper.BooleanFieldMapper;
-import org.elasticsearch.index.mapper.NestedObjectMapper;
 import org.elasticsearch.index.mapper.ObjectMapper;
 import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
 import org.elasticsearch.xpack.core.ml.dataframe.DataFrameAnalyticsConfig;
@@ -242,7 +241,7 @@ public class ExtractedFieldsDetector {
 
     private void addExcludedNestedPattern(String pattern, Set<FieldSelection> fieldSelection) {
         fieldSelection.add(FieldSelection.excluded(
-            pattern, Collections.singleton(NestedObjectMapper.CONTENT_TYPE), "nested fields are not supported"));
+            pattern, Collections.singleton(ObjectMapper.NESTED_CONTENT_TYPE), "nested fields are not supported"));
     }
 
     private Set<String> getMappingTypes(String field) {
@@ -658,6 +657,6 @@ public class ExtractedFieldsDetector {
     }
 
     private static boolean isNested(Set<String> types) {
-        return types.size() == 1 && types.contains(NestedObjectMapper.CONTENT_TYPE);
+        return types.size() == 1 && types.contains(ObjectMapper.NESTED_CONTENT_TYPE);
     }
 }