Add the `include_type_name` option to the search and document APIs. (#29506)

This commit add the `include_type_name` option to the `index`, `update`,
`delete`, `get`, `bulk` and `search` APIs. When set to `false`, the response
will omit the `_type` in the response. This option doesn't work if the endpoint
contains a type. For instance, the following call would succeed:

```
GET index/_doc/1?include_type_name=false
```

But the following one would fail:

```
GET index/some_type/1?include_type_name=false
```

Relates #15613
This commit is contained in:
Adrien Grand 2018-04-17 11:29:08 +02:00 committed by GitHub
parent fd161d2659
commit d223bcf7ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 311 additions and 30 deletions

View File

@ -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)"

View File

@ -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)"

View File

@ -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"

View File

@ -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)"

View File

@ -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"

View File

@ -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)"

View File

@ -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:
indices.create:
index: index
include_type_name: false
- do:
index:
include_type_name: false
index: index
id: 1
body: { foo: bar }
# Implicit id
- do:
index:
index: index
body: { foo: bar }
- match: { "_index": "index" }
- is_false: _type
# Bulk with explicit id
- 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
- do:
count:
index: index
- match: { count: 4 }
- do:
search:
index: index
include_type_name: false
- 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"

View File

@ -295,9 +295,11 @@ public abstract class DocWriteResponse extends ReplicationResponse implements Wr
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) {

View File

@ -252,7 +252,9 @@ public class GetResult implements Streamable, Iterable<DocumentField>, ToXConten
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) {

View File

@ -72,7 +72,15 @@ public class RestBulkAction extends BaseRestHandler {
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");

View File

@ -24,6 +24,7 @@ import org.elasticsearch.action.support.ActiveShardCount;
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 class RestDeleteAction extends BaseRestHandler {
@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"));

View File

@ -25,6 +25,7 @@ import org.elasticsearch.client.node.NodeClient;
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 class RestGetAction extends BaseRestHandler {
@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"));

View File

@ -24,6 +24,7 @@ import org.elasticsearch.action.support.ActiveShardCount;
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 @@ public class RestIndexAction extends BaseRestHandler {
@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());

View File

@ -25,6 +25,7 @@ import org.elasticsearch.action.update.UpdateRequest;
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 class RestUpdateAction extends BaseRestHandler {
@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"));

View File

@ -27,6 +27,7 @@ import org.elasticsearch.common.logging.DeprecationLogger;
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 class RestSearchAction extends BaseRestHandler {
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));

View File

@ -426,7 +426,7 @@ public final class SearchHit implements Streamable, ToXContentObject, Iterable<D
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) {