Skip to content

Commit e942873

Browse files
authored
[REST Compatible API] Typed endpoints for Index and Get APIs (#69131)
The types removal effort has removed the type from Index API in #47671 and from Get API in #46587 This commit allows to use 'typed' endpoints for the both Index and Get APIs relates compatible types-removal meta issue #54160
1 parent edb4269 commit e942873

File tree

12 files changed

+218
-40
lines changed

12 files changed

+218
-40
lines changed

rest-api-spec/build.gradle

+10-30
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,9 @@ tasks.named("yamlRestCompatTest").configure {
143143
'explain/40_mix_typeless_typeful/Explain with typeless API on an index that has types',
144144
'field_caps/10_basic/Get date_nanos field caps',
145145
'field_caps/30_filter/Field caps with index filter',
146-
'get/100_mix_typeless_typeful/GET with typeless API on an index that has types',
147-
'get/10_basic/Basic',
148-
'get/11_basic_with_types/Basic',
149-
'get/15_default_values/Default values',
150-
'get/16_default_values_with_types/Default values',
151-
'get/20_stored_fields/Stored fields',
152-
'get/21_stored_fields_with_types/Stored fields',
153-
'get/41_routing_with_types/Routing',
154-
'get/50_with_headers/REST test with headers',
155-
'get/51_with_headers_with_types/REST test with headers',
156-
'get/61_realtime_refresh_with_types/Realtime Refresh',
157-
'get/70_source_filtering/Source filtering',
158-
'get/71_source_filtering_with_types/Source filtering',
159-
'get/81_missing_with_types/Missing document with catch',
160-
'get/81_missing_with_types/Missing document with ignore',
161-
'get/91_versions_with_types/Versions',
146+
'get/100_mix_typeless_typeful/GET with typeless API on an index that has types',// failing due to include_type_name #48632
147+
'get/21_stored_fields_with_types/Stored fields', // failing due to include_type_name #48632
148+
'get/71_source_filtering_with_types/Source filtering',// failing due to include_type_name #48632
162149
'get_source/11_basic_with_types/Basic with types',
163150
'get_source/16_default_values_with_types/Default values',
164151
'get_source/41_routing_with_types/Routing',
@@ -168,20 +155,8 @@ tasks.named("yamlRestCompatTest").configure {
168155
'get_source/81_missing_with_types/Missing document with ignore',
169156
'get_source/86_source_missing_with_types/Missing document source with catch',
170157
'get_source/86_source_missing_with_types/Missing document source with ignore',
171-
'index/10_with_id/Index with ID',
172-
'index/11_with_id_with_types/Index with ID',
173-
'index/13_result_with_types/Index result field',
174-
'index/15_without_id/Index without ID',
175-
'index/16_without_id_with_types/Index without ID',
176-
'index/21_optype_with_types/Optype',
177-
'index/37_external_version_with_types/External version',
178-
'index/38_external_gte_version_with_types/External GTE version',
179-
'index/41_routing_with_types/Routing',
180-
'index/61_refresh_with_types/Refresh',
181-
'index/61_refresh_with_types/When refresh url parameter is an empty string that means "refresh immediately"',
182-
'index/61_refresh_with_types/refresh=wait_for waits until changes are visible in search',
183-
'index/70_mix_typeless_typeful/Index call that introduces new field mappings',
184-
'index/70_mix_typeless_typeful/Index with typeless API on an index that has types',
158+
'index/70_mix_typeless_typeful/Index call that introduces new field mappings', // failing due to include_type_name #48632
159+
'index/70_mix_typeless_typeful/Index with typeless API on an index that has types', // failing due to include_type_name #48632
185160
'indices.clone/10_basic/Clone index via API',
186161
'indices.create/10_basic/Create index with explicit _doc type',
187162
'indices.create/10_basic/Create index without soft deletes',
@@ -376,3 +351,8 @@ tasks.named("yamlRestCompatTest").configure {
376351
'update/90_mix_typeless_typeful/Update with typeless API on an index that has types'
377352
].join(',')
378353
}
354+
355+
tasks.named("transformV7RestTests").configure({ task ->
356+
task.replaceMatch("_type", "_doc")
357+
task.addAllowedWarningRegex("\\[types removal\\].*")
358+
})

server/src/main/java/org/elasticsearch/action/DocWriteResponse.java

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.action.support.replication.ReplicationResponse;
1515
import org.elasticsearch.cluster.metadata.IndexMetadata;
1616
import org.elasticsearch.common.Nullable;
17+
import org.elasticsearch.common.RestApiVersion;
1718
import org.elasticsearch.common.io.stream.StreamInput;
1819
import org.elasticsearch.common.io.stream.StreamOutput;
1920
import org.elasticsearch.common.io.stream.Writeable;
@@ -293,6 +294,9 @@ private void writeWithoutShardId(StreamOutput out) throws IOException {
293294
@Override
294295
public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
295296
builder.startObject();
297+
if (builder.getRestApiVersion() == RestApiVersion.V_7) {
298+
builder.field(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME);
299+
}
296300
innerToXContent(builder, params);
297301
builder.endObject();
298302
return builder;

server/src/main/java/org/elasticsearch/index/get/GetResult.java

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import org.elasticsearch.ElasticsearchParseException;
1212
import org.elasticsearch.Version;
13+
import org.elasticsearch.common.RestApiVersion;
1314
import org.elasticsearch.common.Strings;
1415
import org.elasticsearch.common.bytes.BytesReference;
1516
import org.elasticsearch.common.compress.CompressorFactory;
@@ -282,6 +283,9 @@ public XContentBuilder toXContentEmbedded(XContentBuilder builder, Params params
282283
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
283284
builder.startObject();
284285
builder.field(_INDEX, index);
286+
if (builder.getRestApiVersion() == RestApiVersion.V_7) {
287+
builder.field(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME);
288+
}
285289
builder.field(_ID, id);
286290
if (isExists()) {
287291
if (version != -1) {

server/src/main/java/org/elasticsearch/index/mapper/MapperService.java

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public enum MergeReason {
7777
}
7878

7979
public static final String SINGLE_MAPPING_NAME = "_doc";
80+
public static final String TYPE_FIELD_NAME = "_type";
8081
public static final Setting<Long> INDEX_MAPPING_NESTED_FIELDS_LIMIT_SETTING =
8182
Setting.longSetting("index.mapping.nested_fields.limit", 50L, 0, Property.Dynamic, Property.IndexScope);
8283
// maximum allowed number of nested json objects across all fields in a single document

server/src/main/java/org/elasticsearch/rest/action/document/RestGetAction.java

+14-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import org.elasticsearch.action.get.GetRequest;
1212
import org.elasticsearch.action.get.GetResponse;
1313
import org.elasticsearch.client.node.NodeClient;
14+
import org.elasticsearch.common.RestApiVersion;
1415
import org.elasticsearch.common.Strings;
1516
import org.elasticsearch.index.VersionType;
1617
import org.elasticsearch.rest.BaseRestHandler;
@@ -29,6 +30,8 @@
2930
import static org.elasticsearch.rest.RestStatus.OK;
3031

3132
public class RestGetAction extends BaseRestHandler {
33+
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in "
34+
+ "document get requests is deprecated, use the /{index}/_doc/{id} endpoint instead.";
3235

3336
@Override
3437
public String getName() {
@@ -39,11 +42,21 @@ public String getName() {
3942
public List<Route> routes() {
4043
return List.of(
4144
new Route(GET, "/{index}/_doc/{id}"),
42-
new Route(HEAD, "/{index}/_doc/{id}"));
45+
new Route(HEAD, "/{index}/_doc/{id}"),
46+
Route.builder(GET, "/{index}/{type}/{id}")
47+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
48+
.build(),
49+
Route.builder(HEAD, "/{index}/{type}/{id}")
50+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
51+
.build());
4352
}
4453

4554
@Override
4655
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
56+
if (request.getRestApiVersion() == RestApiVersion.V_7) {
57+
request.param("type"); // consume and ignore the type
58+
}
59+
4760
GetRequest getRequest = new GetRequest(request.param("index"), request.param("id"));
4861

4962
getRequest.refresh(request.paramAsBoolean("refresh", getRequest.refresh()));

server/src/main/java/org/elasticsearch/rest/action/document/RestIndexAction.java

+27-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.action.support.ActiveShardCount;
1515
import org.elasticsearch.client.node.NodeClient;
1616
import org.elasticsearch.cluster.node.DiscoveryNodes;
17+
import org.elasticsearch.common.RestApiVersion;
1718
import org.elasticsearch.index.VersionType;
1819
import org.elasticsearch.rest.BaseRestHandler;
1920
import org.elasticsearch.rest.RestRequest;
@@ -29,12 +30,21 @@
2930
import static org.elasticsearch.rest.RestRequest.Method.PUT;
3031

3132
public class RestIndexAction extends BaseRestHandler {
33+
static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in document "
34+
+ "index requests is deprecated, use the typeless endpoints instead (/{index}/_doc/{id}, /{index}/_doc, "
35+
+ "or /{index}/_create/{id}).";
3236

3337
@Override
3438
public List<Route> routes() {
3539
return List.of(
3640
new Route(POST, "/{index}/_doc/{id}"),
37-
new Route(PUT, "/{index}/_doc/{id}"));
41+
new Route(PUT, "/{index}/_doc/{id}"),
42+
Route.builder(POST, "/{index}/{type}/{id}")
43+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
44+
.build(),
45+
Route.builder(PUT, "/{index}/{type}/{id}")
46+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
47+
.build());
3848
}
3949

4050
@Override
@@ -53,7 +63,13 @@ public String getName() {
5363
public List<Route> routes() {
5464
return List.of(
5565
new Route(POST, "/{index}/_create/{id}"),
56-
new Route(PUT, "/{index}/_create/{id}"));
66+
new Route(PUT, "/{index}/_create/{id}"),
67+
Route.builder(POST, "/{index}/{type}/{id}/_create")
68+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
69+
.build(),
70+
Route.builder(PUT, "/{index}/{type}/{id}/_create")
71+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
72+
.build());
5773
}
5874

5975
@Override
@@ -85,7 +101,11 @@ public String getName() {
85101

86102
@Override
87103
public List<Route> routes() {
88-
return List.of(new Route(POST, "/{index}/_doc"));
104+
return List.of(
105+
new Route(POST, "/{index}/_doc"),
106+
Route.builder(POST, "/{index}/{type}")
107+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7)
108+
.build());
89109
}
90110

91111
@Override
@@ -101,6 +121,10 @@ public RestChannelConsumer prepareRequest(RestRequest request, final NodeClient
101121

102122
@Override
103123
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
124+
if (request.getRestApiVersion() == RestApiVersion.V_7) {
125+
request.param("type"); // consume and ignore the type
126+
}
127+
104128
IndexRequest indexRequest = new IndexRequest(request.param("index"));
105129
indexRequest.id(request.param("id"));
106130
indexRequest.routing(request.param("routing"));

server/src/test/java/org/elasticsearch/action/DocWriteResponseTests.java

+33
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010

1111
import org.elasticsearch.action.DocWriteResponse.Result;
1212
import org.elasticsearch.action.support.replication.ReplicationResponse.ShardInfo;
13+
import org.elasticsearch.common.RestApiVersion;
1314
import org.elasticsearch.common.bytes.BytesReference;
1415
import org.elasticsearch.common.xcontent.ToXContent;
1516
import org.elasticsearch.common.xcontent.XContentBuilder;
1617
import org.elasticsearch.common.xcontent.XContentParser;
1718
import org.elasticsearch.common.xcontent.json.JsonXContent;
19+
import org.elasticsearch.index.mapper.MapperService;
1820
import org.elasticsearch.index.seqno.SequenceNumbers;
1921
import org.elasticsearch.index.shard.ShardId;
2022
import org.elasticsearch.test.ESTestCase;
@@ -96,4 +98,35 @@ public void testToXContentDoesntIncludeForcedRefreshUnlessForced() throws IOExce
9698
}
9799
}
98100
}
101+
102+
public void testTypeWhenCompatible() throws IOException {
103+
DocWriteResponse response = new DocWriteResponse(
104+
new ShardId("index", "uuid", 0),
105+
"id",
106+
SequenceNumbers.UNASSIGNED_SEQ_NO,
107+
17,
108+
0,
109+
DocWriteResponse.Result.CREATED
110+
) {
111+
// DocWriteResponse is abstract so we have to sneak a subclass in here to test it.
112+
};
113+
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
114+
builder.withCompatibleVersion(RestApiVersion.V_7);
115+
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
116+
117+
try (XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder))) {
118+
assertThat(parser.map(), hasEntry(MapperService.TYPE_FIELD_NAME, MapperService.SINGLE_MAPPING_NAME));
119+
}
120+
}
121+
122+
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
123+
builder.withCompatibleVersion(RestApiVersion.V_8);
124+
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
125+
126+
try (XContentParser parser = createParser(JsonXContent.jsonXContent, BytesReference.bytes(builder))) {
127+
assertThat(parser.map(), not(hasKey(MapperService.TYPE_FIELD_NAME)));
128+
}
129+
}
130+
}
131+
99132
}

server/src/test/java/org/elasticsearch/index/get/GetResultTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88

99
package org.elasticsearch.index.get;
1010

11+
import org.elasticsearch.common.RestApiVersion;
1112
import org.elasticsearch.common.Strings;
1213
import org.elasticsearch.common.bytes.BytesArray;
1314
import org.elasticsearch.common.bytes.BytesReference;
1415
import org.elasticsearch.common.collect.Tuple;
1516
import org.elasticsearch.common.document.DocumentField;
1617
import org.elasticsearch.common.io.stream.BytesStreamOutput;
1718
import org.elasticsearch.common.xcontent.ToXContent;
19+
import org.elasticsearch.common.xcontent.XContentBuilder;
1820
import org.elasticsearch.common.xcontent.XContentHelper;
1921
import org.elasticsearch.common.xcontent.XContentParser;
2022
import org.elasticsearch.common.xcontent.XContentType;
23+
import org.elasticsearch.common.xcontent.json.JsonXContent;
2124
import org.elasticsearch.index.mapper.IdFieldMapper;
2225
import org.elasticsearch.index.mapper.IndexFieldMapper;
2326
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
@@ -86,6 +89,34 @@ public void testToXContent() throws IOException {
8689
}
8790
}
8891

92+
public void testToCompatibleXContent() throws IOException {
93+
{
94+
GetResult getResult = new GetResult("index", "id", 0, 1, 1, true, new BytesArray("{ \"field1\" : " +
95+
"\"value1\", \"field2\":\"value2\"}"), singletonMap("field1", new DocumentField("field1",
96+
singletonList("value1"))), singletonMap("field1", new DocumentField("metafield",
97+
singletonList("metavalue"))));
98+
99+
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
100+
builder.withCompatibleVersion(RestApiVersion.V_7);
101+
getResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
102+
String output = Strings.toString(builder);
103+
assertEquals("{\"_index\":\"index\",\"_type\":\"_doc\",\"_id\":\"id\",\"_version\":1,\"_seq_no\":0,\"_primary_term\":1," +
104+
"\"metafield\":\"metavalue\",\"found\":true,\"_source\":{ \"field1\" : \"value1\", \"field2\":\"value2\"}," +
105+
"\"fields\":{\"field1\":[\"value1\"]}}", output);
106+
}
107+
}
108+
{
109+
GetResult getResult = new GetResult("index", "id", UNASSIGNED_SEQ_NO, 0, 1, false, null, null, null);
110+
111+
try (XContentBuilder builder = JsonXContent.contentBuilder()) {
112+
builder.withCompatibleVersion(RestApiVersion.V_7);
113+
getResult.toXContent(builder, ToXContent.EMPTY_PARAMS);
114+
String output = Strings.toString(builder);
115+
assertEquals("{\"_index\":\"index\",\"_type\":\"_doc\",\"_id\":\"id\",\"found\":false}", output);
116+
}
117+
}
118+
}
119+
89120
public void testToAndFromXContentEmbedded() throws Exception {
90121
XContentType xContentType = randomFrom(XContentType.values());
91122
Tuple<GetResult, GetResult> tuple = randomGetResult(xContentType);

server/src/test/java/org/elasticsearch/rest/RestControllerTests.java

+3-4
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
package org.elasticsearch.rest;
1010

11-
import org.elasticsearch.Version;
1211
import org.elasticsearch.client.node.NodeClient;
1312
import org.elasticsearch.common.RestApiVersion;
1413
import org.elasticsearch.common.breaker.CircuitBreaker;
@@ -630,7 +629,7 @@ public void testDispatchCompatibleHandler() {
630629

631630
RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);
632631

633-
final byte version = RestApiVersion.minimumSupported().major;
632+
final RestApiVersion version = RestApiVersion.minimumSupported();
634633

635634
final String mediaType = randomCompatibleMediaType(version);
636635
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);
@@ -654,7 +653,7 @@ public void testDispatchCompatibleRequestToNewlyAddedHandler() {
654653

655654
RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);
656655

657-
final byte version = RestApiVersion.minimumSupported().major;
656+
final RestApiVersion version = RestApiVersion.minimumSupported();
658657

659658
final String mediaType = randomCompatibleMediaType(version);
660659
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);
@@ -690,7 +689,7 @@ private FakeRestRequest requestWithContent(String mediaType) {
690689
public void testCurrentVersionVNDMediaTypeIsNotUsingCompatibility() {
691690
RestController restController = new RestController(Collections.emptySet(), null, client, circuitBreakerService, usageService);
692691

693-
final byte version = Version.CURRENT.major;
692+
final RestApiVersion version = RestApiVersion.current();
694693

695694
final String mediaType = randomCompatibleMediaType(version);
696695
FakeRestRequest fakeRestRequest = requestWithContent(mediaType);

0 commit comments

Comments
 (0)