diff --git a/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml b/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml index 0a3dd47c665fc..a9eddc9696e6a 100644 --- a/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml +++ b/rest-api-spec/test/indices.get_field_mapping/20_missing_field.yaml @@ -1,5 +1,5 @@ --- -"Raise 404 when field doesn't exist": +"Return empty object if field doesn't exist, but type and index do": - do: indices.create: @@ -13,9 +13,9 @@ analyzer: whitespace - do: - catch: missing indices.get_field_mapping: index: test_index type: test_type - field: not_text + field: not_existent + - match: { '': {}} diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java index a62ea0d353bfc..470ed6c4e247d 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/GetFieldMappingsResponse.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import org.elasticsearch.action.ActionResponse; +import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; @@ -88,6 +89,8 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws } public static class FieldMappingMetaData implements ToXContent { + public static final FieldMappingMetaData NULL = new FieldMappingMetaData("", BytesArray.EMPTY); + private String fullName; private BytesReference source; diff --git a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java index 3345f40b8e427..f52d5f50e9bf7 100644 --- a/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java +++ b/src/main/java/org/elasticsearch/action/admin/indices/mapping/get/TransportGetFieldMappingsAction.java @@ -92,6 +92,7 @@ private ImmutableMap>> indexMapBuilder = ImmutableMap.builder(); Sets.SetView intersection = Sets.intersection(Sets.newHashSet(concreteIndices), indicesService.indices()); for (String index : intersection) { @@ -114,7 +115,7 @@ public boolean apply(String type) { MapBuilder> typeMappings = new MapBuilder>(); for (String type : typeIntersection) { DocumentMapper documentMapper = indexService.mapperService().documentMapper(type); - ImmutableMap fieldMapping = findFieldMappingsByType(documentMapper, fields, includeDefaults); + ImmutableMap fieldMapping = findFieldMappingsByType(documentMapper, fields, includeDefaults, isProbablySingleFieldRequest); if (!fieldMapping.isEmpty()) { typeMappings.put(type, fieldMapping); } @@ -170,7 +171,7 @@ public Boolean paramAsBooleanOptional(String key, Boolean defaultValue) { }; private ImmutableMap findFieldMappingsByType(DocumentMapper documentMapper, String[] fields, - boolean includeDefaults) throws ElasticsearchException { + boolean includeDefaults, boolean isProbablySingleFieldRequest) throws ElasticsearchException { MapBuilder fieldMappings = new MapBuilder(); ImmutableList allFieldMappers = documentMapper.mappers().mappers(); for (String field : fields) { @@ -215,6 +216,8 @@ private ImmutableMap findFieldMappingsByType(Docum FieldMapper fieldMapper = documentMapper.mappers().smartNameFieldMapper(field); if (fieldMapper != null) { addFieldMapper(field, fieldMapper, fieldMappings, includeDefaults); + } else if (isProbablySingleFieldRequest) { + fieldMappings.put(field, FieldMappingMetaData.NULL); } } } diff --git a/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java index ee32475ee8805..43969c690a8e5 100644 --- a/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java +++ b/src/main/java/org/elasticsearch/rest/action/admin/indices/mapping/get/RestGetFieldMappingAction.java @@ -35,10 +35,12 @@ import org.elasticsearch.rest.action.support.RestXContentBuilder; import java.io.IOException; +import java.util.Map; import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestStatus.NOT_FOUND; import static org.elasticsearch.rest.RestStatus.OK; +import static org.elasticsearch.rest.action.support.RestXContentBuilder.emptyBuilder; /** * @@ -70,14 +72,21 @@ public void handleRequest(final RestRequest request, final RestChannel channel) @Override public void onResponse(GetFieldMappingsResponse response) { try { - XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); - builder.startObject(); - ImmutableMap>> mappingsByIndex = response.mappings(); + + boolean isPossibleSingleFieldRequest = indices.length == 1 && types.length == 1 && fields.length == 1; + if (isPossibleSingleFieldRequest && isFieldMappingMissingField(mappingsByIndex)) { + channel.sendResponse(new XContentRestResponse(request, OK, emptyBuilder(request))); + return; + } + RestStatus status = OK; if (mappingsByIndex.isEmpty() && fields.length > 0) { status = NOT_FOUND; } + + XContentBuilder builder = RestXContentBuilder.restContentBuilder(request); + builder.startObject(); response.toXContent(builder, ToXContent.EMPTY_PARAMS); builder.endObject(); channel.sendResponse(new XContentRestResponse(request, status, builder)); @@ -97,4 +106,25 @@ public void onFailure(Throwable e) { }); } + /** + * + * Helper method to find out if the only included fieldmapping metadata is typed NULL, which means + * that type and index exist, but the field did not + */ + private boolean isFieldMappingMissingField(ImmutableMap>> mappingsByIndex) throws IOException { + if (mappingsByIndex.size() != 1) { + return false; + } + + for (ImmutableMap> value : mappingsByIndex.values()) { + for (ImmutableMap fieldValue : value.values()) { + for (Map.Entry fieldMappingMetaDataEntry : fieldValue.entrySet()) { + if (fieldMappingMetaDataEntry.getValue() == FieldMappingMetaData.NULL) { + return true; + } + } + } + } + return false; + } }