1
0
Эх сурвалжийг харах

Add an `include_type_name` option. (#29453)

This adds an `include_type_name` option to the `indices.create`,
`indices.get_mapping` and `indices.put_mapping` APIs, which defaults to `true`.
When set to `false`, then mappings will be returned directly in the body of
the `indices.get_mapping` API, without keying them by the type name, the
`indices.create` will expect mappings directly under the `mappings` key, and
the `indices.put_mapping` will use `_doc` as a type name and fail if a `type`
is provided explicitly.

Relates #15613
Adrien Grand 7 жил өмнө
parent
commit
6a6c0ea5e6

+ 25 - 0
docs/reference/indices/create-index.asciidoc

@@ -173,3 +173,28 @@ PUT test?wait_for_active_shards=2
 
 A detailed explanation of `wait_for_active_shards` and its possible values can be found
 <<index-wait-for-active-shards,here>>.
+
+[float]
+=== Skipping types
+
+Types are scheduled to be fully removed in Elasticsearch 8.0 and will not appear
+in requests or responses anymore. You can opt in for this future behaviour by
+setting `include_type_name=false` and putting mappings directly under `mappings`
+in the index creation call.
+
+Here is an example:
+
+[source,js]
+--------------------------------------------------
+PUT test?include_type_name=false
+{
+  "mappings": {
+    "properties": {
+      "foo": {
+        "type": "keyword"
+      }
+    }
+  }
+}
+--------------------------------------------------
+// CONSOLE

+ 45 - 0
docs/reference/indices/get-mapping.asciidoc

@@ -41,3 +41,48 @@ GET /_mapping
 --------------------------------------------------
 // CONSOLE
 // TEST[setup:twitter]
+
+[float]
+=== Skipping types
+
+Types are scheduled to be fully removed in Elasticsearch 8.0 and will not appear
+in requests or responses anymore. You can opt in for this future behaviour by
+setting `include_type_name=false` in the request, which will return mappings
+directly under `mappings` without keying by the type name.
+
+Here is an example:
+
+[source,js]
+--------------------------------------------------
+PUT test?include_type_name=false
+{
+  "mappings": {
+    "properties": {
+      "foo": {
+        "type": "keyword"
+      }
+    }
+  }
+}
+
+GET test/_mappings?include_type_name=false
+--------------------------------------------------
+// CONSOLE
+
+which returns
+
+[source,js]
+--------------------------------------------------
+{
+  "test": {
+    "mappings": {
+      "properties": {
+        "foo": {
+          "type": "keyword"
+        }
+      }
+    }
+  }
+}
+--------------------------------------------------
+// TESTRESPONSE

+ 51 - 0
docs/reference/indices/put-mapping.asciidoc

@@ -109,3 +109,54 @@ PUT my_index/_mapping/_doc
 
 Each <<mapping-params,mapping parameter>> specifies whether or not its setting
 can be updated on an existing field.
+
+[float]
+=== Skipping types
+
+Types are scheduled to be fully removed in Elasticsearch 8.0 and will not appear
+in requests or responses anymore. You can opt in for this future behaviour by
+setting `include_type_name=false`.
+
+NOTE: This should only be done on indices that have been created with
+`include_type_name=false` or that used `_doc` as a type name.
+
+The Console script from the above section is equivalent to the below invocation:
+
+[source,js]
+-----------------------------------
+PUT my_index?include_type_name=false <1>
+{
+  "mappings": {
+    "properties": {
+      "name": {
+        "properties": {
+          "first": {
+            "type": "text"
+          }
+        }
+      },
+      "user_id": {
+        "type": "keyword"
+      }
+    }
+  }
+}
+
+PUT my_index/_mapping?include_type_name=false
+{
+  "properties": {
+    "name": {
+      "properties": {
+        "last": { <2>
+          "type": "text"
+        }
+      }
+    },
+    "user_id": {
+      "type": "keyword",
+      "ignore_above": 100 <3>
+    }
+  }
+}
+-----------------------------------
+// CONSOLE

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json

@@ -13,6 +13,10 @@
         }
       },
       "params": {
+        "include_type_name": {
+          "type" : "string",
+          "description" : "Whether a type should be expected in the body of the mappings."
+        },
         "wait_for_active_shards": {
           "type" : "string",
           "description" : "Set the number of active shards to wait for before the operation returns." 

+ 4 - 0
rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json

@@ -16,6 +16,10 @@
         }
       },
       "params": {
+        "include_type_name": {
+          "type" : "string",
+          "description" : "Whether to add the type name to the response"
+        },
         "ignore_unavailable": {
             "type" : "boolean",
             "description" : "Whether specified concrete indices should be ignored when unavailable (missing or closed)"

+ 5 - 2
rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json

@@ -4,7 +4,7 @@
     "methods": ["PUT", "POST"],
     "url": {
       "path": "/{index}/{type}/_mapping",
-      "paths": ["/{index}/{type}/_mapping", "/{index}/_mapping/{type}", "/_mapping/{type}", "/{index}/{type}/_mappings", "/{index}/_mappings/{type}", "/_mappings/{type}"],
+      "paths": ["/{index}/{type}/_mapping", "/{index}/_mapping/{type}", "/_mapping/{type}", "/{index}/{type}/_mappings", "/{index}/_mappings/{type}", "/_mappings/{type}", "{index}/_mappings", "{index}/_mapping"],
       "parts": {
         "index": {
           "type" : "list",
@@ -12,11 +12,14 @@
         },
         "type": {
           "type" : "string",
-          "required" : true,
           "description" : "The name of the document type"
         }
       },
       "params": {
+        "include_type_name": {
+          "type" : "string",
+          "description" : "Whether a type should be expected in the body of the mappings."
+        },
         "timeout": {
           "type" : "time",
           "description" : "Explicit operation timeout"

+ 81 - 0
rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_no_types.yml

@@ -0,0 +1,81 @@
+---
+"Create indices and manage mappings without types":
+
+  - skip:
+      version: " - 6.99.99"
+      reason:  include_type_name was introduced in 7.0.0
+
+  - do:
+      indices.create:
+        index: index
+        include_type_name: false
+        body:
+          mappings:
+            properties:
+              foo:
+                type: keyword
+
+  - do:
+      indices.get_mapping:
+        index: index
+        include_type_name: false
+
+  - match: { index.mappings.properties.foo.type: "keyword" }
+
+  - do:
+      indices.put_mapping:
+        index: index
+        include_type_name: false
+        body:
+          properties:
+            bar:
+              type: float
+
+  - do:
+      indices.get_mapping:
+        index: index
+        include_type_name: false
+
+  - match: { index.mappings.properties.foo.type: "keyword" }
+  - match: { index.mappings.properties.bar.type: "float" }
+
+---
+"PUT mapping with a type and include_type_name: false":
+
+  - skip:
+      version: " - 6.99.99"
+      reason:  include_type_name was introduced in 7.0.0
+
+  - do:
+      indices.create:
+        index: index
+
+  - do:
+      catch: /illegal_argument_exception/
+      indices.put_mapping:
+        index: index
+        type: _doc
+        include_type_name: false
+        body:
+          properties:
+            bar:
+              type: float
+
+---
+"Empty index with the include_type_name=false option":
+
+  - skip:
+      version: " - 6.99.99"
+      reason:  include_type_name was introduced in 7.0.0
+
+  - do:
+      indices.create:
+        index: index
+        include_type_name: false
+
+  - do:
+      indices.get_mapping:
+        index: index
+        include_type_name: false
+
+  - match: { index.mappings: {} }

+ 14 - 1
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java

@@ -23,12 +23,18 @@ import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
 import org.elasticsearch.action.support.ActiveShardCount;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.common.xcontent.LoggingDeprecationHandler;
+import org.elasticsearch.common.xcontent.XContentHelper;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestRequest;
 import org.elasticsearch.rest.action.RestToXContentListener;
 
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 public class RestCreateIndexAction extends BaseRestHandler {
     public RestCreateIndexAction(Settings settings, RestController controller) {
@@ -43,9 +49,16 @@ public class RestCreateIndexAction extends BaseRestHandler {
 
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
+        final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
         CreateIndexRequest createIndexRequest = new CreateIndexRequest(request.param("index"));
         if (request.hasContent()) {
-            createIndexRequest.source(request.content(), request.getXContentType());
+            Map<String, Object> sourceAsMap = XContentHelper.convertToMap(request.content(), false, request.getXContentType()).v2();
+            if (includeTypeName == false && sourceAsMap.containsKey("mappings")) {
+                Map<String, Object> newSourceAsMap = new HashMap<>(sourceAsMap);
+                newSourceAsMap.put("mappings", Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, sourceAsMap.get("mappings")));
+                sourceAsMap = newSourceAsMap;
+            }
+            createIndexRequest.source(sourceAsMap, LoggingDeprecationHandler.INSTANCE);
         }
         createIndexRequest.timeout(request.paramAsTime("timeout", createIndexRequest.timeout()));
         createIndexRequest.masterNodeTimeout(request.paramAsTime("master_timeout", createIndexRequest.masterNodeTimeout()));

+ 21 - 4
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java

@@ -77,6 +77,7 @@ public class RestGetMappingAction extends BaseRestHandler {
 
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
+        final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
         final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
         final String[] types = request.paramAsStringArrayOrEmptyIfAll("type");
         final GetMappingsRequest getMappingsRequest = new GetMappingsRequest();
@@ -141,13 +142,29 @@ public class RestGetMappingAction extends BaseRestHandler {
                     for (final ObjectObjectCursor<String, ImmutableOpenMap<String, MappingMetaData>> indexEntry : mappingsByIndex) {
                         builder.startObject(indexEntry.key);
                         {
-                            builder.startObject("mappings");
-                            {
+                            if (includeTypeName == false) {
+                                MappingMetaData mappings = null;
                                 for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
-                                    builder.field(typeEntry.key, typeEntry.value.sourceAsMap());
+                                    if (typeEntry.key.equals("_default_") == false) {
+                                        assert mappings == null;
+                                        mappings = typeEntry.value;
+                                    }
                                 }
+                                if (mappings == null) {
+                                    // no mappings yet
+                                    builder.startObject("mappings").endObject();
+                                } else {
+                                    builder.field("mappings", mappings.sourceAsMap());
+                                }
+                            } else {
+                                builder.startObject("mappings");
+                                {
+                                    for (final ObjectObjectCursor<String, MappingMetaData> typeEntry : indexEntry.value) {
+                                        builder.field(typeEntry.key, typeEntry.value.sourceAsMap());
+                                    }
+                                }
+                                builder.endObject();
                             }
-                            builder.endObject();
                         }
                         builder.endObject();
                     }

+ 7 - 1
server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutMappingAction.java

@@ -24,6 +24,7 @@ import org.elasticsearch.action.support.IndicesOptions;
 import org.elasticsearch.client.node.NodeClient;
 import org.elasticsearch.common.Strings;
 import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.index.mapper.MapperService;
 import org.elasticsearch.rest.BaseRestHandler;
 import org.elasticsearch.rest.RestController;
 import org.elasticsearch.rest.RestRequest;
@@ -67,8 +68,13 @@ public class RestPutMappingAction extends BaseRestHandler {
 
     @Override
     public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
+        final boolean includeTypeName = request.paramAsBoolean("include_type_name", true);
         PutMappingRequest putMappingRequest = putMappingRequest(Strings.splitStringByCommaToArray(request.param("index")));
-        putMappingRequest.type(request.param("type"));
+        final String type = request.param("type");
+        if (type != null && includeTypeName == false) {
+            throw new IllegalArgumentException("Cannot set include_type_name=false and provide a type at the same time");
+        }
+        putMappingRequest.type(includeTypeName ? type : MapperService.SINGLE_MAPPING_NAME);
         putMappingRequest.source(request.requiredContent(), request.getXContentType());
         putMappingRequest.timeout(request.paramAsTime("timeout", putMappingRequest.timeout()));
         putMappingRequest.masterNodeTimeout(request.paramAsTime("master_timeout", putMappingRequest.masterNodeTimeout()));