From cd904337c6651578ab13ee4b1c74bdacb7b1a061 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 9 Jan 2019 19:23:23 +0100 Subject: [PATCH] Add an `include_type_name` option to 6.x. (#29453) (#37147) 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 | 25 +++ docs/reference/indices/get-mapping.asciidoc | 48 ++++ docs/reference/indices/put-mapping.asciidoc | 36 +++ .../upgrades/FullClusterRestartIT.java | 110 +++++++++ .../rest-api-spec/api/indices.create.json | 4 + .../api/indices.get_mapping.json | 4 + .../api/indices.put_mapping.json | 7 +- .../test/indices.create/10_basic.yml | 80 +++---- .../indices.create/11_basic_with_types.yml | 134 +++++++++++ .../test/indices.get_mapping/10_basic.yml | 158 +++++-------- .../11_basic_with_types.yml | 144 ++++++++++++ .../70_mix_typeless_typeful.yml | 23 ++ .../test/indices.put_mapping/10_basic.yml | 96 +++++--- .../11_basic_with_types.yml | 69 ++++++ .../20_mix_typeless_typeful.yml | 52 +++++ .../indices.put_mapping/all_path_options.yml | 199 +++++++---------- .../all_path_options_with_types.yml | 209 ++++++++++++++++++ .../mapping/get/GetMappingsResponse.java | 5 +- .../metadata/MetaDataMappingService.java | 29 ++- .../admin/indices/RestCreateIndexAction.java | 15 +- .../admin/indices/RestGetMappingAction.java | 3 +- .../admin/indices/RestPutMappingAction.java | 8 +- 22 files changed, 1147 insertions(+), 311 deletions(-) create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/11_basic_with_types.yml create mode 100644 rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/11_basic_with_types.yml 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/11_basic_with_types.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/all_path_options_with_types.yml diff --git a/docs/reference/indices/create-index.asciidoc b/docs/reference/indices/create-index.asciidoc index b7629a7cad566..a1061ac67674f 100644 --- a/docs/reference/indices/create-index.asciidoc +++ b/docs/reference/indices/create-index.asciidoc @@ -177,3 +177,28 @@ 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 by default. You can already opt in for +this behavior by 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..d71ec60967f8b 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 return the type name as a top-level key by default. You can already opt in for +this behavior by 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..8cf60b0316758 100644 --- a/docs/reference/indices/put-mapping.asciidoc +++ b/docs/reference/indices/put-mapping.asciidoc @@ -114,3 +114,39 @@ 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 by default. You can already opt in for +this behavior by setting `include_type_name=false` and putting mappings directly under +`mappings` in the index creation 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 +{ + "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..4b036a004f8bd 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" : "boolean", + "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..cfa3e56fa8a5f 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" : "boolean", + "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..367a46ee37473 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" : "boolean", + "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.create/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml index 24512b9f31ae1..637a146ca68ca 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/10_basic.yml @@ -1,24 +1,31 @@ --- "Create index with mappings": - + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index body: mappings: - type_1: {} + {} - do: indices.get_mapping: + include_type_name: false index: test_index - - is_true: test_index.mappings.type_1 + - is_true: test_index.mappings --- "Create index with settings": - + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index body: settings: @@ -33,11 +40,11 @@ --- "Create index": - skip: - version: " - 5.5.99" - reason: create index response contains index name since 5.6.0 - + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index - match: { acknowledged: true } @@ -45,9 +52,12 @@ --- "Create index with wait_for_active_shards set to all": - + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index wait_for_active_shards: all body: @@ -59,16 +69,18 @@ --- "Create index with aliases": - + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index body: mappings: - type_1: - properties: - field: - type: text + properties: + field: + type: text aliases: test_alias: {} test_blias: @@ -89,49 +101,15 @@ - is_false: test_index.aliases.test_clias.index_routing - is_false: test_index.aliases.test_clias.search_routing ---- -"Create index with write aliases": - - skip: - version: " - 6.99.99" - reason: is_write_index is not implemented in ES <= 6.x - - do: - indices.create: - index: test_index - body: - aliases: - test_alias: {} - test_blias: - is_write_index: false - test_clias: - is_write_index: true - - - do: - indices.get_alias: - index: test_index - - - is_false: test_index.aliases.test_alias.is_write_index - - is_false: test_index.aliases.test_blias.is_write_index - - is_true: test_index.aliases.test_clias.is_write_index - ---- -"Create index with no type mappings": - - do: - catch: /illegal_argument_exception/ - indices.create: - index: test_index - body: - mappings: - "" : {} - --- "Create index with invalid mappings": - do: catch: /illegal_argument_exception/ indices.create: + include_type_name: false index: test_index body: mappings: - test_type: - properties: - "": - type: keyword + properties: + "": + type: keyword diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/11_basic_with_types.yml new file mode 100644 index 0000000000000..aeb883d4cf0af --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.create/11_basic_with_types.yml @@ -0,0 +1,134 @@ +--- +"Create index with mappings": + + - do: + indices.create: + index: test_index + body: + mappings: + type_1: {} + + - do: + indices.get_mapping: + index: test_index + + - is_true: test_index.mappings.type_1 + +--- +"Create index with settings": + + - do: + indices.create: + index: test_index + body: + settings: + number_of_replicas: "0" + + - do: + indices.get_settings: + index: test_index + + - match: { test_index.settings.index.number_of_replicas: "0"} + +--- +"Create index": + + - do: + indices.create: + index: test_index + + - match: { acknowledged: true } + - match: { index: "test_index"} + +--- +"Create index with wait_for_active_shards set to all": + + - do: + indices.create: + index: test_index + wait_for_active_shards: all + body: + settings: + number_of_replicas: "0" + + - match: { acknowledged: true } + - match: { shards_acknowledged: true } + +--- +"Create index with aliases": + + - do: + indices.create: + index: test_index + body: + mappings: + type_1: + properties: + field: + type: text + aliases: + test_alias: {} + test_blias: + routing: b + test_clias: + filter: + term: + field : value + + - do: + indices.get_alias: + index: test_index + + - match: {test_index.aliases.test_blias.search_routing: b} + - match: {test_index.aliases.test_blias.index_routing: b} + - is_false: test_index.aliases.test_blias.filter + - match: {test_index.aliases.test_clias.filter.term.field: value} + - is_false: test_index.aliases.test_clias.index_routing + - is_false: test_index.aliases.test_clias.search_routing + +--- +"Create index with write aliases": + - skip: + version: " - 6.99.99" + reason: is_write_index is not implemented in ES <= 6.x + - do: + indices.create: + index: test_index + body: + aliases: + test_alias: {} + test_blias: + is_write_index: false + test_clias: + is_write_index: true + + - do: + indices.get_alias: + index: test_index + + - is_false: test_index.aliases.test_alias.is_write_index + - is_false: test_index.aliases.test_blias.is_write_index + - is_true: test_index.aliases.test_clias.is_write_index + +--- +"Create index with no type mappings": + - do: + catch: /illegal_argument_exception/ + indices.create: + index: test_index + body: + mappings: + "" : {} + +--- +"Create index with invalid mappings": + - do: + catch: /illegal_argument_exception/ + indices.create: + index: test_index + body: + mappings: + test_type: + properties: + "": + type: keyword diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml index 90bb2747a7bfc..643b7441765d1 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/10_basic.yml @@ -1,144 +1,98 @@ --- setup: + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: - indices.create: - index: test_1 - body: - mappings: - doc: {} + indices.create: + include_type_name: false + index: test_1 + body: + mappings: {} - do: - indices.create: - index: test_2 - body: - mappings: - doc: {} + indices.create: + include_type_name: false + index: test_2 + body: + mappings: {} --- "Get /{index}/_mapping with empty mappings": - - do: - indices.create: - index: t + - do: + indices.create: + include_type_name: false + index: t - - do: - indices.get_mapping: - index: t + - do: + indices.get_mapping: + include_type_name: false + index: t - - match: { t.mappings: {}} + - match: { t.mappings: {}} --- "Get /_mapping": - - do: - indices.get_mapping: {} + - do: + indices.get_mapping: + include_type_name: false - - is_true: test_1.mappings.doc - - is_true: test_2.mappings.doc + - is_true: test_1.mappings + - is_true: test_2.mappings --- "Get /{index}/_mapping": - - do: - indices.get_mapping: - index: test_1 - - - is_true: test_1.mappings.doc - - is_false: test_2 - - ---- -"Get /{index}/_mapping/_all": - - - do: - indices.get_mapping: - index: test_1 - type: _all - - - is_true: test_1.mappings.doc - - is_false: test_2 - ---- -"Get /{index}/_mapping/*": - - - do: - indices.get_mapping: - index: test_1 - type: '*' - - - is_true: test_1.mappings.doc - - is_false: test_2 - ---- -"Get /{index}/_mapping/{type}": - - - do: - indices.get_mapping: - index: test_1 - type: doc - - - is_true: test_1.mappings.doc - - is_false: test_2 - ---- -"Get /{index}/_mapping/{type*}": - - - do: - indices.get_mapping: + - do: + indices.get_mapping: + include_type_name: false index: test_1 - type: 'd*' - - is_true: test_1.mappings.doc - - is_false: test_2 - ---- -"Get /_mapping/{type}": + - is_true: test_1.mappings + - is_false: test_2 - - do: - indices.get_mapping: - type: doc - - is_true: test_1.mappings.doc - - is_true: test_2.mappings.doc --- -"Get /_all/_mapping/{type}": +"Get /_all/_mapping": - - do: - indices.get_mapping: + - do: + indices.get_mapping: + include_type_name: false index: _all - type: doc - - is_true: test_1.mappings.doc - - is_true: test_2.mappings.doc + - is_true: test_1.mappings + - is_true: test_2.mappings --- -"Get /*/_mapping/{type}": +"Get /*/_mapping": - - do: - indices.get_mapping: + - do: + indices.get_mapping: + include_type_name: false index: '*' - type: doc - - is_true: test_1.mappings.doc - - is_true: test_2.mappings.doc + - is_true: test_1.mappings + - is_true: test_2.mappings --- -"Get /index,index/_mapping/{type}": +"Get /index,index/_mapping": - - do: - indices.get_mapping: + - do: + indices.get_mapping: + include_type_name: false index: test_1,test_2 - type: doc - - is_true: test_1.mappings.doc - - is_true: test_2.mappings.doc + - is_true: test_1.mappings + - is_true: test_2.mappings --- -"Get /index*/_mapping/{type}": +"Get /index*/_mapping/": - - do: - indices.get_mapping: + - do: + indices.get_mapping: + include_type_name: false index: '*2' - type: doc - - is_true: test_2.mappings.doc - - is_false: test_1 + - is_true: test_2.mappings + - is_false: test_1 diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/11_basic_with_types.yml new file mode 100644 index 0000000000000..5cb22e4d4143b --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.get_mapping/11_basic_with_types.yml @@ -0,0 +1,144 @@ +--- +setup: + - do: + indices.create: + index: test_1 + body: + mappings: + doc: {} + - do: + indices.create: + index: test_2 + body: + mappings: + doc: {} +--- +"Get /{index}/_mapping with empty mappings": + + - do: + indices.create: + index: t + + - do: + indices.get_mapping: + index: t + + - match: { t.mappings: {}} + +--- +"Get /_mapping": + + - do: + indices.get_mapping: {} + + - is_true: test_1.mappings.doc + - is_true: test_2.mappings.doc + +--- +"Get /{index}/_mapping": + + - do: + indices.get_mapping: + index: test_1 + + - is_true: test_1.mappings.doc + - is_false: test_2 + + +--- +"Get /{index}/_mapping/_all": + + - do: + indices.get_mapping: + index: test_1 + type: _all + + - is_true: test_1.mappings.doc + - is_false: test_2 + +--- +"Get /{index}/_mapping/*": + + - do: + indices.get_mapping: + index: test_1 + type: '*' + + - is_true: test_1.mappings.doc + - is_false: test_2 + +--- +"Get /{index}/_mapping/{type}": + + - do: + indices.get_mapping: + index: test_1 + type: doc + + - is_true: test_1.mappings.doc + - is_false: test_2 + +--- +"Get /{index}/_mapping/{type*}": + + - do: + indices.get_mapping: + index: test_1 + type: 'd*' + + - is_true: test_1.mappings.doc + - is_false: test_2 + +--- +"Get /_mapping/{type}": + + - do: + indices.get_mapping: + type: doc + + - is_true: test_1.mappings.doc + - is_true: test_2.mappings.doc + +--- +"Get /_all/_mapping/{type}": + + - do: + indices.get_mapping: + index: _all + type: doc + + - is_true: test_1.mappings.doc + - is_true: test_2.mappings.doc + +--- +"Get /*/_mapping/{type}": + + - do: + indices.get_mapping: + index: '*' + type: doc + + - is_true: test_1.mappings.doc + - is_true: test_2.mappings.doc + +--- +"Get /index,index/_mapping/{type}": + + - do: + indices.get_mapping: + index: test_1,test_2 + type: doc + + - is_true: test_1.mappings.doc + - is_true: test_2.mappings.doc + +--- +"Get /index*/_mapping/{type}": + + - do: + indices.get_mapping: + index: '*2' + type: doc + + - is_true: test_2.mappings.doc + - is_false: test_1 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/10_basic.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml index 1d33f2d31bb15..a1146380026ea 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/10_basic.yml @@ -1,69 +1,97 @@ --- "Test Create and update mapping": + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index - do: indices.put_mapping: + include_type_name: false index: test_index - type: test_type body: - test_type: - properties: - text1: - type: text - analyzer: whitespace - text2: - type: text - analyzer: whitespace - subfield.text3: - type: text + properties: + text1: + type: text + analyzer: whitespace + text2: + type: text + analyzer: whitespace + subfield.text3: + type: text - do: indices.get_mapping: + include_type_name: false index: test_index - - - match: {test_index.mappings.test_type.properties.text1.type: text} - - match: {test_index.mappings.test_type.properties.text1.analyzer: whitespace} - - match: {test_index.mappings.test_type.properties.text2.type: text} - - match: {test_index.mappings.test_type.properties.text2.analyzer: whitespace} + + - match: {test_index.mappings.properties.text1.type: text} + - match: {test_index.mappings.properties.text1.analyzer: whitespace} + - match: {test_index.mappings.properties.text2.type: text} + - match: {test_index.mappings.properties.text2.analyzer: whitespace} - do: indices.put_mapping: + include_type_name: false index: test_index - type: test_type body: - test_type: - properties: - text1: - type: text - analyzer: whitespace - fields: - text_raw: - type: keyword + properties: + text1: + type: text + analyzer: whitespace + fields: + text_raw: + type: keyword - do: indices.get_mapping: + include_type_name: false index: test_index - - - match: {test_index.mappings.test_type.properties.text1.type: text} - - match: {test_index.mappings.test_type.properties.subfield.properties.text3.type: text} - - match: {test_index.mappings.test_type.properties.text1.fields.text_raw.type: keyword} + + - match: {test_index.mappings.properties.text1.type: text} + - match: {test_index.mappings.properties.subfield.properties.text3.type: text} + - match: {test_index.mappings.properties.text1.fields.text_raw.type: keyword} --- "Create index with invalid mappings": + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index - do: catch: /illegal_argument_exception/ indices.put_mapping: + include_type_name: false index: test_index - type: test_type body: - test_type: - properties: - "": - type: keyword + properties: + "": + type: keyword + +--- +"PUT mapping with a type and include_type_name: false": + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 + - do: + indices.create: + index: index + include_type_name: false + + - do: + catch: /illegal_argument_exception/ + indices.put_mapping: + index: index + type: _doc + include_type_name: false + body: + properties: + bar: + type: float diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/11_basic_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/11_basic_with_types.yml new file mode 100644 index 0000000000000..57c565a100a32 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/11_basic_with_types.yml @@ -0,0 +1,69 @@ +--- +"Test Create and update mapping": + - do: + indices.create: + index: test_index + + - do: + indices.put_mapping: + index: test_index + type: test_type + body: + test_type: + properties: + text1: + type: text + analyzer: whitespace + text2: + type: text + analyzer: whitespace + subfield.text3: + type: text + + - do: + indices.get_mapping: + index: test_index + + - match: {test_index.mappings.test_type.properties.text1.type: text} + - match: {test_index.mappings.test_type.properties.text1.analyzer: whitespace} + - match: {test_index.mappings.test_type.properties.text2.type: text} + - match: {test_index.mappings.test_type.properties.text2.analyzer: whitespace} + + - do: + indices.put_mapping: + index: test_index + type: test_type + body: + test_type: + properties: + text1: + type: text + analyzer: whitespace + fields: + text_raw: + type: keyword + + + - do: + indices.get_mapping: + index: test_index + + - match: {test_index.mappings.test_type.properties.text1.type: text} + - match: {test_index.mappings.test_type.properties.subfield.properties.text3.type: text} + - match: {test_index.mappings.test_type.properties.text1.fields.text_raw.type: keyword} + +--- +"Create index with invalid mappings": + - do: + indices.create: + index: test_index + - do: + catch: /illegal_argument_exception/ + indices.put_mapping: + index: test_index + type: test_type + body: + test_type: + properties: + "": + 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/all_path_options.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml index e125ec2a70f4e..4d1253a486a83 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options.yml @@ -1,12 +1,18 @@ setup: + - skip: + version: " - 6.6.99" + reason: include_type_name was introduced in 6.7 - do: indices.create: + include_type_name: false index: test_index1 - do: indices.create: + include_type_name: false index: test_index2 - do: indices.create: + include_type_name: false index: foo @@ -14,34 +20,33 @@ setup: "put one mapping per index": - do: indices.put_mapping: + include_type_name: false index: test_index1 - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: indices.put_mapping: + include_type_name: false index: test_index2 - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index1.mappings.properties.text.type: text} + - match: {test_index1.mappings.properties.text.analyzer: whitespace} - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index2.mappings.properties.text.type: text} + - match: {test_index2.mappings.properties.text.analyzer: whitespace} - match: { foo.mappings: {} } @@ -50,73 +55,73 @@ setup: - do: indices.put_mapping: + include_type_name: false index: _all - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index1.mappings.properties.text.type: text} + - match: {test_index1.mappings.properties.text.analyzer: whitespace} - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index2.mappings.properties.text.type: text} + - match: {test_index2.mappings.properties.text.analyzer: whitespace} - - match: {foo.mappings.test_type.properties.text.type: text} - - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} + - match: {foo.mappings.properties.text.type: text} + - match: {foo.mappings.properties.text.analyzer: whitespace} --- "put mapping in * index": - do: indices.put_mapping: + include_type_name: false index: "*" - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index1.mappings.properties.text.type: text} + - match: {test_index1.mappings.properties.text.analyzer: whitespace} - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index2.mappings.properties.text.type: text} + - match: {test_index2.mappings.properties.text.analyzer: whitespace} - - match: {foo.mappings.test_type.properties.text.type: text} - - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} + - match: {foo.mappings.properties.text.type: text} + - match: {foo.mappings.properties.text.analyzer: whitespace} --- "put mapping in prefix* index": - do: indices.put_mapping: + include_type_name: false index: "test_index*" - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index1.mappings.properties.text.type: text} + - match: {test_index1.mappings.properties.text.analyzer: whitespace} - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index2.mappings.properties.text.type: text} + - match: {test_index2.mappings.properties.text.analyzer: whitespace} - match: { foo.mappings: {} } @@ -124,87 +129,53 @@ setup: "put mapping in list of indices": - do: indices.put_mapping: + include_type_name: false index: [test_index1, test_index2] - type: test_type body: - test_type: - properties: - text: - type: text - analyzer: whitespace + properties: + text: + type: text + analyzer: whitespace - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index1.mappings.properties.text.type: text} + - match: {test_index1.mappings.properties.text.analyzer: whitespace} - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + - match: {test_index2.mappings.properties.text.type: text} + - match: {test_index2.mappings.properties.text.analyzer: whitespace} - match: { foo.mappings: {} } ---- -"put mapping with blank index": - - do: - indices.put_mapping: - type: test_type - body: - test_type: - properties: - text: - type: text - analyzer: whitespace - - - do: - indices.get_mapping: {} - - - match: {test_index1.mappings.test_type.properties.text.type: text} - - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} - - - match: {test_index2.mappings.test_type.properties.text.type: text} - - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} - - - match: {foo.mappings.test_type.properties.text.type: text} - - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} - ---- -"put mapping with missing type": - - - - do: - catch: param - indices.put_mapping: {} - --- "post a mapping with default analyzer twice": - do: indices.put_mapping: + include_type_name: false index: test_index1 - type: test_type body: - test_type: - dynamic: false - properties: - text: - analyzer: default - type: text + dynamic: false + properties: + text: + analyzer: default + type: text - do: indices.put_mapping: + include_type_name: false index: test_index1 - type: test_type body: - test_type: - dynamic: false - properties: - text: - analyzer: default - type: text + dynamic: false + properties: + text: + analyzer: default + type: text - do: - indices.get_mapping: {} + indices.get_mapping: + include_type_name: false - - match: {test_index1.mappings.test_type.properties.text.type: text} - + - match: {test_index1.mappings.properties.text.type: text} diff --git a/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options_with_types.yml b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options_with_types.yml new file mode 100644 index 0000000000000..2a9d917741b00 --- /dev/null +++ b/rest-api-spec/src/main/resources/rest-api-spec/test/indices.put_mapping/all_path_options_with_types.yml @@ -0,0 +1,209 @@ +setup: + - do: + indices.create: + index: test_index1 + - do: + indices.create: + index: test_index2 + - do: + indices.create: + index: foo + + +--- +"put one mapping per index": + - do: + indices.put_mapping: + index: test_index1 + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + - do: + indices.put_mapping: + index: test_index2 + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: { foo.mappings: {} } + +--- +"put mapping in _all index": + + - do: + indices.put_mapping: + index: _all + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {foo.mappings.test_type.properties.text.type: text} + - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} + +--- +"put mapping in * index": + - do: + indices.put_mapping: + index: "*" + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {foo.mappings.test_type.properties.text.type: text} + - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} + +--- +"put mapping in prefix* index": + - do: + indices.put_mapping: + index: "test_index*" + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: { foo.mappings: {} } + +--- +"put mapping in list of indices": + - do: + indices.put_mapping: + index: [test_index1, test_index2] + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: { foo.mappings: {} } + +--- +"put mapping with blank index": + - do: + indices.put_mapping: + type: test_type + body: + test_type: + properties: + text: + type: text + analyzer: whitespace + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} + - match: {test_index1.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {test_index2.mappings.test_type.properties.text.type: text} + - match: {test_index2.mappings.test_type.properties.text.analyzer: whitespace} + + - match: {foo.mappings.test_type.properties.text.type: text} + - match: {foo.mappings.test_type.properties.text.analyzer: whitespace} + +--- +"put mapping with missing type": + + + - do: + catch: param + indices.put_mapping: {} + +--- +"post a mapping with default analyzer twice": + + - do: + indices.put_mapping: + index: test_index1 + type: test_type + body: + test_type: + dynamic: false + properties: + text: + analyzer: default + type: text + + - do: + indices.put_mapping: + index: test_index1 + type: test_type + body: + test_type: + dynamic: false + properties: + text: + analyzer: default + type: text + + - do: + indices.get_mapping: {} + + - match: {test_index1.mappings.test_type.properties.text.type: text} 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");