Skip to content

Commit

Permalink
Treat put-mapping calls with _doc as a top-level key as typed calls. (
Browse files Browse the repository at this point in the history
#38032)

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.
  • Loading branch information
jpountz authored Jan 31, 2019
1 parent 3c439d3 commit a536fa7
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,45 @@
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":

- skip:
version: " - 6.99.99"
reason: Backport first


- 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"
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.");
}
}
Expand All @@ -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);
Expand Down Expand Up @@ -365,6 +371,15 @@ public String describeTasks(List<PutMappingClusterStateUpdateRequest> 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<String, Object> 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<ClusterStateUpdateResponse> listener) {
clusterService.submitStateUpdateTask("put-mapping",
request,
Expand Down

0 comments on commit a536fa7

Please sign in to comment.