Today the `_field_caps` API returns the list of indices where a field is present only if this field has different types within the requested indices. However if the request is an index pattern (or an alias, or both...) there is no way to infer the indices if the response contains only fields that have the same type in all indices. This commit changes the response to always return the list of indices in the response. It also adds a way to retrieve unmapped field in a specific section per field called `unmapped`. This section is created for each field that is present in some indices but not all if the parameter `include_unmapped` is set to true in the request (defaults to false).
This commit is contained in:
parent
40aef2b8aa
commit
6184efaff6
|
@ -1255,6 +1255,8 @@ public class SearchIT extends ESRestHighLevelClientTestCase {
|
||||||
FieldCapabilitiesResponse response = execute(request,
|
FieldCapabilitiesResponse response = execute(request,
|
||||||
highLevelClient()::fieldCaps, highLevelClient()::fieldCapsAsync);
|
highLevelClient()::fieldCaps, highLevelClient()::fieldCapsAsync);
|
||||||
|
|
||||||
|
assertEquals(new String[] {"index1", "index2"}, response.getIndices());
|
||||||
|
|
||||||
// Check the capabilities for the 'rating' field.
|
// Check the capabilities for the 'rating' field.
|
||||||
assertTrue(response.get().containsKey("rating"));
|
assertTrue(response.get().containsKey("rating"));
|
||||||
Map<String, FieldCapabilities> ratingResponse = response.getField("rating");
|
Map<String, FieldCapabilities> ratingResponse = response.getField("rating");
|
||||||
|
|
|
@ -71,6 +71,7 @@ GET _field_caps?fields=rating,title
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
{
|
{
|
||||||
"fields": {
|
"fields": {
|
||||||
|
"indices": ["index1", "index2", "index3", "index4", "index5"],
|
||||||
"rating": { <1>
|
"rating": { <1>
|
||||||
"long": {
|
"long": {
|
||||||
"searchable": true,
|
"searchable": true,
|
||||||
|
@ -103,9 +104,61 @@ and as a `keyword` in `index3` and `index4`.
|
||||||
<3> The field `rating` is not searchable in `index4`.
|
<3> The field `rating` is not searchable in `index4`.
|
||||||
<4> The field `title` is defined as `text` in all indices.
|
<4> The field `title` is defined as `text` in all indices.
|
||||||
|
|
||||||
|
[float]
|
||||||
|
=== Unmapped fields
|
||||||
|
|
||||||
|
By default unmapped fields are ignored. You can include them in the response by
|
||||||
|
adding a parameter called `include_unmapped` in the request:
|
||||||
|
|
||||||
|
[source,js]
|
||||||
|
--------------------------------------------------
|
||||||
|
GET _field_caps?fields=rating,title&include_unmapped
|
||||||
|
--------------------------------------------------
|
||||||
|
// CONSOLE
|
||||||
|
|
||||||
|
In which case the response will contain an entry for each field that is present in
|
||||||
|
some indices but not all:
|
||||||
|
|
||||||
|
[source,js]
|
||||||
|
--------------------------------------------------
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"indices": ["index1", "index2", "index3"],
|
||||||
|
"rating": {
|
||||||
|
"long": {
|
||||||
|
"searchable": true,
|
||||||
|
"aggregatable": false,
|
||||||
|
"indices": ["index1", "index2"],
|
||||||
|
"non_aggregatable_indices": ["index1"]
|
||||||
|
},
|
||||||
|
"keyword": {
|
||||||
|
"searchable": false,
|
||||||
|
"aggregatable": true,
|
||||||
|
"indices": ["index3", "index4"],
|
||||||
|
"non_searchable_indices": ["index4"]
|
||||||
|
},
|
||||||
|
"unmapped": { <1>
|
||||||
|
"indices": ["index5"],
|
||||||
|
"searchable": false,
|
||||||
|
"aggregatable": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"text": {
|
||||||
|
"indices": ["index1", "index2", "index3", "index4"],
|
||||||
|
"searchable": true,
|
||||||
|
"aggregatable": false
|
||||||
|
},
|
||||||
|
"unmapped": { <2>
|
||||||
|
"indices": ["index5"]
|
||||||
|
"searchable": false,
|
||||||
|
"aggregatable": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
--------------------------------------------------
|
||||||
|
// NOTCONSOLE
|
||||||
|
|
||||||
|
<1> The `rating` field is unmapped` in `index5`.
|
||||||
|
<2> The `title` field is unmapped` in `index5`.
|
||||||
|
|
|
@ -32,6 +32,11 @@
|
||||||
"options" : ["open","closed","none","all"],
|
"options" : ["open","closed","none","all"],
|
||||||
"default" : "open",
|
"default" : "open",
|
||||||
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
|
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
|
||||||
|
},
|
||||||
|
"include_unmapped": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"description": "Indicates whether unmapped fields should be included in the response."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,8 +14,8 @@ setup:
|
||||||
type: double
|
type: double
|
||||||
geo:
|
geo:
|
||||||
type: geo_point
|
type: geo_point
|
||||||
date:
|
misc:
|
||||||
type: date
|
type: text
|
||||||
object:
|
object:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -299,3 +299,28 @@ setup:
|
||||||
- match: {fields.geo.keyword.indices: ["test3"]}
|
- match: {fields.geo.keyword.indices: ["test3"]}
|
||||||
- is_false: fields.geo.keyword.non_searchable_indices
|
- is_false: fields.geo.keyword.non_searchable_indices
|
||||||
- is_false: fields.geo.keyword.on_aggregatable_indices
|
- is_false: fields.geo.keyword.on_aggregatable_indices
|
||||||
|
|
||||||
|
---
|
||||||
|
"Field caps with include_unmapped":
|
||||||
|
- skip:
|
||||||
|
version: " - 7.0.99"
|
||||||
|
reason: include_unmapped has been added in 7.1.0
|
||||||
|
|
||||||
|
- do:
|
||||||
|
field_caps:
|
||||||
|
include_unmapped: true
|
||||||
|
index: 'test1,test2,test3'
|
||||||
|
fields: [text, misc]
|
||||||
|
|
||||||
|
- match: {fields.text.text.searchable: true}
|
||||||
|
- match: {fields.text.text.aggregatable: false}
|
||||||
|
- is_false: fields.text.text.indices
|
||||||
|
- is_false: fields.text.text.non_searchable_indices
|
||||||
|
- is_false: fields.text.text.non_aggregatable_indices
|
||||||
|
- match: {fields.misc.text.searchable: true}
|
||||||
|
- match: {fields.misc.text.aggregatable: false}
|
||||||
|
- match: {fields.misc.text.indices: ["test1"]}
|
||||||
|
- match: {fields.misc.unmapped.searchable: false}
|
||||||
|
- match: {fields.misc.unmapped.aggregatable: false}
|
||||||
|
- match: {fields.misc.unmapped.indices: ["test2", "test3"]}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.elasticsearch.action.fieldcaps;
|
package org.elasticsearch.action.fieldcaps;
|
||||||
|
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
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;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
@ -34,6 +35,8 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes the capabilities of a field optionally merged across multiple indices.
|
* Describes the capabilities of a field optionally merged across multiple indices.
|
||||||
|
@ -214,30 +217,30 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
FieldCapabilities that = (FieldCapabilities) o;
|
FieldCapabilities that = (FieldCapabilities) o;
|
||||||
|
return isSearchable == that.isSearchable &&
|
||||||
if (isSearchable != that.isSearchable) return false;
|
isAggregatable == that.isAggregatable &&
|
||||||
if (isAggregatable != that.isAggregatable) return false;
|
Objects.equals(name, that.name) &&
|
||||||
if (!name.equals(that.name)) return false;
|
Objects.equals(type, that.type) &&
|
||||||
if (!type.equals(that.type)) return false;
|
Arrays.equals(indices, that.indices) &&
|
||||||
if (!Arrays.equals(indices, that.indices)) return false;
|
Arrays.equals(nonSearchableIndices, that.nonSearchableIndices) &&
|
||||||
if (!Arrays.equals(nonSearchableIndices, that.nonSearchableIndices)) return false;
|
Arrays.equals(nonAggregatableIndices, that.nonAggregatableIndices);
|
||||||
return Arrays.equals(nonAggregatableIndices, that.nonAggregatableIndices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = name.hashCode();
|
int result = Objects.hash(name, type, isSearchable, isAggregatable);
|
||||||
result = 31 * result + type.hashCode();
|
|
||||||
result = 31 * result + (isSearchable ? 1 : 0);
|
|
||||||
result = 31 * result + (isAggregatable ? 1 : 0);
|
|
||||||
result = 31 * result + Arrays.hashCode(indices);
|
result = 31 * result + Arrays.hashCode(indices);
|
||||||
result = 31 * result + Arrays.hashCode(nonSearchableIndices);
|
result = 31 * result + Arrays.hashCode(nonSearchableIndices);
|
||||||
result = 31 * result + Arrays.hashCode(nonAggregatableIndices);
|
result = 31 * result + Arrays.hashCode(nonAggregatableIndices);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Strings.toString(this);
|
||||||
|
}
|
||||||
|
|
||||||
static class Builder {
|
static class Builder {
|
||||||
private String name;
|
private String name;
|
||||||
private String type;
|
private String type;
|
||||||
|
@ -260,6 +263,10 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
||||||
this.isAggregatable &= agg;
|
this.isAggregatable &= agg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> getIndices() {
|
||||||
|
return indiceList.stream().map(c -> c.name).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
FieldCapabilities build(boolean withIndices) {
|
FieldCapabilities build(boolean withIndices) {
|
||||||
final String[] indices;
|
final String[] indices;
|
||||||
/* Eclipse can't deal with o -> o.name, maybe because of
|
/* Eclipse can't deal with o -> o.name, maybe because of
|
||||||
|
|
|
@ -29,8 +29,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class FieldCapabilitiesIndexRequest
|
public class FieldCapabilitiesIndexRequest extends SingleShardRequest<FieldCapabilitiesIndexRequest> {
|
||||||
extends SingleShardRequest<FieldCapabilitiesIndexRequest> {
|
|
||||||
|
|
||||||
private String[] fields;
|
private String[] fields;
|
||||||
private OriginalIndices originalIndices;
|
private OriginalIndices originalIndices;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response for {@link FieldCapabilitiesIndexRequest} requests.
|
* Response for {@link FieldCapabilitiesIndexRequest} requests.
|
||||||
|
@ -89,14 +90,13 @@ public class FieldCapabilitiesIndexResponse extends ActionResponse implements Wr
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
FieldCapabilitiesIndexResponse that = (FieldCapabilitiesIndexResponse) o;
|
FieldCapabilitiesIndexResponse that = (FieldCapabilitiesIndexResponse) o;
|
||||||
|
return Objects.equals(indexName, that.indexName) &&
|
||||||
return responseMap.equals(that.responseMap);
|
Objects.equals(responseMap, that.responseMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return responseMap.hashCode();
|
return Objects.hash(indexName, responseMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.fieldcaps;
|
package org.elasticsearch.action.fieldcaps;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionRequest;
|
import org.elasticsearch.action.ActionRequest;
|
||||||
import org.elasticsearch.action.ActionRequestValidationException;
|
import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
import org.elasticsearch.action.IndicesRequest;
|
import org.elasticsearch.action.IndicesRequest;
|
||||||
|
@ -44,6 +45,7 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
private String[] indices = Strings.EMPTY_ARRAY;
|
private String[] indices = Strings.EMPTY_ARRAY;
|
||||||
private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen();
|
private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen();
|
||||||
private String[] fields = Strings.EMPTY_ARRAY;
|
private String[] fields = Strings.EMPTY_ARRAY;
|
||||||
|
private boolean includeUnmapped = false;
|
||||||
// pkg private API mainly for cross cluster search to signal that we do multiple reductions ie. the results should not be merged
|
// pkg private API mainly for cross cluster search to signal that we do multiple reductions ie. the results should not be merged
|
||||||
private boolean mergeResults = true;
|
private boolean mergeResults = true;
|
||||||
|
|
||||||
|
@ -51,8 +53,7 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
new ObjectParser<>(NAME, FieldCapabilitiesRequest::new);
|
new ObjectParser<>(NAME, FieldCapabilitiesRequest::new);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PARSER.declareStringArray(fromList(String.class, FieldCapabilitiesRequest::fields),
|
PARSER.declareStringArray(fromList(String.class, FieldCapabilitiesRequest::fields), FIELDS_FIELD);
|
||||||
FIELDS_FIELD);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldCapabilitiesRequest() {}
|
public FieldCapabilitiesRequest() {}
|
||||||
|
@ -83,6 +84,11 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
indices = in.readStringArray();
|
indices = in.readStringArray();
|
||||||
indicesOptions = IndicesOptions.readIndicesOptions(in);
|
indicesOptions = IndicesOptions.readIndicesOptions(in);
|
||||||
mergeResults = in.readBoolean();
|
mergeResults = in.readBoolean();
|
||||||
|
if (in.getVersion().onOrAfter(Version.V_7_1_0)) {
|
||||||
|
includeUnmapped = in.readBoolean();
|
||||||
|
} else {
|
||||||
|
includeUnmapped = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,6 +98,9 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
out.writeStringArray(indices);
|
out.writeStringArray(indices);
|
||||||
indicesOptions.writeIndicesOptions(out);
|
indicesOptions.writeIndicesOptions(out);
|
||||||
out.writeBoolean(mergeResults);
|
out.writeBoolean(mergeResults);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_1_0)) {
|
||||||
|
out.writeBoolean(includeUnmapped);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,6 +132,11 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FieldCapabilitiesRequest includeUnmapped(boolean includeUnmapped) {
|
||||||
|
this.includeUnmapped = includeUnmapped;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] indices() {
|
public String[] indices() {
|
||||||
return indices;
|
return indices;
|
||||||
|
@ -133,12 +147,15 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
return indicesOptions;
|
return indicesOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean includeUnmapped() {
|
||||||
|
return includeUnmapped;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionRequestValidationException validate() {
|
public ActionRequestValidationException validate() {
|
||||||
ActionRequestValidationException validationException = null;
|
ActionRequestValidationException validationException = null;
|
||||||
if (fields == null || fields.length == 0) {
|
if (fields == null || fields.length == 0) {
|
||||||
validationException =
|
validationException = ValidateActions.addValidationError("no fields specified", validationException);
|
||||||
ValidateActions.addValidationError("no fields specified", validationException);
|
|
||||||
}
|
}
|
||||||
return validationException;
|
return validationException;
|
||||||
}
|
}
|
||||||
|
@ -152,14 +169,12 @@ public final class FieldCapabilitiesRequest extends ActionRequest implements Ind
|
||||||
return Arrays.equals(indices, that.indices) &&
|
return Arrays.equals(indices, that.indices) &&
|
||||||
Objects.equals(indicesOptions, that.indicesOptions) &&
|
Objects.equals(indicesOptions, that.indicesOptions) &&
|
||||||
Arrays.equals(fields, that.fields) &&
|
Arrays.equals(fields, that.fields) &&
|
||||||
Objects.equals(mergeResults, that.mergeResults);
|
Objects.equals(mergeResults, that.mergeResults) &&
|
||||||
|
includeUnmapped == that.includeUnmapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(Arrays.hashCode(indices),
|
return Objects.hash(Arrays.hashCode(indices), indicesOptions, Arrays.hashCode(fields), mergeResults, includeUnmapped);
|
||||||
indicesOptions,
|
|
||||||
Arrays.hashCode(fields),
|
|
||||||
mergeResults);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,4 +36,9 @@ public class FieldCapabilitiesRequestBuilder extends ActionRequestBuilder<FieldC
|
||||||
request().fields(fields);
|
request().fields(fields);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FieldCapabilitiesRequestBuilder setIncludeUnmapped(boolean includeUnmapped) {
|
||||||
|
request().includeUnmapped(includeUnmapped);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.fieldcaps;
|
package org.elasticsearch.action.fieldcaps;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.collect.Tuple;
|
import org.elasticsearch.common.collect.Tuple;
|
||||||
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;
|
||||||
|
@ -31,6 +33,7 @@ import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
import org.elasticsearch.common.xcontent.XContentParserUtils;
|
import org.elasticsearch.common.xcontent.XContentParserUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -42,32 +45,43 @@ import java.util.stream.Collectors;
|
||||||
* Response for {@link FieldCapabilitiesRequest} requests.
|
* Response for {@link FieldCapabilitiesRequest} requests.
|
||||||
*/
|
*/
|
||||||
public class FieldCapabilitiesResponse extends ActionResponse implements ToXContentObject {
|
public class FieldCapabilitiesResponse extends ActionResponse implements ToXContentObject {
|
||||||
|
private static final ParseField INDICES_FIELD = new ParseField("indices");
|
||||||
private static final ParseField FIELDS_FIELD = new ParseField("fields");
|
private static final ParseField FIELDS_FIELD = new ParseField("fields");
|
||||||
|
|
||||||
|
private String[] indices;
|
||||||
private Map<String, Map<String, FieldCapabilities>> responseMap;
|
private Map<String, Map<String, FieldCapabilities>> responseMap;
|
||||||
private List<FieldCapabilitiesIndexResponse> indexResponses;
|
private List<FieldCapabilitiesIndexResponse> indexResponses;
|
||||||
|
|
||||||
FieldCapabilitiesResponse(Map<String, Map<String, FieldCapabilities>> responseMap) {
|
FieldCapabilitiesResponse(String[] indices, Map<String, Map<String, FieldCapabilities>> responseMap) {
|
||||||
this(responseMap, Collections.emptyList());
|
this(indices, responseMap, Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
FieldCapabilitiesResponse(List<FieldCapabilitiesIndexResponse> indexResponses) {
|
FieldCapabilitiesResponse(List<FieldCapabilitiesIndexResponse> indexResponses) {
|
||||||
this(Collections.emptyMap(), indexResponses);
|
this(Strings.EMPTY_ARRAY, Collections.emptyMap(), indexResponses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldCapabilitiesResponse(Map<String, Map<String, FieldCapabilities>> responseMap,
|
private FieldCapabilitiesResponse(String[] indices, Map<String, Map<String, FieldCapabilities>> responseMap,
|
||||||
List<FieldCapabilitiesIndexResponse> indexResponses) {
|
List<FieldCapabilitiesIndexResponse> indexResponses) {
|
||||||
this.responseMap = Objects.requireNonNull(responseMap);
|
this.responseMap = Objects.requireNonNull(responseMap);
|
||||||
this.indexResponses = Objects.requireNonNull(indexResponses);
|
this.indexResponses = Objects.requireNonNull(indexResponses);
|
||||||
|
this.indices = indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for serialization
|
* Used for serialization
|
||||||
*/
|
*/
|
||||||
FieldCapabilitiesResponse() {
|
FieldCapabilitiesResponse() {
|
||||||
this(Collections.emptyMap(), Collections.emptyList());
|
this(Strings.EMPTY_ARRAY, Collections.emptyMap(), Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the concrete list of indices that were requested.
|
||||||
|
*/
|
||||||
|
public String[] getIndices() {
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the field capabilities map.
|
* Get the field capabilities map.
|
||||||
*/
|
*/
|
||||||
|
@ -94,8 +108,12 @@ public class FieldCapabilitiesResponse extends ActionResponse implements ToXCont
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
this.responseMap =
|
if (in.getVersion().onOrAfter(Version.V_7_1_0)) {
|
||||||
in.readMap(StreamInput::readString, FieldCapabilitiesResponse::readField);
|
indices = in.readStringArray();
|
||||||
|
} else {
|
||||||
|
indices = Strings.EMPTY_ARRAY;
|
||||||
|
}
|
||||||
|
this.responseMap = in.readMap(StreamInput::readString, FieldCapabilitiesResponse::readField);
|
||||||
indexResponses = in.readList(FieldCapabilitiesIndexResponse::new);
|
indexResponses = in.readList(FieldCapabilitiesIndexResponse::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,20 +124,27 @@ public class FieldCapabilitiesResponse extends ActionResponse implements ToXCont
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
|
if (out.getVersion().onOrAfter(Version.V_7_1_0)) {
|
||||||
|
out.writeStringArray(indices);
|
||||||
|
}
|
||||||
out.writeMap(responseMap, StreamOutput::writeString, FieldCapabilitiesResponse::writeField);
|
out.writeMap(responseMap, StreamOutput::writeString, FieldCapabilitiesResponse::writeField);
|
||||||
out.writeList(indexResponses);
|
out.writeList(indexResponses);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writeField(StreamOutput out,
|
private static void writeField(StreamOutput out, Map<String, FieldCapabilities> map) throws IOException {
|
||||||
Map<String, FieldCapabilities> map) throws IOException {
|
|
||||||
out.writeMap(map, StreamOutput::writeString, (valueOut, fc) -> fc.writeTo(valueOut));
|
out.writeMap(map, StreamOutput::writeString, (valueOut, fc) -> fc.writeTo(valueOut));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
return builder.startObject()
|
if (indexResponses.size() > 0) {
|
||||||
.field(FIELDS_FIELD.getPreferredName(), responseMap)
|
throw new IllegalStateException("cannot serialize non-merged response");
|
||||||
.endObject();
|
}
|
||||||
|
builder.startObject();
|
||||||
|
builder.field(INDICES_FIELD.getPreferredName(), indices);
|
||||||
|
builder.field(FIELDS_FIELD.getPreferredName(), responseMap);
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FieldCapabilitiesResponse fromXContent(XContentParser parser) throws IOException {
|
public static FieldCapabilitiesResponse fromXContent(XContentParser parser) throws IOException {
|
||||||
|
@ -129,11 +154,14 @@ public class FieldCapabilitiesResponse extends ActionResponse implements ToXCont
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static final ConstructingObjectParser<FieldCapabilitiesResponse, Void> PARSER =
|
private static final ConstructingObjectParser<FieldCapabilitiesResponse, Void> PARSER =
|
||||||
new ConstructingObjectParser<>("field_capabilities_response", true,
|
new ConstructingObjectParser<>("field_capabilities_response", true,
|
||||||
a -> new FieldCapabilitiesResponse(
|
a -> {
|
||||||
((List<Tuple<String, Map<String, FieldCapabilities>>>) a[0]).stream()
|
List<String> indices = a[0] == null ? Collections.emptyList() : (List<String>) a[0];
|
||||||
.collect(Collectors.toMap(Tuple::v1, Tuple::v2))));
|
return new FieldCapabilitiesResponse(indices.stream().toArray(String[]::new),
|
||||||
|
((List<Tuple<String, Map<String, FieldCapabilities>>>) a[1]).stream().collect(Collectors.toMap(Tuple::v1, Tuple::v2)));
|
||||||
|
});
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), INDICES_FIELD);
|
||||||
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> {
|
PARSER.declareNamedObjects(ConstructingObjectParser.constructorArg(), (p, c, n) -> {
|
||||||
Map<String, FieldCapabilities> typeToCapabilities = parseTypeToCapabilities(p, n);
|
Map<String, FieldCapabilities> typeToCapabilities = parseTypeToCapabilities(p, n);
|
||||||
return new Tuple<>(n, typeToCapabilities);
|
return new Tuple<>(n, typeToCapabilities);
|
||||||
|
@ -158,14 +186,21 @@ public class FieldCapabilitiesResponse extends ActionResponse implements ToXCont
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
|
||||||
FieldCapabilitiesResponse that = (FieldCapabilitiesResponse) o;
|
FieldCapabilitiesResponse that = (FieldCapabilitiesResponse) o;
|
||||||
|
return Arrays.equals(indices, that.indices) &&
|
||||||
return responseMap.equals(that.responseMap);
|
Objects.equals(responseMap, that.responseMap) &&
|
||||||
|
Objects.equals(indexResponses, that.indexResponses);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return responseMap.hashCode();
|
int result = Objects.hash(responseMap, indexResponses);
|
||||||
|
result = 31 * result + Arrays.hashCode(indices);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return Strings.toString(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,13 @@ import org.elasticsearch.transport.RemoteClusterService;
|
||||||
import org.elasticsearch.transport.TransportService;
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class TransportFieldCapabilitiesAction extends HandledTransportAction<FieldCapabilitiesRequest, FieldCapabilitiesResponse> {
|
public class TransportFieldCapabilitiesAction extends HandledTransportAction<FieldCapabilitiesRequest, FieldCapabilitiesResponse> {
|
||||||
private final ThreadPool threadPool;
|
private final ThreadPool threadPool;
|
||||||
|
@ -75,20 +78,21 @@ public class TransportFieldCapabilitiesAction extends HandledTransportAction<Fie
|
||||||
} else {
|
} else {
|
||||||
concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, localIndices);
|
concreteIndices = indexNameExpressionResolver.concreteIndexNames(clusterState, localIndices);
|
||||||
}
|
}
|
||||||
|
final String[] allIndices = mergeIndiceNames(concreteIndices, remoteClusterIndices);
|
||||||
final int totalNumRequest = concreteIndices.length + remoteClusterIndices.size();
|
final int totalNumRequest = concreteIndices.length + remoteClusterIndices.size();
|
||||||
final CountDown completionCounter = new CountDown(totalNumRequest);
|
final CountDown completionCounter = new CountDown(totalNumRequest);
|
||||||
final List<FieldCapabilitiesIndexResponse> indexResponses = Collections.synchronizedList(new ArrayList<>());
|
final List<FieldCapabilitiesIndexResponse> indexResponses = Collections.synchronizedList(new ArrayList<>());
|
||||||
final Runnable onResponse = () -> {
|
final Runnable onResponse = () -> {
|
||||||
if (completionCounter.countDown()) {
|
if (completionCounter.countDown()) {
|
||||||
if (request.isMergeResults()) {
|
if (request.isMergeResults()) {
|
||||||
listener.onResponse(merge(indexResponses));
|
listener.onResponse(merge(allIndices, indexResponses, request.includeUnmapped()));
|
||||||
} else {
|
} else {
|
||||||
listener.onResponse(new FieldCapabilitiesResponse(indexResponses));
|
listener.onResponse(new FieldCapabilitiesResponse(indexResponses));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (totalNumRequest == 0) {
|
if (totalNumRequest == 0) {
|
||||||
listener.onResponse(new FieldCapabilitiesResponse(Collections.emptyMap()));
|
listener.onResponse(new FieldCapabilitiesResponse(allIndices, Collections.emptyMap()));
|
||||||
} else {
|
} else {
|
||||||
ActionListener<FieldCapabilitiesIndexResponse> innerListener = new ActionListener<FieldCapabilitiesIndexResponse>() {
|
ActionListener<FieldCapabilitiesIndexResponse> innerListener = new ActionListener<FieldCapabilitiesIndexResponse>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,35 +133,61 @@ public class TransportFieldCapabilitiesAction extends HandledTransportAction<Fie
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private FieldCapabilitiesResponse merge(List<FieldCapabilitiesIndexResponse> indexResponses) {
|
private String[] mergeIndiceNames(String[] localIndices, Map<String, OriginalIndices> remoteIndices) {
|
||||||
Map<String, Map<String, FieldCapabilities.Builder>> responseMapBuilder = new HashMap<> ();
|
Set<String> allIndices = new HashSet<>();
|
||||||
|
Arrays.stream(localIndices).forEach(allIndices::add);
|
||||||
|
for (Map.Entry<String, OriginalIndices> entry : remoteIndices.entrySet()) {
|
||||||
|
for (String index : entry.getValue().indices()) {
|
||||||
|
allIndices.add(RemoteClusterAware.buildRemoteIndexName(entry.getKey(), index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allIndices.stream().toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private FieldCapabilitiesResponse merge(String[] indices, List<FieldCapabilitiesIndexResponse> indexResponses,
|
||||||
|
boolean includeUnmapped) {
|
||||||
|
final Map<String, Map<String, FieldCapabilities.Builder>> responseMapBuilder = new HashMap<> ();
|
||||||
for (FieldCapabilitiesIndexResponse response : indexResponses) {
|
for (FieldCapabilitiesIndexResponse response : indexResponses) {
|
||||||
innerMerge(responseMapBuilder, response.getIndexName(), response.get());
|
innerMerge(responseMapBuilder, response.getIndexName(), response.get());
|
||||||
}
|
}
|
||||||
|
final Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>();
|
||||||
Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>();
|
for (Map.Entry<String, Map<String, FieldCapabilities.Builder>> entry : responseMapBuilder.entrySet()) {
|
||||||
for (Map.Entry<String, Map<String, FieldCapabilities.Builder>> entry :
|
final Map<String, FieldCapabilities.Builder> typeMapBuilder = entry.getValue();
|
||||||
responseMapBuilder.entrySet()) {
|
if (includeUnmapped) {
|
||||||
Map<String, FieldCapabilities> typeMap = new HashMap<>();
|
addUnmappedFields(indices, entry.getKey(), typeMapBuilder);
|
||||||
boolean multiTypes = entry.getValue().size() > 1;
|
}
|
||||||
for (Map.Entry<String, FieldCapabilities.Builder> fieldEntry :
|
boolean multiTypes = typeMapBuilder.size() > 1;
|
||||||
entry.getValue().entrySet()) {
|
final Map<String, FieldCapabilities> typeMap = new HashMap<>();
|
||||||
|
for (Map.Entry<String, FieldCapabilities.Builder> fieldEntry : typeMapBuilder.entrySet()) {
|
||||||
typeMap.put(fieldEntry.getKey(), fieldEntry.getValue().build(multiTypes));
|
typeMap.put(fieldEntry.getKey(), fieldEntry.getValue().build(multiTypes));
|
||||||
}
|
}
|
||||||
responseMap.put(entry.getKey(), typeMap);
|
responseMap.put(entry.getKey(), Collections.unmodifiableMap(typeMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FieldCapabilitiesResponse(responseMap);
|
return new FieldCapabilitiesResponse(indices, Collections.unmodifiableMap(responseMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void innerMerge(Map<String, Map<String, FieldCapabilities.Builder>> responseMapBuilder, String indexName,
|
private void addUnmappedFields(String[] indices, String field, Map<String, FieldCapabilities.Builder> typeMap) {
|
||||||
Map<String, FieldCapabilities> map) {
|
Set<String> unmappedIndices = new HashSet<>();
|
||||||
|
Arrays.stream(indices).forEach(unmappedIndices::add);
|
||||||
|
typeMap.values().stream().forEach((b) -> b.getIndices().stream().forEach(unmappedIndices::remove));
|
||||||
|
if (unmappedIndices.isEmpty() == false) {
|
||||||
|
FieldCapabilities.Builder unmapped = new FieldCapabilities.Builder(field, "unmapped");
|
||||||
|
typeMap.put("unmapped", unmapped);
|
||||||
|
for (String index : unmappedIndices) {
|
||||||
|
unmapped.add(index, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void innerMerge(Map<String, Map<String, FieldCapabilities.Builder>> responseMapBuilder,
|
||||||
|
String indexName, Map<String, FieldCapabilities> map) {
|
||||||
for (Map.Entry<String, FieldCapabilities> entry : map.entrySet()) {
|
for (Map.Entry<String, FieldCapabilities> entry : map.entrySet()) {
|
||||||
final String field = entry.getKey();
|
final String field = entry.getKey();
|
||||||
final FieldCapabilities fieldCap = entry.getValue();
|
final FieldCapabilities fieldCap = entry.getValue();
|
||||||
Map<String, FieldCapabilities.Builder> typeMap = responseMapBuilder.computeIfAbsent(field, f -> new HashMap<>());
|
Map<String, FieldCapabilities.Builder> typeMap = responseMapBuilder.computeIfAbsent(field, f -> new HashMap<>());
|
||||||
FieldCapabilities.Builder builder = typeMap.computeIfAbsent(fieldCap.getType(), key -> new FieldCapabilities.Builder(field,
|
FieldCapabilities.Builder builder = typeMap.computeIfAbsent(fieldCap.getType(),
|
||||||
key));
|
key -> new FieldCapabilities.Builder(field, key));
|
||||||
builder.add(indexName, fieldCap.isSearchable(), fieldCap.isAggregatable());
|
builder.add(indexName, fieldCap.isSearchable(), fieldCap.isAggregatable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class RestFieldCapabilitiesAction extends BaseRestHandler {
|
||||||
|
|
||||||
fieldRequest.indicesOptions(
|
fieldRequest.indicesOptions(
|
||||||
IndicesOptions.fromRequest(request, fieldRequest.indicesOptions()));
|
IndicesOptions.fromRequest(request, fieldRequest.indicesOptions()));
|
||||||
|
fieldRequest.includeUnmapped(request.paramAsBoolean("include_unmapped", false));
|
||||||
return channel -> client.fieldCaps(fieldRequest, new RestToXContentListener<>(channel));
|
return channel -> client.fieldCaps(fieldRequest, new RestToXContentListener<>(channel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class FieldCapabilitiesRequestTests extends AbstractStreamableTestCase<Fi
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
request.indicesOptions(randomBoolean() ? IndicesOptions.strictExpand() : IndicesOptions.lenientExpandOpen());
|
request.indicesOptions(randomBoolean() ? IndicesOptions.strictExpand() : IndicesOptions.lenientExpandOpen());
|
||||||
}
|
}
|
||||||
|
request.includeUnmapped(randomBoolean());
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +76,7 @@ public class FieldCapabilitiesRequestTests extends AbstractStreamableTestCase<Fi
|
||||||
request.indicesOptions(indicesOptions);
|
request.indicesOptions(indicesOptions);
|
||||||
});
|
});
|
||||||
mutators.add(request -> request.setMergeResults(!request.isMergeResults()));
|
mutators.add(request -> request.setMergeResults(!request.isMergeResults()));
|
||||||
|
mutators.add(request -> request.includeUnmapped(!request.includeUnmapped()));
|
||||||
|
|
||||||
FieldCapabilitiesRequest mutatedInstance = copyInstance(instance);
|
FieldCapabilitiesRequest mutatedInstance = copyInstance(instance);
|
||||||
Consumer<FieldCapabilitiesRequest> mutator = randomFrom(mutators);
|
Consumer<FieldCapabilitiesRequest> mutator = randomFrom(mutators);
|
||||||
|
|
|
@ -19,32 +19,18 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.fieldcaps;
|
package org.elasticsearch.action.fieldcaps;
|
||||||
|
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.test.AbstractStreamableTestCase;
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentParser;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentType;
|
|
||||||
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiLettersOfLength;
|
import static com.carrotsearch.randomizedtesting.RandomizedTest.randomAsciiLettersOfLength;
|
||||||
|
|
||||||
|
|
||||||
public class FieldCapabilitiesResponseTests extends AbstractStreamableXContentTestCase<FieldCapabilitiesResponse> {
|
public class FieldCapabilitiesResponseTests extends AbstractStreamableTestCase<FieldCapabilitiesResponse> {
|
||||||
|
|
||||||
@Override
|
|
||||||
protected FieldCapabilitiesResponse doParseInstance(XContentParser parser) throws IOException {
|
|
||||||
return FieldCapabilitiesResponse.fromXContent(parser);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FieldCapabilitiesResponse createBlankInstance() {
|
protected FieldCapabilitiesResponse createBlankInstance() {
|
||||||
return new FieldCapabilitiesResponse();
|
return new FieldCapabilitiesResponse();
|
||||||
|
@ -52,36 +38,15 @@ public class FieldCapabilitiesResponseTests extends AbstractStreamableXContentTe
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected FieldCapabilitiesResponse createTestInstance() {
|
protected FieldCapabilitiesResponse createTestInstance() {
|
||||||
if (randomBoolean()) {
|
List<FieldCapabilitiesIndexResponse> responses = new ArrayList<>();
|
||||||
// merged responses
|
int numResponse = randomIntBetween(0, 10);
|
||||||
Map<String, Map<String, FieldCapabilities>> responses = new HashMap<>();
|
|
||||||
|
|
||||||
String[] fields = generateRandomStringArray(5, 10, false, true);
|
for (int i = 0; i < numResponse; i++) {
|
||||||
assertNotNull(fields);
|
responses.add(createRandomIndexResponse());
|
||||||
|
|
||||||
for (String field : fields) {
|
|
||||||
Map<String, FieldCapabilities> typesToCapabilities = new HashMap<>();
|
|
||||||
String[] types = generateRandomStringArray(5, 10, false, false);
|
|
||||||
assertNotNull(types);
|
|
||||||
|
|
||||||
for (String type : types) {
|
|
||||||
typesToCapabilities.put(type, FieldCapabilitiesTests.randomFieldCaps(field));
|
|
||||||
}
|
|
||||||
responses.put(field, typesToCapabilities);
|
|
||||||
}
|
|
||||||
return new FieldCapabilitiesResponse(responses);
|
|
||||||
} else {
|
|
||||||
// non-merged responses
|
|
||||||
List<FieldCapabilitiesIndexResponse> responses = new ArrayList<>();
|
|
||||||
int numResponse = randomIntBetween(0, 10);
|
|
||||||
for (int i = 0; i < numResponse; i++) {
|
|
||||||
responses.add(createRandomIndexResponse());
|
|
||||||
}
|
|
||||||
return new FieldCapabilitiesResponse(responses);
|
|
||||||
}
|
}
|
||||||
|
return new FieldCapabilitiesResponse(responses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private FieldCapabilitiesIndexResponse createRandomIndexResponse() {
|
private FieldCapabilitiesIndexResponse createRandomIndexResponse() {
|
||||||
Map<String, FieldCapabilities> responses = new HashMap<>();
|
Map<String, FieldCapabilities> responses = new HashMap<>();
|
||||||
|
|
||||||
|
@ -118,78 +83,6 @@ public class FieldCapabilitiesResponseTests extends AbstractStreamableXContentTe
|
||||||
FieldCapabilitiesTests.randomFieldCaps(toReplace)));
|
FieldCapabilitiesTests.randomFieldCaps(toReplace)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new FieldCapabilitiesResponse(mutatedResponses);
|
return new FieldCapabilitiesResponse(null, mutatedResponses);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
|
||||||
// Disallow random fields from being inserted under the 'fields' key, as this
|
|
||||||
// map only contains field names, and also under 'fields.FIELD_NAME', as these
|
|
||||||
// maps only contain type names.
|
|
||||||
return field -> field.matches("fields(\\.\\w+)?");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testToXContent() throws IOException {
|
|
||||||
FieldCapabilitiesResponse response = createSimpleResponse();
|
|
||||||
|
|
||||||
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
|
||||||
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
|
||||||
|
|
||||||
String generatedResponse = BytesReference.bytes(builder).utf8ToString();
|
|
||||||
assertEquals((
|
|
||||||
"{" +
|
|
||||||
" \"fields\": {" +
|
|
||||||
" \"rating\": { " +
|
|
||||||
" \"keyword\": {" +
|
|
||||||
" \"type\": \"keyword\"," +
|
|
||||||
" \"searchable\": false," +
|
|
||||||
" \"aggregatable\": true," +
|
|
||||||
" \"indices\": [\"index3\", \"index4\"]," +
|
|
||||||
" \"non_searchable_indices\": [\"index4\"] " +
|
|
||||||
" }," +
|
|
||||||
" \"long\": {" +
|
|
||||||
" \"type\": \"long\"," +
|
|
||||||
" \"searchable\": true," +
|
|
||||||
" \"aggregatable\": false," +
|
|
||||||
" \"indices\": [\"index1\", \"index2\"]," +
|
|
||||||
" \"non_aggregatable_indices\": [\"index1\"] " +
|
|
||||||
" }" +
|
|
||||||
" }," +
|
|
||||||
" \"title\": { " +
|
|
||||||
" \"text\": {" +
|
|
||||||
" \"type\": \"text\"," +
|
|
||||||
" \"searchable\": true," +
|
|
||||||
" \"aggregatable\": false" +
|
|
||||||
" }" +
|
|
||||||
" }" +
|
|
||||||
" }" +
|
|
||||||
"}").replaceAll("\\s+", ""), generatedResponse);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testEmptyResponse() throws IOException {
|
|
||||||
FieldCapabilitiesResponse testInstance = new FieldCapabilitiesResponse();
|
|
||||||
assertSerialization(testInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FieldCapabilitiesResponse createSimpleResponse() {
|
|
||||||
Map<String, FieldCapabilities> titleCapabilities = new HashMap<>();
|
|
||||||
titleCapabilities.put("text", new FieldCapabilities("title", "text", true, false));
|
|
||||||
|
|
||||||
Map<String, FieldCapabilities> ratingCapabilities = new HashMap<>();
|
|
||||||
ratingCapabilities.put("long", new FieldCapabilities("rating", "long",
|
|
||||||
true, false,
|
|
||||||
new String[]{"index1", "index2"},
|
|
||||||
null,
|
|
||||||
new String[]{"index1"}));
|
|
||||||
ratingCapabilities.put("keyword", new FieldCapabilities("rating", "keyword",
|
|
||||||
false, true,
|
|
||||||
new String[]{"index3", "index4"},
|
|
||||||
new String[]{"index4"},
|
|
||||||
null));
|
|
||||||
|
|
||||||
Map<String, Map<String, FieldCapabilities>> responses = new HashMap<>();
|
|
||||||
responses.put("title", titleCapabilities);
|
|
||||||
responses.put("rating", ratingCapabilities);
|
|
||||||
return new FieldCapabilitiesResponse(responses);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,8 +86,8 @@ public class FieldCapabilitiesTests extends AbstractSerializingTestCase<FieldCap
|
||||||
assertThat(cap2.isAggregatable(), equalTo(false));
|
assertThat(cap2.isAggregatable(), equalTo(false));
|
||||||
assertThat(cap2.indices().length, equalTo(3));
|
assertThat(cap2.indices().length, equalTo(3));
|
||||||
assertThat(cap2.indices(), equalTo(new String[]{"index1", "index2", "index3"}));
|
assertThat(cap2.indices(), equalTo(new String[]{"index1", "index2", "index3"}));
|
||||||
assertThat(cap1.nonSearchableIndices(), equalTo(new String[]{"index1", "index3"}));
|
assertThat(cap2.nonSearchableIndices(), equalTo(new String[]{"index1", "index3"}));
|
||||||
assertThat(cap1.nonAggregatableIndices(), equalTo(new String[]{"index2", "index3"}));
|
assertThat(cap2.nonAggregatableIndices(), equalTo(new String[]{"index2", "index3"}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.action.fieldcaps;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentParser;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentType;
|
||||||
|
import org.elasticsearch.test.AbstractStreamableXContentTestCase;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
public class MergedFieldCapabilitiesResponseTests extends AbstractStreamableXContentTestCase<FieldCapabilitiesResponse> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FieldCapabilitiesResponse doParseInstance(XContentParser parser) throws IOException {
|
||||||
|
return FieldCapabilitiesResponse.fromXContent(parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FieldCapabilitiesResponse createBlankInstance() {
|
||||||
|
return new FieldCapabilitiesResponse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FieldCapabilitiesResponse createTestInstance() {
|
||||||
|
// merged responses
|
||||||
|
Map<String, Map<String, FieldCapabilities>> responses = new HashMap<>();
|
||||||
|
|
||||||
|
String[] fields = generateRandomStringArray(5, 10, false, true);
|
||||||
|
assertNotNull(fields);
|
||||||
|
|
||||||
|
for (String field : fields) {
|
||||||
|
Map<String, FieldCapabilities> typesToCapabilities = new HashMap<>();
|
||||||
|
String[] types = generateRandomStringArray(5, 10, false, false);
|
||||||
|
assertNotNull(types);
|
||||||
|
|
||||||
|
for (String type : types) {
|
||||||
|
typesToCapabilities.put(type, FieldCapabilitiesTests.randomFieldCaps(field));
|
||||||
|
}
|
||||||
|
responses.put(field, typesToCapabilities);
|
||||||
|
}
|
||||||
|
int numIndices = randomIntBetween(1, 10);
|
||||||
|
String[] indices = new String[numIndices];
|
||||||
|
for (int i = 0; i < numIndices; i++) {
|
||||||
|
indices[i] = randomAlphaOfLengthBetween(5, 10);
|
||||||
|
}
|
||||||
|
return new FieldCapabilitiesResponse(indices, responses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected FieldCapabilitiesResponse mutateInstance(FieldCapabilitiesResponse response) {
|
||||||
|
Map<String, Map<String, FieldCapabilities>> mutatedResponses = new HashMap<>(response.get());
|
||||||
|
|
||||||
|
int mutation = response.get().isEmpty() ? 0 : randomIntBetween(0, 2);
|
||||||
|
|
||||||
|
switch (mutation) {
|
||||||
|
case 0:
|
||||||
|
String toAdd = randomAlphaOfLength(10);
|
||||||
|
mutatedResponses.put(toAdd, Collections.singletonMap(
|
||||||
|
randomAlphaOfLength(10),
|
||||||
|
FieldCapabilitiesTests.randomFieldCaps(toAdd)));
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
String toRemove = randomFrom(mutatedResponses.keySet());
|
||||||
|
mutatedResponses.remove(toRemove);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
String toReplace = randomFrom(mutatedResponses.keySet());
|
||||||
|
mutatedResponses.put(toReplace, Collections.singletonMap(
|
||||||
|
randomAlphaOfLength(10),
|
||||||
|
FieldCapabilitiesTests.randomFieldCaps(toReplace)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new FieldCapabilitiesResponse(null, mutatedResponses);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Predicate<String> getRandomFieldsExcludeFilter() {
|
||||||
|
// Disallow random fields from being inserted under the 'fields' key, as this
|
||||||
|
// map only contains field names, and also under 'fields.FIELD_NAME', as these
|
||||||
|
// maps only contain type names.
|
||||||
|
return field -> field.matches("fields(\\.\\w+)?");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testToXContent() throws IOException {
|
||||||
|
FieldCapabilitiesResponse response = createSimpleResponse();
|
||||||
|
|
||||||
|
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
|
||||||
|
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
|
||||||
|
|
||||||
|
String generatedResponse = BytesReference.bytes(builder).utf8ToString();
|
||||||
|
assertEquals((
|
||||||
|
"{" +
|
||||||
|
" \"indices\": null," +
|
||||||
|
" \"fields\": {" +
|
||||||
|
" \"rating\": { " +
|
||||||
|
" \"keyword\": {" +
|
||||||
|
" \"type\": \"keyword\"," +
|
||||||
|
" \"searchable\": false," +
|
||||||
|
" \"aggregatable\": true," +
|
||||||
|
" \"indices\": [\"index3\", \"index4\"]," +
|
||||||
|
" \"non_searchable_indices\": [\"index4\"] " +
|
||||||
|
" }," +
|
||||||
|
" \"long\": {" +
|
||||||
|
" \"type\": \"long\"," +
|
||||||
|
" \"searchable\": true," +
|
||||||
|
" \"aggregatable\": false," +
|
||||||
|
" \"indices\": [\"index1\", \"index2\"]," +
|
||||||
|
" \"non_aggregatable_indices\": [\"index1\"] " +
|
||||||
|
" }" +
|
||||||
|
" }," +
|
||||||
|
" \"title\": { " +
|
||||||
|
" \"text\": {" +
|
||||||
|
" \"type\": \"text\"," +
|
||||||
|
" \"searchable\": true," +
|
||||||
|
" \"aggregatable\": false" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
" }" +
|
||||||
|
"}").replaceAll("\\s+", ""), generatedResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testEmptyResponse() throws IOException {
|
||||||
|
FieldCapabilitiesResponse testInstance = new FieldCapabilitiesResponse();
|
||||||
|
assertSerialization(testInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FieldCapabilitiesResponse createSimpleResponse() {
|
||||||
|
Map<String, FieldCapabilities> titleCapabilities = new HashMap<>();
|
||||||
|
titleCapabilities.put("text", new FieldCapabilities("title", "text", true, false));
|
||||||
|
|
||||||
|
Map<String, FieldCapabilities> ratingCapabilities = new HashMap<>();
|
||||||
|
ratingCapabilities.put("long", new FieldCapabilities("rating", "long",
|
||||||
|
true, false,
|
||||||
|
new String[]{"index1", "index2"},
|
||||||
|
null,
|
||||||
|
new String[]{"index1"}));
|
||||||
|
ratingCapabilities.put("keyword", new FieldCapabilities("rating", "keyword",
|
||||||
|
false, true,
|
||||||
|
new String[]{"index3", "index4"},
|
||||||
|
new String[]{"index4"},
|
||||||
|
null));
|
||||||
|
|
||||||
|
Map<String, Map<String, FieldCapabilities>> responses = new HashMap<>();
|
||||||
|
responses.put("title", titleCapabilities);
|
||||||
|
responses.put("rating", ratingCapabilities);
|
||||||
|
return new FieldCapabilitiesResponse(null, responses);
|
||||||
|
}
|
||||||
|
}
|
|
@ -113,7 +113,7 @@ public class FieldFilterMapperPluginTests extends ESSingleNodeTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, Collection<String> expectedFields) {
|
private static void assertFieldCaps(FieldCapabilitiesResponse fieldCapabilitiesResponse, Collection<String> expectedFields) {
|
||||||
Map<String, Map<String, FieldCapabilities>> responseMap = fieldCapabilitiesResponse.get();
|
Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>(fieldCapabilitiesResponse.get());
|
||||||
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
|
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
|
||||||
for (String field : builtInMetaDataFields) {
|
for (String field : builtInMetaDataFields) {
|
||||||
Map<String, FieldCapabilities> remove = responseMap.remove(field);
|
Map<String, FieldCapabilities> remove = responseMap.remove(field);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import org.elasticsearch.plugins.Plugin;
|
||||||
import org.elasticsearch.test.ESIntegTestCase;
|
import org.elasticsearch.test.ESIntegTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -60,6 +61,13 @@ public class FieldCapabilitiesIT extends ESIntegTestCase {
|
||||||
.field("type", "alias")
|
.field("type", "alias")
|
||||||
.field("path", "playlist")
|
.field("path", "playlist")
|
||||||
.endObject()
|
.endObject()
|
||||||
|
.startObject("old_field")
|
||||||
|
.field("type", "long")
|
||||||
|
.endObject()
|
||||||
|
.startObject("new_field")
|
||||||
|
.field("type", "alias")
|
||||||
|
.field("path", "old_field")
|
||||||
|
.endObject()
|
||||||
.endObject()
|
.endObject()
|
||||||
.endObject()
|
.endObject()
|
||||||
.endObject();
|
.endObject();
|
||||||
|
@ -75,10 +83,14 @@ public class FieldCapabilitiesIT extends ESIntegTestCase {
|
||||||
.startObject("route_length_miles")
|
.startObject("route_length_miles")
|
||||||
.field("type", "double")
|
.field("type", "double")
|
||||||
.endObject()
|
.endObject()
|
||||||
|
.startObject("new_field")
|
||||||
|
.field("type", "long")
|
||||||
|
.endObject()
|
||||||
.endObject()
|
.endObject()
|
||||||
.endObject()
|
.endObject()
|
||||||
.endObject();
|
.endObject();
|
||||||
assertAcked(prepareCreate("new_index").addMapping("_doc", newIndexMapping));
|
assertAcked(prepareCreate("new_index").addMapping("_doc", newIndexMapping));
|
||||||
|
assertAcked(client().admin().indices().prepareAliases().addAlias("new_index", "current"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FieldFilterPlugin extends Plugin implements MapperPlugin {
|
public static class FieldFilterPlugin extends Plugin implements MapperPlugin {
|
||||||
|
@ -94,9 +106,9 @@ public class FieldCapabilitiesIT extends ESIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAlias() {
|
public void testFieldAlias() {
|
||||||
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("distance", "route_length_miles")
|
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("distance", "route_length_miles").get();
|
||||||
.get();
|
|
||||||
|
|
||||||
|
assertIndices(response, "old_index", "new_index");
|
||||||
// Ensure the response has entries for both requested fields.
|
// Ensure the response has entries for both requested fields.
|
||||||
assertTrue(response.get().containsKey("distance"));
|
assertTrue(response.get().containsKey("distance"));
|
||||||
assertTrue(response.get().containsKey("route_length_miles"));
|
assertTrue(response.get().containsKey("route_length_miles"));
|
||||||
|
@ -126,26 +138,73 @@ public class FieldCapabilitiesIT extends ESIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAliasWithWildcard() {
|
public void testFieldAliasWithWildcard() {
|
||||||
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("route*")
|
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("route*").get();
|
||||||
.get();
|
|
||||||
|
|
||||||
|
assertIndices(response, "old_index", "new_index");
|
||||||
assertEquals(1, response.get().size());
|
assertEquals(1, response.get().size());
|
||||||
assertTrue(response.get().containsKey("route_length_miles"));
|
assertTrue(response.get().containsKey("route_length_miles"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAliasFiltering() {
|
public void testFieldAliasFiltering() {
|
||||||
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields(
|
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("secret-soundtrack", "route_length_miles").get();
|
||||||
"secret-soundtrack", "route_length_miles")
|
assertIndices(response, "old_index", "new_index");
|
||||||
.get();
|
|
||||||
assertEquals(1, response.get().size());
|
assertEquals(1, response.get().size());
|
||||||
assertTrue(response.get().containsKey("route_length_miles"));
|
assertTrue(response.get().containsKey("route_length_miles"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testFieldAliasFilteringWithWildcard() {
|
public void testFieldAliasFilteringWithWildcard() {
|
||||||
FieldCapabilitiesResponse response = client().prepareFieldCaps()
|
FieldCapabilitiesResponse response = client().prepareFieldCaps().setFields("distance", "secret*").get();
|
||||||
.setFields("distance", "secret*")
|
assertIndices(response, "old_index", "new_index");
|
||||||
.get();
|
|
||||||
assertEquals(1, response.get().size());
|
assertEquals(1, response.get().size());
|
||||||
assertTrue(response.get().containsKey("distance"));
|
assertTrue(response.get().containsKey("distance"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testWithUnmapped() {
|
||||||
|
FieldCapabilitiesResponse response = client().prepareFieldCaps()
|
||||||
|
.setFields("new_field", "old_field")
|
||||||
|
.setIncludeUnmapped(true)
|
||||||
|
.get();
|
||||||
|
assertIndices(response, "old_index", "new_index");
|
||||||
|
|
||||||
|
assertEquals(2, response.get().size());
|
||||||
|
assertTrue(response.get().containsKey("old_field"));
|
||||||
|
|
||||||
|
Map<String, FieldCapabilities> oldField = response.getField("old_field");
|
||||||
|
assertEquals(2, oldField.size());
|
||||||
|
|
||||||
|
assertTrue(oldField.containsKey("long"));
|
||||||
|
assertEquals(
|
||||||
|
new FieldCapabilities("old_field", "long", true, true, new String[] {"old_index"}, null, null),
|
||||||
|
oldField.get("long"));
|
||||||
|
|
||||||
|
assertTrue(oldField.containsKey("unmapped"));
|
||||||
|
assertEquals(
|
||||||
|
new FieldCapabilities("old_field", "unmapped", false, false, new String[] {"new_index"}, null, null),
|
||||||
|
oldField.get("unmapped"));
|
||||||
|
|
||||||
|
Map<String, FieldCapabilities> newField = response.getField("new_field");
|
||||||
|
assertEquals(1, newField.size());
|
||||||
|
|
||||||
|
assertTrue(newField.containsKey("long"));
|
||||||
|
assertEquals(
|
||||||
|
new FieldCapabilities("new_field", "long", true, true),
|
||||||
|
newField.get("long"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWithIndexAlias() {
|
||||||
|
FieldCapabilitiesResponse response = client().prepareFieldCaps("current").setFields("*").get();
|
||||||
|
assertIndices(response, "new_index");
|
||||||
|
|
||||||
|
FieldCapabilitiesResponse response1 = client().prepareFieldCaps("current", "old_index").setFields("*").get();
|
||||||
|
assertIndices(response1, "old_index", "new_index");
|
||||||
|
FieldCapabilitiesResponse response2 = client().prepareFieldCaps("current", "old_index", "new_index").setFields("*").get();
|
||||||
|
assertEquals(response1, response2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertIndices(FieldCapabilitiesResponse response, String... indices) {
|
||||||
|
assertNotNull(response.getIndices());
|
||||||
|
Arrays.sort(indices);
|
||||||
|
Arrays.sort(response.getIndices());
|
||||||
|
assertArrayEquals(indices, response.getIndices());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@ public class DocumentAndFieldLevelSecurityTests extends SecurityIntegTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertExpectedFields(FieldCapabilitiesResponse fieldCapabilitiesResponse, String... expectedFields) {
|
private static void assertExpectedFields(FieldCapabilitiesResponse fieldCapabilitiesResponse, String... expectedFields) {
|
||||||
Map<String, Map<String, FieldCapabilities>> responseMap = fieldCapabilitiesResponse.get();
|
Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>(fieldCapabilitiesResponse.get());
|
||||||
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
|
Set<String> builtInMetaDataFields = IndicesModule.getBuiltInMetaDataFields();
|
||||||
for (String field : builtInMetaDataFields) {
|
for (String field : builtInMetaDataFields) {
|
||||||
Map<String, FieldCapabilities> remove = responseMap.remove(field);
|
Map<String, FieldCapabilities> remove = responseMap.remove(field);
|
||||||
|
|
Loading…
Reference in New Issue