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:
parent
d9699e02f4
commit
5e58f4066e
|
@ -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: { '': {}}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue