diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json b/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json index c30ee70e2eb82..61fd9ba3513ca 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/bulk.json @@ -16,6 +16,10 @@ } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "wait_for_active_shards": { "type" : "string", "description" : "Sets the number of shard copies that must be active before proceeding with the bulk operation. Defaults to 1, meaning the primary shard only. Set to `all` for all shard copies, otherwise set to any non-negative value less than or equal to the total number of copies for the shard (number of replicas + 1)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json b/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json index 389d00c670622..b146c34b441ea 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/delete.json @@ -4,7 +4,7 @@ "methods": ["DELETE"], "url": { "path": "/{index}/{type}/{id}", - "paths": ["/{index}/{type}/{id}"], + "paths": ["/{index}/{type}/{id}", "/{index}/_doc/{id}"], "parts": { "id": { "type" : "string", @@ -18,11 +18,14 @@ }, "type": { "type" : "string", - "required" : true, "description" : "The type of the document" } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "wait_for_active_shards": { "type" : "string", "description" : "Sets the number of shard copies that must be active before proceeding with the delete operation. Defaults to 1, meaning the primary shard only. Set to `all` for all shard copies, otherwise set to any non-negative value less than or equal to the total number of copies for the shard (number of replicas + 1)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/get.json b/rest-api-spec/src/main/resources/rest-api-spec/api/get.json index 8aba39e7710af..9f26ca565b293 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/get.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/get.json @@ -4,7 +4,7 @@ "methods": ["GET"], "url": { "path": "/{index}/{type}/{id}", - "paths": ["/{index}/{type}/{id}"], + "paths": ["/{index}/{type}/{id}", "/{index}/_doc/{id}"], "parts": { "id": { "type" : "string", @@ -18,11 +18,14 @@ }, "type": { "type" : "string", - "required" : true, "description" : "The type of the document (use `_all` to fetch the first document matching the ID across all types)" } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "stored_fields": { "type": "list", "description" : "A comma-separated list of stored fields to return in the response" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/index.json b/rest-api-spec/src/main/resources/rest-api-spec/api/index.json index 574206a0dc3ed..3e07ff7acfa37 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/index.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/index.json @@ -21,6 +21,10 @@ } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "wait_for_active_shards": { "type" : "string", "description" : "Sets the number of shard copies that must be active before proceeding with the index operation. Defaults to 1, meaning the primary shard only. Set to `all` for all shard copies, otherwise set to any non-negative value less than or equal to the total number of copies for the shard (number of replicas + 1)" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json index 3802747ed1b88..af2b3104a93f5 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/search.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/search.json @@ -16,6 +16,10 @@ } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "analyzer": { "type" : "string", "description" : "The analyzer to use for the query string" diff --git a/rest-api-spec/src/main/resources/rest-api-spec/api/update.json b/rest-api-spec/src/main/resources/rest-api-spec/api/update.json index ffa99cc9dc312..a63e248d00f6e 100644 --- a/rest-api-spec/src/main/resources/rest-api-spec/api/update.json +++ b/rest-api-spec/src/main/resources/rest-api-spec/api/update.json @@ -4,7 +4,7 @@ "methods": ["POST"], "url": { "path": "/{index}/{type}/{id}/_update", - "paths": ["/{index}/{type}/{id}/_update"], + "paths": ["/{index}/{type}/{id}/_update", "/{index}/_doc/{id}/_update"], "parts": { "id": { "type": "string", @@ -18,11 +18,14 @@ }, "type": { "type": "string", - "required": true, "description": "The type of the document" } }, "params": { + "include_type_name": { + "type" : "string", + "description" : "Whether to add the type name to the response" + }, "wait_for_active_shards": { "type": "string", "description": "Sets the number of shard copies that must be active before proceeding with the update operation. Defaults to 1, meaning the primary shard only. Set to `all` for all shard copies, otherwise set to any non-negative value less than or equal to the total number of copies for the shard (number of replicas + 1)" 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 index 40effe01b080f..aa05deb326024 100644 --- 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 @@ -39,44 +39,257 @@ - match: { index.mappings.properties.foo.type: "keyword" } - match: { index.mappings.properties.bar.type: "float" } -# Explicit id +--- +"Index explicit IDs without types": + + - skip: + version: " - 6.99.99" + reason: include_type_name was introduced in 7.0.0 + - do: - index: - index: index - id: 1 - body: { foo: bar } + indices.create: + index: index + include_type_name: false -# Implicit id - do: index: + include_type_name: false index: index + id: 1 body: { foo: bar } -# Bulk with explicit id + - match: { "_index": "index" } + - is_false: _type + - do: bulk: index: index + include_type_name: false body: | { "index": { "_id": "2" } } { "doc": { "foo": "baz" } } -# Bulk with implicit id + - match: { "items.0.index._index": "index" } + - is_false: items.0.index._type + +--- +"Index implicit IDs 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 + + - do: + index: + index: index + include_type_name: false + body: { foo: bar } + + - match: { "_index": "index" } + - is_false: _type + - do: bulk: index: index + include_type_name: false body: | { "index": { } } { "doc": { "foo": "baz" } } + - match: { "items.0.index._index": "index" } + - is_false: items.0.index._type + +--- +"Mixing include_type_name=false with explicit 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 + + - do: + catch: /illegal_argument_exception/ + index: + index: index + type: type + id: 1 + include_type_name: false + body: { foo: bar } + + - do: + catch: /illegal_argument_exception/ + index: + index: index + type: type + include_type_name: false + body: { foo: bar } + + - do: + catch: /illegal_argument_exception/ + get: + index: index + type: type + id: 1 + include_type_name: false + + - do: + catch: /illegal_argument_exception/ + update: + index: index + type: type + id: 1 + include_type_name: false + body: + doc: { foo: baz } + + - do: + catch: /illegal_argument_exception/ + delete: + index: index + type: type + id: 1 + include_type_name: false + + - do: + catch: /illegal_argument_exception/ + search: + index: index + type: type + include_type_name: false + + - do: + catch: /illegal_argument_exception/ + search: + index: index + type: _doc + include_type_name: false + +--- +"Update API 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 + + - do: + index: + index: index + id: 1 + include_type_name: false + body: { "foo": "bar" } + + - do: + update: + index: index + id: 1 + include_type_name: false + body: + doc: { "foo": "baz" } + + - match: { "_index": "index" } + - is_false: _type + +--- +"GET API 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 + + - do: + index: + index: index + id: 1 + include_type_name: false + body: { "foo": "bar" } + + - do: + get: + index: index + id: 1 + include_type_name: false + + - match: { "_index": "index" } + - is_false: _type + +--- +"Delete API 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 + + - do: + index: + index: index + id: 1 + include_type_name: false + body: { "foo": "bar" } + + - do: + delete: + index: index + id: 1 + include_type_name: false + + - match: { "_index": "index" } + - is_false: _type + +--- +"Search 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 + + - do: + index: + index: index + id: 1 + include_type_name: false + body: { "foo": "bar" } + - do: indices.refresh: - index: index + index: index - do: - count: + search: index: index + include_type_name: false - - match: { count: 4 } + - match: { "hits.total": 1 } + - match: { "hits.hits.0._index": "index" } + - is_false: hits.hits.0._type --- "PUT mapping with a type and include_type_name: false": @@ -88,6 +301,7 @@ - do: indices.create: index: index + include_type_name: false - do: catch: /illegal_argument_exception/ @@ -101,7 +315,7 @@ type: float --- -"Empty index with the include_type_name=false option": +"GET mappings on empty index with the include_type_name=false option": - skip: version: " - 6.99.99" diff --git a/server/src/main/java/org/elasticsearch/action/DocWriteResponse.java b/server/src/main/java/org/elasticsearch/action/DocWriteResponse.java index 69ba6db63ef07..8fa183a843419 100644 --- a/server/src/main/java/org/elasticsearch/action/DocWriteResponse.java +++ b/server/src/main/java/org/elasticsearch/action/DocWriteResponse.java @@ -295,9 +295,11 @@ public final XContentBuilder toXContent(XContentBuilder builder, Params params) public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { ReplicationResponse.ShardInfo shardInfo = getShardInfo(); - builder.field(_INDEX, shardId.getIndexName()) - .field(_TYPE, type) - .field(_ID, id) + builder.field(_INDEX, shardId.getIndexName()); + if (params.paramAsBoolean("include_type_name", true)) { + builder.field(_TYPE, type); + } + builder.field(_ID, id) .field(_VERSION, version) .field(RESULT, getResult().getLowercase()); if (forcedRefresh) { diff --git a/server/src/main/java/org/elasticsearch/index/get/GetResult.java b/server/src/main/java/org/elasticsearch/index/get/GetResult.java index ae59c6f507749..021e97767d840 100644 --- a/server/src/main/java/org/elasticsearch/index/get/GetResult.java +++ b/server/src/main/java/org/elasticsearch/index/get/GetResult.java @@ -252,7 +252,9 @@ public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException { builder.startObject(); builder.field(_INDEX, index); - builder.field(_TYPE, type); + if (params.paramAsBoolean("include_type_name", true)) { + builder.field(_TYPE, type); + } builder.field(_ID, id); if (isExists()) { if (version != -1) { diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java index 8db9710af3139..0e242bb6d9f78 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestBulkAction.java @@ -72,7 +72,15 @@ public String getName() { public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { BulkRequest bulkRequest = Requests.bulkRequest(); String defaultIndex = request.param("index"); - String defaultType = request.param("type", MapperService.SINGLE_MAPPING_NAME); + String defaultType = request.param("type"); + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); + if (includeTypeName == false && defaultType != null) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the bulx APIs with the " + + "[_bulk] and [{index}/_bulk] endpoints."); + } + if (defaultType == null) { + defaultType = MapperService.SINGLE_MAPPING_NAME; + } String defaultRouting = request.param("routing"); FetchSourceContext defaultFetchSourceContext = FetchSourceContext.parseFromRestRequest(request); String defaultPipeline = request.param("pipeline"); diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestDeleteAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestDeleteAction.java index f6b0878c03802..05b60d3d7cbb2 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestDeleteAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestDeleteAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -47,7 +48,13 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - DeleteRequest deleteRequest = new DeleteRequest(request.param("index"), request.param("type"), request.param("id")); + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); + final String type = request.param("type"); + if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the delete API with the " + + "[{index}/_doc/{id}] endpoints."); + } + DeleteRequest deleteRequest = new DeleteRequest(request.param("index"), type, request.param("id")); deleteRequest.routing(request.param("routing")); deleteRequest.timeout(request.paramAsTime("timeout", DeleteRequest.DEFAULT_TIMEOUT)); deleteRequest.setRefreshPolicy(request.param("refresh")); diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestGetAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestGetAction.java index e1d3f7557783c..8044600d6196e 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestGetAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestGetAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -55,7 +56,13 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - final GetRequest getRequest = new GetRequest(request.param("index"), request.param("type"), request.param("id")); + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); + final String type = request.param("type"); + if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the get APIs with the " + + "[{index}/_doc/{id}] endpoint."); + } + final GetRequest getRequest = new GetRequest(request.param("index"), type, request.param("id")); getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh())); getRequest.routing(request.param("routing")); getRequest.preference(request.param("preference")); diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java index 5cc514f744098..619fd811e6a7c 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java @@ -24,6 +24,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -78,7 +79,13 @@ void validateOpType(String opType) { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - IndexRequest indexRequest = new IndexRequest(request.param("index"), request.param("type"), request.param("id")); + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); + final String type = request.param("type"); + if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the index APIs with the " + + "[{index}/_doc/{id}] and [{index}/_doc] endpoints."); + } + IndexRequest indexRequest = new IndexRequest(request.param("index"), type, request.param("id")); indexRequest.routing(request.param("routing")); indexRequest.setPipeline(request.param("pipeline")); indexRequest.source(request.requiredContent(), request.getXContentType()); diff --git a/server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java b/server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java index de7c1fad5b26a..29cc6e8e028a8 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/document/RestUpdateAction.java @@ -25,6 +25,7 @@ import org.elasticsearch.client.node.NodeClient; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.index.VersionType; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; @@ -50,7 +51,13 @@ public String getName() { @Override public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException { - UpdateRequest updateRequest = new UpdateRequest(request.param("index"), request.param("type"), request.param("id")); + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); + final String type = request.param("type"); + if (includeTypeName == false && MapperService.SINGLE_MAPPING_NAME.equals(type) == false) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the update API with the " + + "[{index}/_doc/{id}/_update] endpoint."); + } + UpdateRequest updateRequest = new UpdateRequest(request.param("index"), type, request.param("id")); updateRequest.routing(request.param("routing")); updateRequest.timeout(request.paramAsTime("timeout", updateRequest.timeout())); updateRequest.setRefreshPolicy(request.param("refresh")); diff --git a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java index 6f0c033a0cf22..3098dc03a8c71 100644 --- a/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java +++ b/server/src/main/java/org/elasticsearch/rest/action/search/RestSearchAction.java @@ -27,6 +27,7 @@ import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.index.mapper.MapperService; import org.elasticsearch.index.query.QueryBuilder; import org.elasticsearch.rest.BaseRestHandler; import org.elasticsearch.rest.RestController; @@ -150,8 +151,13 @@ public static void parseSearchRequest(SearchRequest searchRequest, RestRequest r searchRequest.scroll(new Scroll(parseTimeValue(scroll, null, "scroll"))); } + final boolean includeTypeName = request.paramAsBoolean("include_type_name", true); String types = request.param("type"); if (types != null) { + if (includeTypeName == false) { + throw new IllegalArgumentException("You may only use the [include_type_name=false] option with the search API with the " + + "[{index}/_search] endpoint."); + } DEPRECATION_LOGGER.deprecated("The {index}/{type}/_search endpoint is deprecated, use {index}/_search instead"); } searchRequest.types(Strings.splitStringByCommaToArray(types)); diff --git a/server/src/main/java/org/elasticsearch/search/SearchHit.java b/server/src/main/java/org/elasticsearch/search/SearchHit.java index 96a5ebc25e2da..da7a42b22e3e6 100644 --- a/server/src/main/java/org/elasticsearch/search/SearchHit.java +++ b/server/src/main/java/org/elasticsearch/search/SearchHit.java @@ -426,7 +426,7 @@ public XContentBuilder toInnerXContent(XContentBuilder builder, Params params) t if (index != null) { builder.field(Fields._INDEX, RemoteClusterAware.buildRemoteIndexName(clusterAlias, index)); } - if (type != null) { + if (type != null && params.paramAsBoolean("include_type_name", true)) { builder.field(Fields._TYPE, type); } if (id != null) {