|
38 | 38 | import org.elasticsearch.common.compress.CompressedXContent;
|
39 | 39 | import org.elasticsearch.common.inject.Inject;
|
40 | 40 | import org.elasticsearch.common.unit.TimeValue;
|
| 41 | +import org.elasticsearch.common.xcontent.XContentHelper; |
| 42 | +import org.elasticsearch.common.xcontent.XContentType; |
41 | 43 | import org.elasticsearch.core.internal.io.IOUtils;
|
42 | 44 | import org.elasticsearch.index.Index;
|
43 | 45 | import org.elasticsearch.index.IndexService;
|
@@ -273,7 +275,10 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt
|
273 | 275 | updateList.add(indexMetaData);
|
274 | 276 | // try and parse it (no need to add it here) so we can bail early in case of parsing exception
|
275 | 277 | DocumentMapper newMapper;
|
276 |
| - DocumentMapper existingMapper = getMapperForUpdate(mapperService, mappingType); |
| 278 | + DocumentMapper existingMapper = mapperService.documentMapper(mappingType); |
| 279 | + if (existingMapper == null && isMappingSourceTyped(mapperService, mappingUpdateSource, request.type()) == false) { |
| 280 | + existingMapper = getMapperForUpdate(mapperService, mappingType); |
| 281 | + } |
277 | 282 | String typeForUpdate = existingMapper == null ? mappingType : existingMapper.type();
|
278 | 283 |
|
279 | 284 | if (MapperService.DEFAULT_MAPPING.equals(typeForUpdate)) {
|
@@ -325,9 +330,16 @@ private ClusterState applyRequest(ClusterState currentState, PutMappingClusterSt
|
325 | 330 | // we use the exact same indexService and metadata we used to validate above here to actually apply the update
|
326 | 331 | final Index index = indexMetaData.getIndex();
|
327 | 332 | final MapperService mapperService = indexMapperServices.get(index);
|
| 333 | + |
| 334 | + // If the _type name is _doc and there is no _doc top-level key then this means that we |
| 335 | + // are handling a typeless call. In such a case, we override _doc with the actual type |
| 336 | + // name in the mappings. This allows to use typeless APIs on typed indices. |
328 | 337 | String typeForUpdate = mappingType;
|
329 | 338 | CompressedXContent existingSource = null;
|
330 |
| - DocumentMapper existingMapper = getMapperForUpdate(mapperService, mappingType); |
| 339 | + DocumentMapper existingMapper = mapperService.documentMapper(mappingType); |
| 340 | + if (existingMapper == null && isMappingSourceTyped(mapperService, mappingUpdateSource, request.type()) == false) { |
| 341 | + existingMapper = getMapperForUpdate(mapperService, mappingType); |
| 342 | + } |
331 | 343 | if (existingMapper != null) {
|
332 | 344 | typeForUpdate = existingMapper.type();
|
333 | 345 | existingSource = existingMapper.mappingSource();
|
@@ -388,6 +400,15 @@ public String describeTasks(List<PutMappingClusterStateUpdateRequest> tasks) {
|
388 | 400 | }
|
389 | 401 | }
|
390 | 402 |
|
| 403 | + /** |
| 404 | + * Returns {@code true} if the given {@code mappingSource} includes a type |
| 405 | + * as a top-level object. |
| 406 | + */ |
| 407 | + private static boolean isMappingSourceTyped(MapperService mapperService, CompressedXContent mappingSource, String type) { |
| 408 | + Map<String, Object> root = XContentHelper.convertToMap(mappingSource.compressedReference(), true, XContentType.JSON).v2(); |
| 409 | + return root.size() == 1 && root.keySet().iterator().next().equals(type); |
| 410 | + } |
| 411 | + |
391 | 412 | public void putMapping(final PutMappingClusterStateUpdateRequest request, final ActionListener<ClusterStateUpdateResponse> listener) {
|
392 | 413 | clusterService.submitStateUpdateTask("put-mapping",
|
393 | 414 | request,
|
|
0 commit comments