Treat put-mapping calls with `_doc` as a top-level key as typed calls. (#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.
This commit is contained in:
parent
3c439d3b92
commit
a536fa7755
|
@ -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"
|
||||
|
|
|
@ -37,6 +37,8 @@ import org.elasticsearch.common.Priority;
|
|||
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 @@ public class MetaDataMappingService {
|
|||
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 @@ public class MetaDataMappingService {
|
|||
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 class MetaDataMappingService {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,
|
||||
|
|
Loading…
Reference in New Issue