From f339b479d267779543e694a6c6e71a36aa62e07f Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 11 Apr 2018 15:54:16 +0200 Subject: [PATCH] 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. On 5.x indices, get-mapping will fail if the index has multiple mappings, and put-mapping will update or introduce mappings for the `_doc` type instead of updating existing mappings. This oddity is required so that we don't have to introduce a new flag to put-mapping requests to know whether they are actually updating the `_doc` type or performing a typeless call. Relates #15613 --- docs/reference/indices/create-index.asciidoc | 26 +++++ docs/reference/indices/get-mapping.asciidoc | 48 ++++++++ docs/reference/indices/put-mapping.asciidoc | 37 ++++++ .../upgrades/FullClusterRestartIT.java | 110 ++++++++++++++++++ .../rest-api-spec/api/indices.create.json | 4 + .../api/indices.get_mapping.json | 4 + .../api/indices.put_mapping.json | 7 +- .../70_mix_typeless_typeful.yml | 23 ++++ .../20_mix_typeless_typeful.yml | 52 +++++++++ .../test/indices.put_mapping/20_no_types.yml | 81 +++++++++++++ .../mapping/get/GetMappingsResponse.java | 5 +- .../metadata/MetaDataMappingService.java | 29 ++++- .../admin/indices/RestCreateIndexAction.java | 15 ++- .../admin/indices/RestGetMappingAction.java | 3 +- .../admin/indices/RestPutMappingAction.java | 8 +- 15 files changed, 442 insertions(+), 10 deletions(-) create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/70_mix_typeless_typeful.yml create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_mix_typeless_typeful.yml create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_no_types.yml diff --git a/docs/reference/indices/create-index.asciidoc b/docs/reference/indices/create-index.asciidoc index b7629a7cad566..17ad190b8805d 100644 --- a/docs/reference/indices/create-index.asciidoc +++ b/docs/reference/indices/create-index.asciidoc @@ -177,3 +177,29 @@ PUT test?wait_for_active_shards=2 A detailed explanation of `wait_for_active_shards` and its possible values can be found <>. + +[float] +=== Skipping types + +Types are being removed from Elasticsearch: in 7.0, the `mappings` element will +no longer take the type name as a top-level key. You can already opt in for this +behavior by setting setting `include_type_name=false` and putting mappings +directly under `mappings` in the index creation call, without specifying a type +name. + +Here is an example: + +[source,js] +-------------------------------------------------- +PUT test?include_type_name=false +{ + "mappings": { + "properties": { + "foo": { + "type": "keyword" + } + } + } +} +-------------------------------------------------- +// CONSOLE diff --git a/docs/reference/indices/get-mapping.asciidoc b/docs/reference/indices/get-mapping.asciidoc index 953f9522a4128..6ce78aed6fd35 100644 --- a/docs/reference/indices/get-mapping.asciidoc +++ b/docs/reference/indices/get-mapping.asciidoc @@ -41,3 +41,51 @@ GET /_mapping -------------------------------------------------- // CONSOLE // TEST[setup:twitter] + +[float] +=== Skipping types + +Types are being removed from Elasticsearch: in 7.0, the `mappings` element will +no longer take the type name as a top-level key. You can already opt in for this +behavior by setting setting `include_type_name=false` on the request. + +NOTE: Such calls will be rejected on indices that have multiple types as it +introduces ambiguity as to which mapping should be returned. Only indices +created by Elasticsearch 5.x may have multiple types. + +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 diff --git a/docs/reference/indices/put-mapping.asciidoc b/docs/reference/indices/put-mapping.asciidoc index 84838d67e1d03..1813a831b90cb 100644 --- a/docs/reference/indices/put-mapping.asciidoc +++ b/docs/reference/indices/put-mapping.asciidoc @@ -114,3 +114,40 @@ PUT my_index/_mapping/_doc Each <> specifies whether or not its setting can be updated on an existing field. + +[float] +=== Skipping types + +Types are being removed from Elasticsearch: in 7.0, the `mappings` element will +no longer take the type name as a top-level key. You can already opt in for this +behavior by setting setting `include_type_name=false` and putting mappings +directly under `mappings` in the put mapping call, without specifying a type +name. + +NOTE: On indices created on Elasticsearch 5.x, such calls will actually +introduce or update mappings for the `_doc` type. It is recommended to avoid +calling the put-mapping API with `include_type_name=false` on 5.x indices. + +Here is an example: + +[source,js] +----------------------------------- +PUT my_index?include_type_name=false <1> +{ + "mappings": { + "properties": { + "name": { + "properties": { + "first": { + "type": "text" + } + } + }, + "user_id": { + "type": "keyword" + } + } + } +} +----------------------------------- +// CONSOLE diff --git a/qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java b/qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java index c2e1031c4f299..86636c9cd5f85 100644 --- a/qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java +++ b/qa/full-cluster-restart/src/test/java/org/elasticsearch/upgrades/FullClusterRestartIT.java @@ -35,6 +35,7 @@ import org.elasticsearch.test.NotEqualMessageBuilder; import org.elasticsearch.test.rest.ESRestTestCase; import org.elasticsearch.test.rest.yaml.ObjectPath; +import org.hamcrest.Matchers; import org.junit.Before; import java.io.IOException; @@ -1003,6 +1004,115 @@ public void testSoftDeletes() throws Exception { } } + public void testIncludeTypeNameOnMultiTypesIndices() throws IOException { + assumeTrue("This test only makes sense if the old cluster runs a version that supports types", + getOldClusterVersion().before(Version.V_6_0_0)); + if (isRunningAgainstOldCluster()) { + String mapping = Strings.toString(JsonXContent.contentBuilder() + .startObject() + .startObject("mappings") + .startObject("type1") + .startObject("properties") + .startObject("foo") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .startObject("type2") + .startObject("properties") + .startObject("foo") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject()); + Request createIndexRequest = new Request("PUT", "/" + index); + createIndexRequest.setJsonEntity(mapping); + client().performRequest(createIndexRequest); + } else { + // GET mappings + Request getMappingsRequest = new Request("GET", index + "/_mappings"); + getMappingsRequest.addParameter("include_type_name", "false"); + ResponseException ex = expectThrows(ResponseException.class, () -> client().performRequest(getMappingsRequest)); + assertThat(EntityUtils.toString(ex.getResponse().getEntity()), Matchers.containsString("has multiple mappings")); + + // PUT mappings + Request putMappingsRequest = new Request("PUT", index + "/_mappings"); + putMappingsRequest.addParameter("include_type_name", "false"); + String mapping = Strings.toString(JsonXContent.contentBuilder() + .startObject() + .startObject("properties") + .startObject("bar") + .field("type", "long") + .endObject() + .endObject() + .endObject()); + putMappingsRequest.setJsonEntity(mapping); + client().performRequest(putMappingsRequest); + + Request getMappingsRequest2 = new Request("GET", index + "/_mappings"); + Response getMappingsResponse = client().performRequest(getMappingsRequest2); + String getMappingsResponseAsString = EntityUtils.toString(getMappingsResponse.getEntity()); + // we introduced a _doc mapping + assertThat(getMappingsResponseAsString, Matchers.containsString("\"_doc\":{")); + } + } + + public void testIncludeTypeNameOnSingleTypeIndices() throws IOException { + if (isRunningAgainstOldCluster()) { + String mapping = Strings.toString(JsonXContent.contentBuilder() + .startObject() + .startObject("mappings") + .startObject("my_type") + .startObject("properties") + .startObject("foo") + .field("type", "keyword") + .endObject() + .endObject() + .endObject() + .endObject() + .endObject()); + Request request = new Request("PUT", "/" + index); + request.setJsonEntity(mapping); + client().performRequest(request); + } else { + // PUT mappings + Request putMappingsRequest = new Request("PUT", index + "/_mappings"); + putMappingsRequest.addParameter("include_type_name", "false"); + String mapping = Strings.toString(JsonXContent.contentBuilder() + .startObject() + .startObject("properties") + .startObject("bar") + .field("type", "long") + .endObject() + .endObject() + .endObject()); + putMappingsRequest.setJsonEntity(mapping); + client().performRequest(putMappingsRequest); + + // GET mappings + Request getMappingsRequest = new Request("GET", index + "/_mappings"); + getMappingsRequest.addParameter("include_type_name", "false"); + if (getOldClusterVersion().before(Version.V_6_0_0)) { + ResponseException ex = expectThrows(ResponseException.class, () -> client().performRequest(getMappingsRequest)); + assertThat(EntityUtils.toString(ex.getResponse().getEntity()), Matchers.containsString("has multiple mappings")); + Request getMappingsRequest2 = new Request("GET", index + "/_mappings"); + Response getMappingsResponse = client().performRequest(getMappingsRequest2); + String getMappingsResponseAsString = EntityUtils.toString(getMappingsResponse.getEntity()); + // we introduced a _doc mapping + assertThat(getMappingsResponseAsString, Matchers.containsString("\"_doc\":{")); + assertThat(getMappingsResponseAsString, Matchers.containsString("\"my_type\":{")); + } else { + Response getMappingsResponse = client().performRequest(getMappingsRequest); + String getMappingsResponseAsString = EntityUtils.toString(getMappingsResponse.getEntity()); + assertThat(getMappingsResponseAsString, Matchers.not(Matchers.containsString("my_type"))); // no type name + assertThat(getMappingsResponseAsString, Matchers.containsString("\"bar\":{\"type\":\"long\"}")); // new field is here + assertThat(getMappingsResponseAsString, Matchers.containsString("\"foo\":{\"type\":\"keyword\"}")); // old field is here too + } + } + } + private void checkSnapshot(String snapshotName, int count, Version tookOnVersion) throws IOException { // Check the snapshot metadata, especially the version Request listSnapshotRequest = new Request("GET", "/_snapshot/repo/" + snapshotName); diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json index 1433c893e251e..ba58fd8da6c06 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.create.json +++ b/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." diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json index e29ed43b3e451..9bfb9c76abf82 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.get_mapping.json +++ b/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)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json b/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json index 5fce0bcefc89a..5c8cb76c11d45 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/indices.put_mapping.json +++ b/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" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/70_mix_typeless_typeful.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/70_mix_typeless_typeful.yml new file mode 100644 index 0000000000000..144a7cbb39e52 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/70_mix_typeless_typeful.yml @@ -0,0 +1,23 @@ +--- +"GET mapping with typeless API on an index that has types": + + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7.0 + + - do: + indices.create: # not using include_type_name: false on purpose + index: index + body: + mappings: + not_doc: + properties: + foo: + type: "keyword" + + - do: + indices.get_mapping: + include_type_name: false + index: index + + - match: { index.mappings.properties.foo.type: "keyword" } diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_mix_typeless_typeful.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_mix_typeless_typeful.yml new file mode 100644 index 0000000000000..c3b57c4cabdfe --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_mix_typeless_typeful.yml @@ -0,0 +1,52 @@ +--- +"PUT mapping with typeless API on an index that has types": + + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7.0 + + - do: + indices.create: # not using include_type_name: false on purpose + index: index + body: + mappings: + not_doc: + properties: + foo: + type: "keyword" + + - do: + indices.put_mapping: + include_type_name: false + index: index + body: + properties: + bar: + type: "long" + + - do: + indices.get_mapping: + include_type_name: false + index: index + + - match: { index.mappings.properties.foo.type: "keyword" } + - match: { index.mappings.properties.bar.type: "long" } + + - do: + indices.put_mapping: + include_type_name: false + index: index + body: + properties: + foo: + type: "keyword" # also test no-op updates that trigger special logic wrt the mapping version + + - do: + catch: bad_request + indices.put_mapping: + index: index + body: + some_other_type: + properties: + bar: + type: "long" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_no_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/20_no_types.yml new file mode 100644 index 0000000000000..347bb5c361715 --- /dev/null +++ b/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.6.99" + reason: include_type_name was introduced in 6.7.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.6.99" + reason: include_type_name was introduced in 6.7.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.6.99" + reason: include_type_name was introduced in 6.7.0 + + - do: + indices.create: + index: index + include_type_name: false + + - do: + indices.get_mapping: + index: index + include_type_name: false + + - match: { index.mappings: {} } diff --git a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java index 2bf52151d4b14..bb285e90a199f 100644 --- a/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java +++ b/server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java @@ -131,7 +131,10 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params, boolea MappingMetaData mappings = null; for (final ObjectObjectCursor typeEntry : indexEntry.value) { if (typeEntry.key.equals("_default_") == false) { - assert mappings == null; + if (mappings != null) { + throw new IllegalArgumentException("Cannot use [include_type_name=false] on index [" + indexEntry.key + + "] that has multiple mappings: " + indexEntry.value.keys()); + } mappings = typeEntry.value; } } diff --git a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java index 53d6457a217ef..c035c73a42e10 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.message.ParameterizedMessage; +import org.elasticsearch.Version; import org.elasticsearch.action.ActionListener; import org.elasticsearch.action.admin.indices.mapping.put.PutMappingClusterStateUpdateRequest; import org.elasticsearch.cluster.AckedClusterStateTaskListener; @@ -50,6 +51,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -239,6 +241,21 @@ class PutMappingExecutor implements ClusterStateTaskExecutor docMappersIt = mapperService.docMappers(false).iterator(); + if (docMappersIt.hasNext()) { + mapper = docMappersIt.next(); + } + if (docMappersIt.hasNext()) { + throw new AssertionError("Index has multiple types: " + mapperService.types()); + } + } + return mapper; + } + private ClusterState applyRequest(ClusterState currentState, PutMappingClusterStateUpdateRequest request, Map indexMapperServices) throws IOException { String mappingType = request.type(); @@ -256,8 +273,10 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt updateList.add(indexMetaData); // try and parse it (no need to add it here) so we can bail early in case of parsing exception DocumentMapper newMapper; - DocumentMapper existingMapper = mapperService.documentMapper(request.type()); - if (MapperService.DEFAULT_MAPPING.equals(request.type())) { + DocumentMapper existingMapper = getMapperForUpdate(mapperService, mappingType); + String typeForUpdate = existingMapper == null ? mappingType : existingMapper.type(); + + if (MapperService.DEFAULT_MAPPING.equals(typeForUpdate)) { // _default_ types do not go through merging, but we do test the new settings. Also don't apply the old default newMapper = mapperService.parse(request.type(), mappingUpdateSource, false); } else { @@ -306,12 +325,14 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt // 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; CompressedXContent existingSource = null; - DocumentMapper existingMapper = mapperService.documentMapper(mappingType); + DocumentMapper existingMapper = getMapperForUpdate(mapperService, mappingType); if (existingMapper != null) { + typeForUpdate = existingMapper.type(); existingSource = existingMapper.mappingSource(); } - DocumentMapper mergedMapper = mapperService.merge(mappingType, mappingUpdateSource, + DocumentMapper mergedMapper = mapperService.merge(typeForUpdate, mappingUpdateSource, MergeReason.MAPPING_UPDATE, request.updateAllTypes()); CompressedXContent updatedSource = mergedMapper.mappingSource(); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java index a95c984bef51b..d38992fae00c4 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestCreateIndexAction.java @@ -25,12 +25,18 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.logging.DeprecationLogger; 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 { @@ -48,9 +54,16 @@ public String getName() { @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 sourceAsMap = XContentHelper.convertToMap(request.content(), false, request.getXContentType()).v2(); + if (includeTypeName == false && sourceAsMap.containsKey("mappings")) { + Map newSourceAsMap = new HashMap<>(sourceAsMap); + newSourceAsMap.put("mappings", Collections.singletonMap(MapperService.SINGLE_MAPPING_NAME, sourceAsMap.get("mappings"))); + sourceAsMap = newSourceAsMap; + } + createIndexRequest.source(sourceAsMap, LoggingDeprecationHandler.INSTANCE); } if (request.hasParam("update_all_types")) { DEPRECATION_LOGGER.deprecated("[update_all_types] is deprecated since indices may not have more than one type anymore"); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java index bf3c3a5fa6029..534aa26d55d26 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java @@ -81,6 +81,7 @@ public String getName() { @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(); @@ -133,7 +134,7 @@ public RestResponse buildResponse(final GetMappingsResponse response, final XCon builder.field("error", message); builder.field("status", status.getStatus()); } - response.toXContent(builder, ToXContent.EMPTY_PARAMS); + response.toXContent(builder, ToXContent.EMPTY_PARAMS, includeTypeName); } builder.endObject(); diff --git a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutMappingAction.java b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutMappingAction.java index e15e45f653ce5..b4de25ab06d59 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutMappingAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestPutMappingAction.java @@ -26,6 +26,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.logging.DeprecationLogger; 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; @@ -72,8 +73,13 @@ public String getName() { @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()); if (request.hasParam("update_all_types")) { DEPRECATION_LOGGER.deprecated("[update_all_types] is deprecated since indices may not have more than one type anymore");