REST API: Consistent get field mapping response

If a get field mapping request is issued, and all but the field can be
found, the response should return an empty JSON object instead of a 404.

Closes #4738
This commit is contained in:
Alexander Reelsen 2014-01-27 22:21:30 +01:00
parent d9699e02f4
commit 5e58f4066e
4 changed files with 44 additions and 8 deletions

View File

@ -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: - do:
indices.create: indices.create:
@ -13,9 +13,9 @@
analyzer: whitespace analyzer: whitespace
- do: - do:
catch: missing
indices.get_field_mapping: indices.get_field_mapping:
index: test_index index: test_index
type: test_type type: test_type
field: not_text field: not_existent
- match: { '': {}}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.action.admin.indices.mapping.get;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import org.elasticsearch.action.ActionResponse; import org.elasticsearch.action.ActionResponse;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.StreamOutput;
@ -88,6 +89,8 @@ public class GetFieldMappingsResponse extends ActionResponse implements ToXConte
} }
public static class FieldMappingMetaData implements ToXContent { public static class FieldMappingMetaData implements ToXContent {
public static final FieldMappingMetaData NULL = new FieldMappingMetaData("", BytesArray.EMPTY);
private String fullName; private String fullName;
private BytesReference source; private BytesReference source;

View File

@ -92,6 +92,7 @@ public class TransportGetFieldMappingsAction extends TransportClusterInfoAction<
return ImmutableMap.of(); return ImmutableMap.of();
} }
boolean isProbablySingleFieldRequest = concreteIndices.length == 1 && types.length == 1 && fields.length == 1;
ImmutableMap.Builder<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> indexMapBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> indexMapBuilder = ImmutableMap.builder();
Sets.SetView<String> intersection = Sets.intersection(Sets.newHashSet(concreteIndices), indicesService.indices()); Sets.SetView<String> intersection = Sets.intersection(Sets.newHashSet(concreteIndices), indicesService.indices());
for (String index : intersection) { for (String index : intersection) {
@ -114,7 +115,7 @@ public class TransportGetFieldMappingsAction extends TransportClusterInfoAction<
MapBuilder<String, ImmutableMap<String, FieldMappingMetaData>> typeMappings = new MapBuilder<String, ImmutableMap<String, FieldMappingMetaData>>(); MapBuilder<String, ImmutableMap<String, FieldMappingMetaData>> typeMappings = new MapBuilder<String, ImmutableMap<String, FieldMappingMetaData>>();
for (String type : typeIntersection) { for (String type : typeIntersection) {
DocumentMapper documentMapper = indexService.mapperService().documentMapper(type); DocumentMapper documentMapper = indexService.mapperService().documentMapper(type);
ImmutableMap<String, FieldMappingMetaData> fieldMapping = findFieldMappingsByType(documentMapper, fields, includeDefaults); ImmutableMap<String, FieldMappingMetaData> fieldMapping = findFieldMappingsByType(documentMapper, fields, includeDefaults, isProbablySingleFieldRequest);
if (!fieldMapping.isEmpty()) { if (!fieldMapping.isEmpty()) {
typeMappings.put(type, fieldMapping); typeMappings.put(type, fieldMapping);
} }
@ -170,7 +171,7 @@ public class TransportGetFieldMappingsAction extends TransportClusterInfoAction<
}; };
private ImmutableMap<String, FieldMappingMetaData> findFieldMappingsByType(DocumentMapper documentMapper, String[] fields, private ImmutableMap<String, FieldMappingMetaData> findFieldMappingsByType(DocumentMapper documentMapper, String[] fields,
boolean includeDefaults) throws ElasticsearchException { boolean includeDefaults, boolean isProbablySingleFieldRequest) throws ElasticsearchException {
MapBuilder<String, FieldMappingMetaData> fieldMappings = new MapBuilder<String, FieldMappingMetaData>(); MapBuilder<String, FieldMappingMetaData> fieldMappings = new MapBuilder<String, FieldMappingMetaData>();
ImmutableList<FieldMapper> allFieldMappers = documentMapper.mappers().mappers(); ImmutableList<FieldMapper> allFieldMappers = documentMapper.mappers().mappers();
for (String field : fields) { for (String field : fields) {
@ -215,6 +216,8 @@ public class TransportGetFieldMappingsAction extends TransportClusterInfoAction<
FieldMapper fieldMapper = documentMapper.mappers().smartNameFieldMapper(field); FieldMapper fieldMapper = documentMapper.mappers().smartNameFieldMapper(field);
if (fieldMapper != null) { if (fieldMapper != null) {
addFieldMapper(field, fieldMapper, fieldMappings, includeDefaults); addFieldMapper(field, fieldMapper, fieldMappings, includeDefaults);
} else if (isProbablySingleFieldRequest) {
fieldMappings.put(field, FieldMappingMetaData.NULL);
} }
} }
} }

View File

@ -35,10 +35,12 @@ import org.elasticsearch.rest.*;
import org.elasticsearch.rest.action.support.RestXContentBuilder; import org.elasticsearch.rest.action.support.RestXContentBuilder;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import static org.elasticsearch.rest.RestRequest.Method.GET; import static org.elasticsearch.rest.RestRequest.Method.GET;
import static org.elasticsearch.rest.RestStatus.NOT_FOUND; import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
import static org.elasticsearch.rest.RestStatus.OK; import static org.elasticsearch.rest.RestStatus.OK;
import static org.elasticsearch.rest.action.support.RestXContentBuilder.emptyBuilder;
/** /**
* *
@ -70,14 +72,21 @@ public class RestGetFieldMappingAction extends BaseRestHandler {
@Override @Override
public void onResponse(GetFieldMappingsResponse response) { public void onResponse(GetFieldMappingsResponse response) {
try { try {
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
builder.startObject();
ImmutableMap<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> mappingsByIndex = response.mappings(); ImmutableMap<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> 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; RestStatus status = OK;
if (mappingsByIndex.isEmpty() && fields.length > 0) { if (mappingsByIndex.isEmpty() && fields.length > 0) {
status = NOT_FOUND; status = NOT_FOUND;
} }
XContentBuilder builder = RestXContentBuilder.restContentBuilder(request);
builder.startObject();
response.toXContent(builder, ToXContent.EMPTY_PARAMS); response.toXContent(builder, ToXContent.EMPTY_PARAMS);
builder.endObject(); builder.endObject();
channel.sendResponse(new XContentRestResponse(request, status, builder)); channel.sendResponse(new XContentRestResponse(request, status, builder));
@ -97,4 +106,25 @@ public class RestGetFieldMappingAction extends BaseRestHandler {
}); });
} }
/**
*
* 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<String, ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>>> mappingsByIndex) throws IOException {
if (mappingsByIndex.size() != 1) {
return false;
}
for (ImmutableMap<String, ImmutableMap<String, FieldMappingMetaData>> value : mappingsByIndex.values()) {
for (ImmutableMap<String, FieldMappingMetaData> fieldValue : value.values()) {
for (Map.Entry<String, FieldMappingMetaData> fieldMappingMetaDataEntry : fieldValue.entrySet()) {
if (fieldMappingMetaDataEntry.getValue() == FieldMappingMetaData.NULL) {
return true;
}
}
}
}
return false;
}
} }