Bladeren bron

Make sure to use the resolved type in DocumentMapperService#extractMappings. (#37451)

* Pull out a shared method MapperService#resolveDocumentType.
* Make sure to resolve the type when extracting the mappings.

Addresses #36811.
Julie Tibshirani 6 jaren geleden
bovenliggende
commit
1a1dbf705f

+ 38 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/index/70_mix_typeless_typeful.yml

@@ -60,3 +60,41 @@
  - match:   { _id:      $id}
  - match:   { _version: 1}
  - match:   { _source: { foo: bar }}
+
+---
+"Index call that introduces new field mappings":
+
+  - skip:
+      version: " - 6.99.99"
+      reason:  Typeless APIs were introduced in 7.0.0
+
+  - do:
+      indices.create: # not using include_type_name: false on purpose
+        index: index
+        body:
+          mappings:
+            not_doc:
+              properties:
+                foo:
+                  type: "keyword"
+  - do:
+      index:
+        index:  index
+        id:     2
+        body:   { new_field: value }
+
+  - match:   { _index:   "index" }
+  - match:   { _type:    "_doc" }
+  - match:   { _id:      "2" }
+  - match:   { _version: 1 }
+
+  - do:
+      get: # using typeful API on purpose
+        index:  index
+        type:   not_doc
+        id:     2
+
+  - match:   { _index:   "index" }
+  - match:   { _type:    "not_doc" }
+  - match:   { _id:      "2" }
+  - match:   { _version: 1}

+ 45 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/update/90_mix_typeless_typeful.yml

@@ -37,3 +37,48 @@
           id:     1
 
  - match: { _source.foo:        baz }
+
+---
+"Update call that introduces new field mappings":
+
+  - skip:
+      version: " - 6.99.99"
+      reason:  Typeless APIs were introduced in 7.0.0
+
+  - do:
+      indices.create: # not using include_type_name: false on purpose
+        index: index
+        body:
+          mappings:
+            not_doc:
+              properties:
+                foo:
+                  type: "keyword"
+
+  - do:
+      index:
+        index:  index
+        type:   not_doc
+        id:     1
+        body:   { foo: bar }
+
+  - do:
+      update:
+        index:  index
+        id:     1
+        body:
+          doc:
+            foo:          baz
+            new_field:    value
+  - do:
+      get: # using typeful API on purpose
+        index:  index
+        type:   not_doc
+        id:     1
+
+  - match:   { _index:   "index" }
+  - match:   { _type:    "not_doc" }
+  - match:   { _id:      "1" }
+  - match:   { _version: 2}
+  - match: { _source.foo:        baz }
+  - match: { _source.new_field:  value }

+ 9 - 12
server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java

@@ -276,8 +276,9 @@ public class MetaDataMappingService {
                 }
                 if (mappingType == null) {
                     mappingType = newMapper.type();
-                } else if (mappingType.equals(newMapper.type()) == false) {
-                    throw new InvalidTypeNameException("Type name provided does not match type name within mapping definition");
+                } else if (mappingType.equals(newMapper.type()) == false
+                        && mapperService.resolveDocumentType(mappingType).equals(newMapper.type()) == false) {
+                    throw new InvalidTypeNameException("Type name provided does not match type name within mapping definition.");
                 }
             }
             assert mappingType != null;
@@ -295,16 +296,12 @@ public class MetaDataMappingService {
                 // we use the exact same indexService and metadata we used to validate above here to actually apply the update
                 final Index index = indexMetaData.getIndex();
                 final MapperService mapperService = indexMapperServices.get(index);
-                String typeForUpdate = mappingType; // the type to use to apply the mapping update
-                if (MapperService.SINGLE_MAPPING_NAME.equals(typeForUpdate)) {
-                    // If the user gave _doc as a special type value or if (s)he is using the new typeless APIs,
-                    // then we apply the mapping update to the existing type. This allows to move to typeless
-                    // APIs with indices whose type name is different from `_doc`.
-                    DocumentMapper mapper = mapperService.documentMapper();
-                    if (mapper != null) {
-                        typeForUpdate = mapper.type();
-                    }
-                }
+
+                // If the user gave _doc as a special type value or if they are using the new typeless APIs,
+                // then we apply the mapping update to the existing type. This allows to move to typeless
+                // APIs with indices whose type name is different from `_doc`.
+                String typeForUpdate = mapperService.resolveDocumentType(mappingType); // the type to use to apply the mapping update
+
                 CompressedXContent existingSource = null;
                 DocumentMapper existingMapper = mapperService.documentMapper(typeForUpdate);
                 if (existingMapper != null) {

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

@@ -197,7 +197,7 @@ public class DocumentMapperParser {
 
         String rootName = root.keySet().iterator().next();
         Tuple<String, Map<String, Object>> mapping;
-        if (type == null || type.equals(rootName)) {
+        if (type == null || type.equals(rootName) || mapperService.resolveDocumentType(type).equals(rootName)) {
             mapping = new Tuple<>(rootName, (Map<String, Object>) root.get(rootName));
         } else {
             mapping = new Tuple<>(type, root);

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

@@ -665,6 +665,23 @@ public class MapperService extends AbstractIndexComponent implements Closeable {
         return null;
     }
 
+    /**
+     * Resolves a type from a mapping-related request into the type that should be used when
+     * merging and updating mappings.
+     *
+     * If the special `_doc` type is provided, then we replace it with the actual type that is
+     * being used in the mappings. This allows typeless APIs such as 'index' or 'put mappings'
+     * to work against indices with a custom type name.
+     */
+    public String resolveDocumentType(String type) {
+        if (MapperService.SINGLE_MAPPING_NAME.equals(type)) {
+            if (mapper != null) {
+                return mapper.type();
+            }
+        }
+        return type;
+    }
+
     /**
      * Returns the document mapper created, including a mapping update if the
      * type has been dynamically created.

+ 9 - 21
server/src/main/java/org/elasticsearch/index/shard/IndexShard.java

@@ -721,7 +721,7 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
         ensureWriteAllowed(origin);
         Engine.Index operation;
         try {
-            final String resolvedType = resolveType(sourceToParse.type());
+            final String resolvedType = mapperService.resolveDocumentType(sourceToParse.type());
             final SourceToParse sourceWithResolvedType;
             if (resolvedType.equals(sourceToParse.type())) {
                 sourceWithResolvedType = sourceToParse;
@@ -844,11 +844,12 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
         } catch (MapperParsingException | IllegalArgumentException | TypeMissingException e) {
             return new Engine.DeleteResult(e, version, operationPrimaryTerm, seqNo, false);
         }
-        if (resolveType(type).equals(mapperService.documentMapper().type()) == false) {
+        if (mapperService.resolveDocumentType(type).equals(mapperService.documentMapper().type()) == false) {
             // We should never get there due to the fact that we generate mapping updates on deletes,
             // but we still prefer to have a hard exception here as we would otherwise delete a
             // document in the wrong type.
-            throw new IllegalStateException("Deleting document from type [" + resolveType(type) + "] while current type is [" +
+            throw new IllegalStateException("Deleting document from type [" +
+                    mapperService.resolveDocumentType(type) + "] while current type is [" +
                     mapperService.documentMapper().type() + "]");
         }
         final Term uid = new Term(IdFieldMapper.NAME, Uid.encodeId(id));
@@ -861,8 +862,8 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
                                                VersionType versionType, Engine.Operation.Origin origin,
                                                long ifSeqNo, long ifPrimaryTerm) {
         long startTime = System.nanoTime();
-        return new Engine.Delete(resolveType(type), id, uid, seqNo, primaryTerm, version, versionType, origin, startTime,
-            ifSeqNo, ifPrimaryTerm);
+        return new Engine.Delete(mapperService.resolveDocumentType(type), id, uid, seqNo, primaryTerm, version, versionType,
+            origin, startTime, ifSeqNo, ifPrimaryTerm);
     }
 
     private Engine.DeleteResult delete(Engine engine, Engine.Delete delete) throws IOException {
@@ -885,7 +886,7 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
     public Engine.GetResult get(Engine.Get get) {
         readAllowed();
         DocumentMapper mapper = mapperService.documentMapper();
-        if (mapper == null || mapper.type().equals(resolveType(get.type())) == false) {
+        if (mapper == null || mapper.type().equals(mapperService.resolveDocumentType(get.type())) == false) {
             return GetResult.NOT_EXISTS;
         }
         return getEngine().get(get, this::acquireSearcher);
@@ -2319,23 +2320,10 @@ public class IndexShard extends AbstractIndexShardComponent implements IndicesCl
         }
     }
 
-    /**
-     * If an index/update/get/delete operation is using the special `_doc` type, then we replace
-     * it with the actual type that is being used in the mappings so that users may use typeless
-     * APIs with indices that have types.
-     */
-    private String resolveType(String type) {
-        if (MapperService.SINGLE_MAPPING_NAME.equals(type)) {
-            DocumentMapper docMapper = mapperService.documentMapper();
-            if (docMapper != null) {
-                return docMapper.type();
-            }
-        }
-        return type;
-    }
 
     private DocumentMapperForType docMapper(String type) {
-        return mapperService.documentMapperWithAutoCreate(resolveType(type));
+        return mapperService.documentMapperWithAutoCreate(
+            mapperService.resolveDocumentType(type));
     }
 
     private EngineConfig newEngineConfig() {