Skip to content

Commit 668a72d

Browse files
authored
[Rest API Compatibility] Typed endpoints for Put and Get Mapping and get field mappings (#71721)
Implements a V7 compatible typed endpoints for REST put and get mapping endpoints. Also for Get Field Mappings endpoints. retrofits the REST layer change removed in #41676 relates main meta issue #51816 relates types removal issue #54160
1 parent 8e564d4 commit 668a72d

File tree

5 files changed

+216
-59
lines changed

5 files changed

+216
-59
lines changed

rest-api-spec/build.gradle

Lines changed: 97 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -144,60 +144,29 @@ tasks.named("yamlRestCompatTest").configure {
144144
'get_source/86_source_missing_with_types/Missing document source with catch',
145145
'get_source/86_source_missing_with_types/Missing document source with ignore',
146146
'indices.create/10_basic/Create index without soft deletes',
147-
// 5 below await retrofitting Removes typed URLs from mapping APIs #41676
148-
'indices.create/11_basic_with_types/Create index with mappings',
149-
'indices.create/20_mix_typeless_typeful/Create a typed index while there is a typeless template',
150-
'indices.create/20_mix_typeless_typeful/Create a typeless index while there is a typed template',
147+
// type information about the type is removed and not passed down. The logic to check for this is also removed.
151148
'indices.create/20_mix_typeless_typeful/Implicitly create a typed index while there is a typeless template',
152149
'indices.create/20_mix_typeless_typeful/Implicitly create a typeless index while there is a typed template',
153150
//
154151
'indices.flush/10_basic/Index synced flush rest test',
155152
'indices.forcemerge/10_basic/Check deprecation warning when incompatible only_expunge_deletes and max_num_segments values are both set',
156-
'indices.get_field_mapping/10_basic/Get field mapping with local is deprecated',// awaits #41676
157-
'indices.get_field_mapping/11_basic_with_types/Get field mapping by index only',// awaits #41676
158-
'indices.get_field_mapping/11_basic_with_types/Get field mapping by type & field',// awaits #41676
159-
'indices.get_field_mapping/11_basic_with_types/Get field mapping by type & field, with another field that doesn\'t exist',// awaits #41676
160-
'indices.get_field_mapping/11_basic_with_types/Get field mapping should work without index specifying type and fields',// awaits #41676
161-
'indices.get_field_mapping/11_basic_with_types/Get field mapping with include_defaults',// awaits #41676
162-
'indices.get_field_mapping/11_basic_with_types/Get field mapping with no index and type',// awaits #41676
163-
'indices.get_field_mapping/21_missing_field_with_types/Return empty object if field doesn\'t exist, but type and index do',// awaits #41676
164-
'indices.get_field_mapping/30_missing_type/Raise 404 when type doesn\'t exist',// awaits #41676
165-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping should work using \'*\' for indices and types',// awaits #41676
166-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping should work using \'_all\' for indices and types',// awaits #41676
167-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping should work using comma_separated values for indices and types',// awaits #41676
168-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping with * for fields',// awaits #41676
169-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping with *t1 for fields',// awaits #41676
170-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping with t* for fields',// awaits #41676
171-
'indices.get_field_mapping/51_field_wildcards_with_types/Get field mapping with wildcarded relative names',// awaits #41676
172-
'indices.get_mapping/11_basic_with_types/Get /*/_mapping/{type}',
173-
'indices.get_mapping/11_basic_with_types/Get /_all/_mapping/{type}',
174-
'indices.get_mapping/11_basic_with_types/Get /_mapping',
175-
'indices.get_mapping/11_basic_with_types/Get /_mapping/{type}',
176-
'indices.get_mapping/11_basic_with_types/Get /index*/_mapping/{type}',
177-
'indices.get_mapping/11_basic_with_types/Get /index,index/_mapping/{type}',
178-
'indices.get_mapping/11_basic_with_types/Get /{index}/_mapping',
179-
'indices.get_mapping/11_basic_with_types/Get /{index}/_mapping/*',
180-
'indices.get_mapping/11_basic_with_types/Get /{index}/_mapping/_all',
181-
'indices.get_mapping/11_basic_with_types/Get /{index}/_mapping/{type*}',
182-
'indices.get_mapping/11_basic_with_types/Get /{index}/_mapping/{type}',
153+
// This test returns test_index.mappings:{} when {} was expected. difference between 20_missing_field and 21_missing_field_with_types?
154+
'indices.get_field_mapping/21_missing_field_with_types/Return empty object if field doesn\'t exist, but type and index do',
155+
// The information about the type is not present in the index. hence it cannot know if the type exist or not.
156+
'indices.get_field_mapping/30_missing_type/Raise 404 when type doesn\'t exist',
157+
// The information about the type is not present in the index. hence it cannot know if the type exist or not.
183158
'indices.get_mapping/20_missing_type/Existent and non-existent type returns 404 and the existing type',
184159
'indices.get_mapping/20_missing_type/Existent and non-existent types returns 404 and the existing type',
185160
'indices.get_mapping/20_missing_type/No type matching pattern returns 404',
186161
'indices.get_mapping/20_missing_type/Non-existent type returns 404',
187162
'indices.get_mapping/20_missing_type/Type missing when no types exist',
163+
//
188164
'indices.open/10_basic/?wait_for_active_shards default is deprecated',
189165
'indices.open/10_basic/?wait_for_active_shards=index-setting',
190-
'indices.put_mapping/10_basic/Put mappings with explicit _doc type',
191-
'indices.put_mapping/11_basic_with_types/Test Create and update mapping',
166+
167+
// The information about the type is not present in the index. hence it cannot know if the type was already used or not
192168
'indices.put_mapping/20_mix_typeless_typeful/PUT mapping with _doc on an index that has types',
193169
'indices.put_mapping/20_mix_typeless_typeful/PUT mapping with typeless API on an index that has types',
194-
'indices.put_mapping/all_path_options_with_types/post a mapping with default analyzer twice',
195-
'indices.put_mapping/all_path_options_with_types/put mapping in * index',
196-
'indices.put_mapping/all_path_options_with_types/put mapping in _all index',
197-
'indices.put_mapping/all_path_options_with_types/put mapping in list of indices',
198-
'indices.put_mapping/all_path_options_with_types/put mapping in prefix* index',
199-
'indices.put_mapping/all_path_options_with_types/put mapping with blank index',
200-
'indices.put_mapping/all_path_options_with_types/put one mapping per index',
201170
// there is a small distinction between empty mappings and no mappings at all. The code to implement this test was refactored #54003
202171
// not fixing this in #70966
203172
'indices.put_template/11_basic_with_types/Put template with empty mappings',
@@ -297,8 +266,94 @@ tasks.named("transformV7RestTests").configure({ task ->
297266
task.replaceValueInMatch("nodes.\$node_id.roles.9", "remote_cluster_client", "node_info role test")
298267
task.removeMatch("nodes.\$node_id.roles.10", "node_info role test")
299268
task.replaceIsTrue("test_index.mappings.type_1", "test_index.mappings._doc")
300-
task.replaceIsFalse("test_index.mappings.type_1", "test_index.mappings._doc")
301-
task.replaceIsFalse("test-1.mappings.my_type", "test-1.mappings._doc")
269+
//override for indices.get and indices.create
270+
//task.replaceIsFalse("test_index.mappings.type_1", "test_index.mappings._doc")
271+
//overrides for indices.create/20_mix_typeless_typeful
272+
task.replaceIsFalse("test-1.mappings._doc","false", "Create a typed index while there is a typeless template")
273+
task.replaceIsFalse("test-1.mappings._doc","false", "Create a typeless index while there is a typed template")
274+
275+
task.replaceIsTrue("test-1.mappings.my_type", "test-1.mappings._doc")
276+
task.replaceIsTrue("test-1.mappings.my_type.properties.foo", "test-1.mappings._doc.properties.foo")
277+
task.replaceIsTrue("test-1.mappings.my_type.properties.bar", "test-1.mappings._doc.properties.bar")
278+
279+
// overrides for indices.get_field_mapping
280+
task.replaceKeyInLength("test_index.mappings.test_type.text.mapping.text.type",
281+
"test_index.mappings._doc.text.mapping.text.type"
282+
)
283+
task.replaceKeyInMatch("test_index.mappings.test_type.text.mapping.text.analyzer",
284+
"test_index.mappings._doc.text.mapping.text.analyzer"
285+
)
286+
task.replaceKeyInMatch("test_index.mappings.test_type.t1.full_name",
287+
"test_index.mappings._doc.t1.full_name"
288+
)
289+
task.replaceKeyInMatch("test_index.mappings.test_type.t2.full_name",
290+
"test_index.mappings._doc.t2.full_name"
291+
)
292+
task.replaceKeyInMatch("test_index.mappings.test_type.obj\\.t1.full_name",
293+
"test_index.mappings._doc.obj\\.t1.full_name"
294+
)
295+
task.replaceKeyInMatch("test_index.mappings.test_type.obj\\.i_t1.full_name",
296+
"test_index.mappings._doc.obj\\.i_t1.full_name"
297+
)
298+
task.replaceKeyInMatch("test_index.mappings.test_type.obj\\.i_t3.full_name",
299+
"test_index.mappings._doc.obj\\.i_t3.full_name"
300+
)
301+
task.replaceKeyInLength("test_index.mappings.test_type",
302+
"test_index.mappings._doc"
303+
)
304+
task.replaceKeyInMatch("test_index_2.mappings.test_type_2.t1.full_name",
305+
"test_index.mappings._doc.t1.full_name"
306+
)
307+
task.replaceKeyInMatch("test_index_2.mappings.test_type_2.t2.full_name",
308+
"test_index.mappings._doc.t2.full_name"
309+
)
310+
task.replaceKeyInLength("test_index_2.mappings.test_type_2",
311+
"test_index.mappings._doc"
312+
)
313+
task.replaceKeyInMatch("test_index.mappings.test_type.text.mapping.text.type",
314+
"test_index.mappings._doc.text.mapping.text.type"
315+
)
316+
// overrides for indices.put_mapping/11_basic_with_types
317+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.text1.type",
318+
"test_index.mappings._doc.properties.text1.type"
319+
)
320+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.text1.analyzer",
321+
"test_index.mappings._doc.properties.text1.analyzer"
322+
)
323+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.text2.type",
324+
"test_index.mappings._doc.properties.text2.type"
325+
)
326+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.text2.analyzer",
327+
"test_index.mappings._doc.properties.text2.analyzer"
328+
)
329+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.subfield.properties.text3.type",
330+
"test_index.mappings._doc.properties.subfield.properties.text3.type"
331+
)
332+
task.replaceKeyInMatch("test_index.mappings.test_type.properties.text1.fields.text_raw.type",
333+
"test_index.mappings._doc.properties.text1.fields.text_raw.type"
334+
)
335+
// overrides for indices.put_mapping/all_path_options_with_types
336+
task.replaceKeyInMatch("test_index1.mappings.test_type.properties.text.type",
337+
"test_index1.mappings._doc.properties.text.type"
338+
)
339+
task.replaceKeyInMatch("test_index1.mappings.test_type.properties.text.analyzer",
340+
"test_index1.mappings._doc.properties.text.analyzer"
341+
)
342+
task.replaceKeyInMatch("test_index2.mappings.test_type.properties.text.type",
343+
"test_index2.mappings._doc.properties.text.type"
344+
)
345+
task.replaceKeyInMatch("test_index2.mappings.test_type.properties.text.analyzer",
346+
"test_index2.mappings._doc.properties.text.analyzer"
347+
)
348+
task.replaceKeyInMatch("foo.mappings.test_type.properties.text.type",
349+
"foo.mappings._doc.properties.text.type"
350+
)
351+
task.replaceKeyInMatch("foo.mappings.test_type.properties.text.analyzer",
352+
"foo.mappings._doc.properties.text.analyzer"
353+
)
354+
// overrides for indices.get_mapping
355+
task.replaceIsTrue("test_1.mappings.doc", "test_1.mappings._doc")
356+
task.replaceIsTrue("test_2.mappings.doc", "test_2.mappings._doc")
302357
})
303358

304359
tasks.register('enforceYamlTestConvention').configure {

server/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetMappingsResponse.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.elasticsearch.action.ActionResponse;
1414
import org.elasticsearch.cluster.metadata.MappingMetadata;
1515
import org.elasticsearch.common.ParseField;
16+
import org.elasticsearch.common.RestApiVersion;
1617
import org.elasticsearch.common.Strings;
1718
import org.elasticsearch.common.collect.ImmutableOpenMap;
1819
import org.elasticsearch.common.io.stream.StreamInput;
@@ -23,6 +24,9 @@
2324

2425
import java.io.IOException;
2526

27+
import static org.elasticsearch.rest.BaseRestHandler.DEFAULT_INCLUDE_TYPE_NAME_POLICY;
28+
import static org.elasticsearch.rest.BaseRestHandler.INCLUDE_TYPE_NAME_PARAMETER;
29+
2630
public class GetMappingsResponse extends ActionResponse implements ToXContentFragment {
2731

2832
private static final ParseField MAPPINGS = new ParseField("mappings");
@@ -65,7 +69,17 @@ public void writeTo(StreamOutput out) throws IOException {
6569
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
6670
for (final ObjectObjectCursor<String, MappingMetadata> indexEntry : getMappings()) {
6771
builder.startObject(indexEntry.key);
68-
if (indexEntry.value != null) {
72+
boolean includeTypeName = params.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER,
73+
DEFAULT_INCLUDE_TYPE_NAME_POLICY);
74+
if (builder.getRestApiVersion() == RestApiVersion.V_7 && includeTypeName && indexEntry.value != null) {
75+
builder.startObject(MAPPINGS.getPreferredName());
76+
77+
if (indexEntry.value != MappingMetadata.EMPTY_MAPPINGS) {
78+
builder.field(MapperService.SINGLE_MAPPING_NAME, indexEntry.value.sourceAsMap());
79+
}
80+
builder.endObject();
81+
82+
} else if (indexEntry.value != null) {
6983
builder.field(MAPPINGS.getPreferredName(), indexEntry.value.sourceAsMap());
7084
} else {
7185
builder.startObject(MAPPINGS.getPreferredName()).endObject();

server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetFieldMappingAction.java

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,21 @@ public class RestGetFieldMappingAction extends BaseRestHandler {
3838

3939
private static final Logger logger = LogManager.getLogger(RestGetFieldMappingAction.class);
4040
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(logger.getName());
41-
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Using include_type_name in get " +
41+
public static final String INCLUDE_TYPE_DEPRECATION_MESSAGE = "[types removal] Using include_type_name in get " +
4242
"field mapping requests is deprecated. The parameter will be removed in the next major version.";
43-
43+
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in get field mapping request is deprecated. " +
44+
"Use typeless api instead";
4445
@Override
4546
public List<Route> routes() {
4647
return List.of(
4748
new Route(GET, "/_mapping/field/{fields}"),
48-
new Route(GET, "/{index}/_mapping/field/{fields}"));
49+
new Route(GET, "/{index}/_mapping/field/{fields}"),
50+
Route.builder(GET, "/_mapping/{type}/field/{fields}")
51+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
52+
Route.builder(GET, "/{index}/{type}/_mapping/field/{fields}")
53+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
54+
Route.builder(GET, "/{index}/_mapping/{type}/field/{fields}")
55+
.deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build());
4956
}
5057

5158
@Override
@@ -58,9 +65,23 @@ public RestChannelConsumer prepareRequest(final RestRequest request, final NodeC
5865
final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
5966
final String[] fields = Strings.splitStringByCommaToArray(request.param("fields"));
6067

61-
if (request.getRestApiVersion() == RestApiVersion.V_7 && request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
62-
request.param(INCLUDE_TYPE_NAME_PARAMETER);
63-
deprecationLogger.compatibleApiWarning("get_field_mapping_with_types", TYPES_DEPRECATION_MESSAGE);
68+
if (request.getRestApiVersion() == RestApiVersion.V_7) {
69+
if (request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
70+
deprecationLogger.compatibleApiWarning("get_field_mapping_with_types", INCLUDE_TYPE_DEPRECATION_MESSAGE);
71+
}
72+
boolean includeTypeName = request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, DEFAULT_INCLUDE_TYPE_NAME_POLICY);
73+
final String[] types = request.paramAsStringArrayOrEmptyIfAll("type");
74+
if (includeTypeName == false && types.length > 0) {
75+
throw new IllegalArgumentException("Types cannot be specified unless include_type_name" + " is set to true.");
76+
}
77+
78+
if (request.hasParam("local")) {
79+
request.param("local");
80+
deprecationLogger.compatibleApiWarning(
81+
"get_field_mapping_local",
82+
"Use [local] in get field mapping requests is deprecated. " + "The parameter will be removed in the next major version"
83+
);
84+
}
6485
}
6586

6687

server/src/main/java/org/elasticsearch/rest/action/admin/indices/RestGetMappingAction.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@
3131
import java.util.function.LongSupplier;
3232

3333
import static org.elasticsearch.rest.RestRequest.Method.GET;
34+
import static org.elasticsearch.rest.RestRequest.Method.HEAD;
3435

3536
public class RestGetMappingAction extends BaseRestHandler {
3637
private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(RestGetMappingAction.class);
37-
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Using include_type_name in get"
38+
public static final String INCLUDE_TYPE_DEPRECATION_MSG = "[types removal] Using include_type_name in get"
3839
+ " mapping requests is deprecated. The parameter will be removed in the next major version.";
40+
public static final String TYPES_DEPRECATION_MESSAGE = "[types removal] Specifying types in get mapping request is deprecated. " +
41+
"Use typeless api instead";
3942

4043
private final ThreadPool threadPool;
4144

@@ -48,8 +51,13 @@ public List<Route> routes() {
4851
return List.of(
4952
new Route(GET, "/_mapping"),
5053
new Route(GET, "/_mappings"),
54+
Route.builder(GET, "/{index}/{type}/_mapping").deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
5155
new Route(GET, "/{index}/_mapping"),
52-
new Route(GET, "/{index}/_mappings"));
56+
new Route(GET, "/{index}/_mappings"),
57+
Route.builder(GET, "/{index}/_mappings/{type}").deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
58+
Route.builder(GET, "/{index}/_mapping/{type}").deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
59+
Route.builder(HEAD, "/{index}/_mapping/{type}").deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build(),
60+
Route.builder(GET, "/_mapping/{type}").deprecated(TYPES_DEPRECATION_MESSAGE, RestApiVersion.V_7).build());
5361
}
5462

5563
@Override
@@ -59,11 +67,21 @@ public String getName() {
5967

6068
@Override
6169
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
62-
if (request.getRestApiVersion() == RestApiVersion.V_7 && request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
63-
request.param(INCLUDE_TYPE_NAME_PARAMETER);
64-
deprecationLogger.compatibleApiWarning("get_mapping_with_types", TYPES_DEPRECATION_MESSAGE);
70+
if (request.getRestApiVersion() == RestApiVersion.V_7) {
71+
if (request.hasParam(INCLUDE_TYPE_NAME_PARAMETER)) {
72+
request.param(INCLUDE_TYPE_NAME_PARAMETER);
73+
deprecationLogger.compatibleApiWarning("get_mapping_with_types", INCLUDE_TYPE_DEPRECATION_MSG);
74+
}
75+
final String[] types = request.paramAsStringArrayOrEmptyIfAll("type");
76+
if (request.paramAsBoolean(INCLUDE_TYPE_NAME_PARAMETER, DEFAULT_INCLUDE_TYPE_NAME_POLICY) == false && types.length > 0) {
77+
throw new IllegalArgumentException("Types cannot be provided in get mapping requests, unless" +
78+
" include_type_name is set to true.");
79+
}
80+
if (request.method().equals(HEAD)) {
81+
deprecationLogger.compatibleApiWarning("get_mapping_types_removal",
82+
"Type exists requests are deprecated, as types have been deprecated.");
83+
}
6584
}
66-
6785
final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
6886

6987
final GetMappingsRequest getMappingsRequest = new GetMappingsRequest();

0 commit comments

Comments
 (0)