From 0669240b76ce57da93b580252f4611732910a7ae Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 30 Jan 2019 14:26:31 +0100 Subject: [PATCH 1/3] Treat put-mapping calls with `_doc` as a top-level key as typed calls. Currently the put-mapping API assumes that because the type name is `_doc` then it is dealing with a typeless put-mapping call. Yet we still allow running the put-mapping API in a typed fashion with `_doc` as a type name. The current logic triggers surprising errors when doing a typed put-mapping call with `_doc` as a type name on an index that has a type already. This is a bit of a corner-case, but is more important on 6.x due to the fact that using the index API with `_doc` as a type name triggers typed calls to the put-mapping API with `_doc` as a type name. --- .../20_mix_typeless_typeful.yml | 30 ++++++++++++++++++- .../metadata/MetaDataMappingService.java | 25 ++++++++++++---- 2 files changed, 49 insertions(+), 6 deletions(-) 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 index 3aedff101110b..adc535b67937d 100644 --- 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 @@ -39,12 +39,40 @@ type: "keyword" # also test no-op updates that trigger special logic wrt the mapping version - do: - catch: bad_request + catch: /the final mapping would have more than 1 type/ indices.put_mapping: include_type_name: true index: index + type: some_other_type body: some_other_type: properties: bar: type: "long" + + +--- +"PUT mapping with _doc on an index that has types": + + - do: + indices.create: + include_type_name: true + index: index + body: + mappings: + my_type: + properties: + foo: + type: "keyword" + + - do: + catch: /the final mapping would have more than 1 type/ + indices.put_mapping: + include_type_name: true + index: index + type: _doc + body: + _doc: + properties: + bar: + type: "long" 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 06dda07d2289e..cf31401983a62 100644 --- a/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java +++ b/server/src/main/java/org/elasticsearch/cluster/metadata/MetaDataMappingService.java @@ -37,6 +37,8 @@ import org.elasticsearch.common.compress.CompressedXContent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.unit.TimeValue; +import org.elasticsearch.common.xcontent.XContentHelper; +import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.index.Index; import org.elasticsearch.index.IndexService; import org.elasticsearch.index.mapper.DocumentMapper; @@ -277,7 +279,8 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt if (mappingType == null) { mappingType = newMapper.type(); } else if (mappingType.equals(newMapper.type()) == false - && mapperService.resolveDocumentType(mappingType).equals(newMapper.type()) == false) { + && (isMappingSourceTyped(mapperService, mappingUpdateSource, request.type()) + || mapperService.resolveDocumentType(mappingType).equals(newMapper.type()) == false)) { throw new InvalidTypeNameException("Type name provided does not match type name within mapping definition."); } } @@ -297,10 +300,13 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt final Index index = indexMetaData.getIndex(); final MapperService mapperService = indexMapperServices.get(index); - // If the user gave _doc as a special type value or if they are using the new typeless APIs, - // then we apply the mapping update to the existing type. This allows to move to typeless - // APIs with indices whose type name is different from `_doc`. - String typeForUpdate = mapperService.resolveDocumentType(mappingType); // the type to use to apply the mapping update + // If the _type name is _doc and there is no _doc top-level key then this means that we + // are handling a typeless call. In such a case, we override _doc with the actual type + // name in the mappings. This allows to use typeless APIs on typed indices. + String typeForUpdate = mappingType; // the type to use to apply the mapping update + if (isMappingSourceTyped(mapperService, mappingUpdateSource, request.type()) == false) { + typeForUpdate = mapperService.resolveDocumentType(mappingType); + } CompressedXContent existingSource = null; DocumentMapper existingMapper = mapperService.documentMapper(typeForUpdate); @@ -365,6 +371,15 @@ public String describeTasks(List tasks) { } } + /** + * Returns {@code true} if the given {@code mappingSource} includes a type + * as a top-level object. + */ + private static boolean isMappingSourceTyped(MapperService mapperService, CompressedXContent mappingSource, String type) { + Map root = XContentHelper.convertToMap(mappingSource.compressedReference(), true, XContentType.JSON).v2(); + return root.size() == 1 && root.keySet().iterator().next().equals(type); + } + public void putMapping(final PutMappingClusterStateUpdateRequest request, final ActionListener listener) { clusterService.submitStateUpdateTask("put-mapping", request, From 3f18fa7ab1795cd270f4f3b65385fba2622caa83 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Wed, 30 Jan 2019 21:50:56 +0100 Subject: [PATCH 2/3] iter --- .../test/indices.put_mapping/20_mix_typeless_typeful.yml | 5 +++++ 1 file changed, 5 insertions(+) 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 index adc535b67937d..7ebede8b24bd2 100644 --- 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 @@ -54,6 +54,11 @@ --- "PUT mapping with _doc on an index that has types": + - skip: + version: " - 6.99.99" + reason: Backport first + + - do: indices.create: include_type_name: true From eaec5720b471c66a73201e520ba3d9d92ab584d6 Mon Sep 17 00:00:00 2001 From: Adrien Grand Date: Thu, 31 Jan 2019 12:20:41 +0100 Subject: [PATCH 3/3] iter --- .../test/indices.put_mapping/20_mix_typeless_typeful.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index 7ebede8b24bd2..d964a382137f8 100644 --- 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 @@ -54,7 +54,7 @@ --- "PUT mapping with _doc on an index that has types": - - skip: + - skip: version: " - 6.99.99" reason: Backport first