Add FieldCapabilities (_field_caps) API (#23007)
This change introduces a new API called `_field_caps` that allows to retrieve the capabilities of specific fields. Example: ```` GET t,s,v,w/_field_caps?fields=field1,field2 ```` ... returns: ```` { "fields": { "field1": { "string": { "searchable": true, "aggregatable": true } }, "field2": { "keyword": { "searchable": false, "aggregatable": true, "non_searchable_indices": ["t"] "indices": ["t", "s"] }, "long": { "searchable": true, "aggregatable": false, "non_aggregatable_indices": ["v"] "indices": ["v", "w"] } } } } ```` In this example `field1` have the same type `text` across the requested indices `t`, `s`, `v`, `w`. Conversely `field2` is defined with two conflicting types `keyword` and `long`. Note that `_field_caps` does not treat this case as an error but rather return the list of unique types seen for this field.
This commit is contained in:
parent
5eba90f37c
commit
a8250b26e7
|
@ -149,6 +149,9 @@ import org.elasticsearch.action.delete.DeleteAction;
|
|||
import org.elasticsearch.action.delete.TransportDeleteAction;
|
||||
import org.elasticsearch.action.explain.ExplainAction;
|
||||
import org.elasticsearch.action.explain.TransportExplainAction;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesAction;
|
||||
import org.elasticsearch.action.fieldcaps.TransportFieldCapabilitiesAction;
|
||||
import org.elasticsearch.action.fieldcaps.TransportFieldCapabilitiesIndexAction;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsAction;
|
||||
import org.elasticsearch.action.fieldstats.TransportFieldStatsAction;
|
||||
import org.elasticsearch.action.get.GetAction;
|
||||
|
@ -205,6 +208,7 @@ import org.elasticsearch.plugins.ActionPlugin;
|
|||
import org.elasticsearch.plugins.ActionPlugin.ActionHandler;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestHandler;
|
||||
import org.elasticsearch.rest.action.RestFieldCapabilitiesAction;
|
||||
import org.elasticsearch.rest.action.RestFieldStatsAction;
|
||||
import org.elasticsearch.rest.action.RestMainAction;
|
||||
import org.elasticsearch.rest.action.admin.cluster.RestCancelTasksAction;
|
||||
|
@ -479,6 +483,8 @@ public class ActionModule extends AbstractModule {
|
|||
actions.register(DeleteStoredScriptAction.INSTANCE, TransportDeleteStoredScriptAction.class);
|
||||
|
||||
actions.register(FieldStatsAction.INSTANCE, TransportFieldStatsAction.class);
|
||||
actions.register(FieldCapabilitiesAction.INSTANCE, TransportFieldCapabilitiesAction.class,
|
||||
TransportFieldCapabilitiesIndexAction.class);
|
||||
|
||||
actions.register(PutPipelineAction.INSTANCE, PutPipelineTransportAction.class);
|
||||
actions.register(GetPipelineAction.INSTANCE, GetPipelineTransportAction.class);
|
||||
|
@ -587,6 +593,7 @@ public class ActionModule extends AbstractModule {
|
|||
registerHandler.accept(new RestDeleteStoredScriptAction(settings, restController));
|
||||
|
||||
registerHandler.accept(new RestFieldStatsAction(settings, restController));
|
||||
registerHandler.accept(new RestFieldCapabilitiesAction(settings, restController));
|
||||
|
||||
// Tasks API
|
||||
registerHandler.accept(new RestListTasksAction(settings, restController, nodesInCluster));
|
||||
|
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* 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.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Describes the capabilities of a field optionally merged across multiple indices.
|
||||
*/
|
||||
public class FieldCapabilities implements Writeable, ToXContent {
|
||||
private final String name;
|
||||
private final String type;
|
||||
private final boolean isSearchable;
|
||||
private final boolean isAggregatable;
|
||||
|
||||
private final String[] indices;
|
||||
private final String[] nonSearchableIndices;
|
||||
private final String[] nonAggregatableIndices;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param name The name of the field.
|
||||
* @param type The type associated with the field.
|
||||
* @param isSearchable Whether this field is indexed for search.
|
||||
* @param isAggregatable Whether this field can be aggregated on.
|
||||
*/
|
||||
FieldCapabilities(String name, String type, boolean isSearchable, boolean isAggregatable) {
|
||||
this(name, type, isSearchable, isAggregatable, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param name The name of the field
|
||||
* @param type The type associated with the field.
|
||||
* @param isSearchable Whether this field is indexed for search.
|
||||
* @param isAggregatable Whether this field can be aggregated on.
|
||||
* @param indices The list of indices where this field name is defined as {@code type},
|
||||
* or null if all indices have the same {@code type} for the field.
|
||||
* @param nonSearchableIndices The list of indices where this field is not searchable,
|
||||
* or null if the field is searchable in all indices.
|
||||
* @param nonAggregatableIndices The list of indices where this field is not aggregatable,
|
||||
* or null if the field is aggregatable in all indices.
|
||||
*/
|
||||
FieldCapabilities(String name, String type,
|
||||
boolean isSearchable, boolean isAggregatable,
|
||||
String[] indices,
|
||||
String[] nonSearchableIndices,
|
||||
String[] nonAggregatableIndices) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.isSearchable = isSearchable;
|
||||
this.isAggregatable = isAggregatable;
|
||||
this.indices = indices;
|
||||
this.nonSearchableIndices = nonSearchableIndices;
|
||||
this.nonAggregatableIndices = nonAggregatableIndices;
|
||||
}
|
||||
|
||||
FieldCapabilities(StreamInput in) throws IOException {
|
||||
this.name = in.readString();
|
||||
this.type = in.readString();
|
||||
this.isSearchable = in.readBoolean();
|
||||
this.isAggregatable = in.readBoolean();
|
||||
this.indices = in.readOptionalStringArray();
|
||||
this.nonSearchableIndices = in.readOptionalStringArray();
|
||||
this.nonAggregatableIndices = in.readOptionalStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
out.writeString(name);
|
||||
out.writeString(type);
|
||||
out.writeBoolean(isSearchable);
|
||||
out.writeBoolean(isAggregatable);
|
||||
out.writeOptionalStringArray(indices);
|
||||
out.writeOptionalStringArray(nonSearchableIndices);
|
||||
out.writeOptionalStringArray(nonAggregatableIndices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.startObject();
|
||||
builder.field("type", type);
|
||||
builder.field("searchable", isSearchable);
|
||||
builder.field("aggregatable", isAggregatable);
|
||||
if (indices != null) {
|
||||
builder.field("indices", indices);
|
||||
}
|
||||
if (nonSearchableIndices != null) {
|
||||
builder.field("non_searchable_indices", nonSearchableIndices);
|
||||
}
|
||||
if (nonAggregatableIndices != null) {
|
||||
builder.field("non_aggregatable_indices", nonAggregatableIndices);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
/**
|
||||
* The name of the field.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this field is indexed for search on all indices.
|
||||
*/
|
||||
public boolean isAggregatable() {
|
||||
return isAggregatable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this field can be aggregated on all indices.
|
||||
*/
|
||||
public boolean isSearchable() {
|
||||
return isSearchable;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of the field.
|
||||
*/
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of indices where this field name is defined as {@code type},
|
||||
* or null if all indices have the same {@code type} for the field.
|
||||
*/
|
||||
public String[] indices() {
|
||||
return indices;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of indices where this field is not searchable,
|
||||
* or null if the field is searchable in all indices.
|
||||
*/
|
||||
public String[] nonSearchableIndices() {
|
||||
return nonSearchableIndices;
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of indices where this field is not aggregatable,
|
||||
* or null if the field is aggregatable in all indices.
|
||||
*/
|
||||
public String[] nonAggregatableIndices() {
|
||||
return nonAggregatableIndices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FieldCapabilities that = (FieldCapabilities) o;
|
||||
|
||||
if (isSearchable != that.isSearchable) return false;
|
||||
if (isAggregatable != that.isAggregatable) return false;
|
||||
if (!name.equals(that.name)) return false;
|
||||
if (!type.equals(that.type)) return false;
|
||||
if (!Arrays.equals(indices, that.indices)) return false;
|
||||
if (!Arrays.equals(nonSearchableIndices, that.nonSearchableIndices)) return false;
|
||||
return Arrays.equals(nonAggregatableIndices, that.nonAggregatableIndices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = name.hashCode();
|
||||
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(nonSearchableIndices);
|
||||
result = 31 * result + Arrays.hashCode(nonAggregatableIndices);
|
||||
return result;
|
||||
}
|
||||
|
||||
static class Builder {
|
||||
private String name;
|
||||
private String type;
|
||||
private boolean isSearchable;
|
||||
private boolean isAggregatable;
|
||||
private List<IndexCaps> indiceList;
|
||||
|
||||
Builder(String name, String type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
this.isSearchable = true;
|
||||
this.isAggregatable = true;
|
||||
this.indiceList = new ArrayList<>();
|
||||
}
|
||||
|
||||
void add(String index, boolean search, boolean agg) {
|
||||
IndexCaps indexCaps = new IndexCaps(index, search, agg);
|
||||
indiceList.add(indexCaps);
|
||||
this.isSearchable &= search;
|
||||
this.isAggregatable &= agg;
|
||||
}
|
||||
|
||||
FieldCapabilities build(boolean withIndices) {
|
||||
final String[] indices;
|
||||
Collections.sort(indiceList, Comparator.comparing(o -> o.name));
|
||||
if (withIndices) {
|
||||
indices = indiceList.stream()
|
||||
.map(caps -> caps.name)
|
||||
.toArray(String[]::new);
|
||||
} else {
|
||||
indices = null;
|
||||
}
|
||||
|
||||
final String[] nonSearchableIndices;
|
||||
if (isSearchable == false &&
|
||||
indiceList.stream().anyMatch((caps) -> caps.isSearchable)) {
|
||||
// Iff this field is searchable in some indices AND non-searchable in others
|
||||
// we record the list of non-searchable indices
|
||||
nonSearchableIndices = indiceList.stream()
|
||||
.filter((caps) -> caps.isSearchable == false)
|
||||
.map(caps -> caps.name)
|
||||
.toArray(String[]::new);
|
||||
} else {
|
||||
nonSearchableIndices = null;
|
||||
}
|
||||
|
||||
final String[] nonAggregatableIndices;
|
||||
if (isAggregatable == false &&
|
||||
indiceList.stream().anyMatch((caps) -> caps.isAggregatable)) {
|
||||
// Iff this field is aggregatable in some indices AND non-searchable in others
|
||||
// we keep the list of non-aggregatable indices
|
||||
nonAggregatableIndices = indiceList.stream()
|
||||
.filter((caps) -> caps.isAggregatable == false)
|
||||
.map(caps -> caps.name)
|
||||
.toArray(String[]::new);
|
||||
} else {
|
||||
nonAggregatableIndices = null;
|
||||
}
|
||||
return new FieldCapabilities(name, type, isSearchable, isAggregatable,
|
||||
indices, nonSearchableIndices, nonAggregatableIndices);
|
||||
}
|
||||
}
|
||||
|
||||
private static class IndexCaps {
|
||||
final String name;
|
||||
final boolean isSearchable;
|
||||
final boolean isAggregatable;
|
||||
|
||||
IndexCaps(String name, boolean isSearchable, boolean isAggregatable) {
|
||||
this.name = name;
|
||||
this.isSearchable = isSearchable;
|
||||
this.isAggregatable = isAggregatable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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.action.Action;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class FieldCapabilitiesAction extends Action<FieldCapabilitiesRequest,
|
||||
FieldCapabilitiesResponse, FieldCapabilitiesRequestBuilder> {
|
||||
|
||||
public static final FieldCapabilitiesAction INSTANCE = new FieldCapabilitiesAction();
|
||||
public static final String NAME = "indices:data/read/field_caps";
|
||||
|
||||
private FieldCapabilitiesAction() {
|
||||
super(NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldCapabilitiesResponse newResponse() {
|
||||
return new FieldCapabilitiesResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldCapabilitiesRequestBuilder newRequestBuilder(ElasticsearchClient client) {
|
||||
return new FieldCapabilitiesRequestBuilder(client, this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* 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.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.support.single.shard.SingleShardRequest;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FieldCapabilitiesIndexRequest
|
||||
extends SingleShardRequest<FieldCapabilitiesIndexRequest> {
|
||||
|
||||
private String[] fields;
|
||||
|
||||
// For serialization
|
||||
FieldCapabilitiesIndexRequest() {}
|
||||
|
||||
FieldCapabilitiesIndexRequest(String[] fields, String index) {
|
||||
super(index);
|
||||
if (fields == null || fields.length == 0) {
|
||||
throw new IllegalArgumentException("specified fields can't be null or empty");
|
||||
}
|
||||
this.fields = fields;
|
||||
}
|
||||
|
||||
public String[] fields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
fields = in.readStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeStringArray(fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* 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.action.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Response for {@link FieldCapabilitiesIndexRequest} requests.
|
||||
*/
|
||||
public class FieldCapabilitiesIndexResponse extends ActionResponse {
|
||||
private String indexName;
|
||||
private Map<String, FieldCapabilities> responseMap;
|
||||
|
||||
FieldCapabilitiesIndexResponse(String indexName, Map<String, FieldCapabilities> responseMap) {
|
||||
this.indexName = indexName;
|
||||
this.responseMap = responseMap;
|
||||
}
|
||||
|
||||
FieldCapabilitiesIndexResponse() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the index name
|
||||
*/
|
||||
public String getIndexName() {
|
||||
return indexName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field capabilities map
|
||||
*/
|
||||
public Map<String, FieldCapabilities> get() {
|
||||
return responseMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get the field capabilities for the provided {@code field}
|
||||
*/
|
||||
public FieldCapabilities getField(String field) {
|
||||
return responseMap.get(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
this.indexName = in.readString();
|
||||
this.responseMap =
|
||||
in.readMap(StreamInput::readString, FieldCapabilities::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeString(indexName);
|
||||
out.writeMap(responseMap,
|
||||
StreamOutput::writeString, (valueOut, fc) -> fc.writeTo(valueOut));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FieldCapabilitiesIndexResponse that = (FieldCapabilitiesIndexResponse) o;
|
||||
|
||||
return responseMap.equals(that.responseMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return responseMap.hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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.action.ActionRequest;
|
||||
import org.elasticsearch.action.ActionRequestValidationException;
|
||||
import org.elasticsearch.action.IndicesRequest;
|
||||
import org.elasticsearch.action.ValidateActions;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.common.ParseField;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ObjectParser;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.elasticsearch.common.xcontent.ObjectParser.fromList;
|
||||
|
||||
public class FieldCapabilitiesRequest extends ActionRequest implements IndicesRequest {
|
||||
public static final ParseField FIELDS_FIELD = new ParseField("fields");
|
||||
public static final String NAME = "field_caps_request";
|
||||
private String[] indices = Strings.EMPTY_ARRAY;
|
||||
private IndicesOptions indicesOptions = IndicesOptions.strictExpandOpen();
|
||||
private String[] fields = Strings.EMPTY_ARRAY;
|
||||
|
||||
private static ObjectParser<FieldCapabilitiesRequest, Void> PARSER =
|
||||
new ObjectParser<>(NAME, FieldCapabilitiesRequest::new);
|
||||
|
||||
static {
|
||||
PARSER.declareStringArray(fromList(String.class, FieldCapabilitiesRequest::fields),
|
||||
FIELDS_FIELD);
|
||||
}
|
||||
|
||||
public FieldCapabilitiesRequest() {}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
fields = in.readStringArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeStringArray(fields);
|
||||
}
|
||||
|
||||
public static FieldCapabilitiesRequest parseFields(XContentParser parser) throws IOException {
|
||||
return PARSER.parse(parser, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of field names to retrieve
|
||||
*/
|
||||
public FieldCapabilitiesRequest fields(String... fields) {
|
||||
if (fields == null || fields.length == 0) {
|
||||
throw new IllegalArgumentException("specified fields can't be null or empty");
|
||||
}
|
||||
Set<String> fieldSet = new HashSet<>(Arrays.asList(fields));
|
||||
this.fields = fieldSet.toArray(new String[0]);
|
||||
return this;
|
||||
}
|
||||
|
||||
public String[] fields() {
|
||||
return fields;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* The list of indices to lookup
|
||||
*/
|
||||
public FieldCapabilitiesRequest indices(String[] indices) {
|
||||
this.indices = indices;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FieldCapabilitiesRequest indicesOptions(IndicesOptions indicesOptions) {
|
||||
this.indicesOptions = indicesOptions;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] indices() {
|
||||
return indices;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndicesOptions indicesOptions() {
|
||||
return indicesOptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionRequestValidationException validate() {
|
||||
ActionRequestValidationException validationException = null;
|
||||
if (fields == null || fields.length == 0) {
|
||||
validationException =
|
||||
ValidateActions.addValidationError("no fields specified", validationException);
|
||||
}
|
||||
return validationException;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FieldCapabilitiesRequest that = (FieldCapabilitiesRequest) o;
|
||||
|
||||
if (!Arrays.equals(indices, that.indices)) return false;
|
||||
if (!indicesOptions.equals(that.indicesOptions)) return false;
|
||||
return Arrays.equals(fields, that.fields);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = Arrays.hashCode(indices);
|
||||
result = 31 * result + indicesOptions.hashCode();
|
||||
result = 31 * result + Arrays.hashCode(fields);
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.action.ActionRequestBuilder;
|
||||
import org.elasticsearch.client.ElasticsearchClient;
|
||||
|
||||
public class FieldCapabilitiesRequestBuilder extends
|
||||
ActionRequestBuilder<FieldCapabilitiesRequest, FieldCapabilitiesResponse,
|
||||
FieldCapabilitiesRequestBuilder> {
|
||||
public FieldCapabilitiesRequestBuilder(ElasticsearchClient client,
|
||||
FieldCapabilitiesAction action,
|
||||
String... indices) {
|
||||
super(client, action, new FieldCapabilitiesRequest().indices(indices));
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of field names to retrieve.
|
||||
*/
|
||||
public FieldCapabilitiesRequestBuilder setFields(String... fields) {
|
||||
request().fields(fields);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* 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.action.ActionResponse;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Response for {@link FieldCapabilitiesRequest} requests.
|
||||
*/
|
||||
public class FieldCapabilitiesResponse extends ActionResponse implements ToXContent {
|
||||
private Map<String, Map<String, FieldCapabilities>> responseMap;
|
||||
|
||||
FieldCapabilitiesResponse(Map<String, Map<String, FieldCapabilities>> responseMap) {
|
||||
this.responseMap = responseMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for serialization
|
||||
*/
|
||||
FieldCapabilitiesResponse() {
|
||||
this.responseMap = Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the field capabilities map.
|
||||
*/
|
||||
public Map<String, Map<String, FieldCapabilities>> get() {
|
||||
return responseMap;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Get the field capabilities per type for the provided {@code field}.
|
||||
*/
|
||||
public Map<String, FieldCapabilities> getField(String field) {
|
||||
return responseMap.get(field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readFrom(StreamInput in) throws IOException {
|
||||
super.readFrom(in);
|
||||
this.responseMap =
|
||||
in.readMap(StreamInput::readString, FieldCapabilitiesResponse::readField);
|
||||
}
|
||||
|
||||
private static Map<String, FieldCapabilities> readField(StreamInput in) throws IOException {
|
||||
return in.readMap(StreamInput::readString, FieldCapabilities::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(StreamOutput out) throws IOException {
|
||||
super.writeTo(out);
|
||||
out.writeMap(responseMap, StreamOutput::writeString, FieldCapabilitiesResponse::writeField);
|
||||
}
|
||||
|
||||
private static void writeField(StreamOutput out,
|
||||
Map<String, FieldCapabilities> map) throws IOException {
|
||||
out.writeMap(map, StreamOutput::writeString, (valueOut, fc) -> fc.writeTo(valueOut));
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||
builder.field("fields", responseMap);
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
FieldCapabilitiesResponse that = (FieldCapabilitiesResponse) o;
|
||||
|
||||
return responseMap.equals(that.responseMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return responseMap.hashCode();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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.action.ActionListener;
|
||||
import org.elasticsearch.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.HandledTransportAction;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
|
||||
public class TransportFieldCapabilitiesAction
|
||||
extends HandledTransportAction<FieldCapabilitiesRequest, FieldCapabilitiesResponse> {
|
||||
private final ClusterService clusterService;
|
||||
private final TransportFieldCapabilitiesIndexAction shardAction;
|
||||
|
||||
@Inject
|
||||
public TransportFieldCapabilitiesAction(Settings settings, TransportService transportService,
|
||||
ClusterService clusterService, ThreadPool threadPool,
|
||||
TransportFieldCapabilitiesIndexAction shardAction,
|
||||
ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver
|
||||
indexNameExpressionResolver) {
|
||||
super(settings, FieldCapabilitiesAction.NAME, threadPool, transportService,
|
||||
actionFilters, indexNameExpressionResolver, FieldCapabilitiesRequest::new);
|
||||
this.clusterService = clusterService;
|
||||
this.shardAction = shardAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doExecute(FieldCapabilitiesRequest request,
|
||||
final ActionListener<FieldCapabilitiesResponse> listener) {
|
||||
ClusterState clusterState = clusterService.state();
|
||||
String[] concreteIndices =
|
||||
indexNameExpressionResolver.concreteIndexNames(clusterState, request);
|
||||
final AtomicInteger indexCounter = new AtomicInteger();
|
||||
final AtomicInteger completionCounter = new AtomicInteger(concreteIndices.length);
|
||||
final AtomicReferenceArray<Object> indexResponses =
|
||||
new AtomicReferenceArray<>(concreteIndices.length);
|
||||
if (concreteIndices.length == 0) {
|
||||
listener.onResponse(new FieldCapabilitiesResponse());
|
||||
} else {
|
||||
for (String index : concreteIndices) {
|
||||
FieldCapabilitiesIndexRequest indexRequest =
|
||||
new FieldCapabilitiesIndexRequest(request.fields(), index);
|
||||
shardAction.execute(indexRequest,
|
||||
new ActionListener<FieldCapabilitiesIndexResponse> () {
|
||||
@Override
|
||||
public void onResponse(FieldCapabilitiesIndexResponse result) {
|
||||
indexResponses.set(indexCounter.getAndIncrement(), result);
|
||||
if (completionCounter.decrementAndGet() == 0) {
|
||||
listener.onResponse(merge(indexResponses));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Exception e) {
|
||||
indexResponses.set(indexCounter.getAndIncrement(), e);
|
||||
if (completionCounter.decrementAndGet() == 0) {
|
||||
listener.onResponse(merge(indexResponses));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FieldCapabilitiesResponse merge(AtomicReferenceArray<Object> indexResponses) {
|
||||
Map<String, Map<String, FieldCapabilities.Builder>> responseMapBuilder = new HashMap<> ();
|
||||
for (int i = 0; i < indexResponses.length(); i++) {
|
||||
Object element = indexResponses.get(i);
|
||||
if (element instanceof FieldCapabilitiesIndexResponse == false) {
|
||||
assert element instanceof Exception;
|
||||
continue;
|
||||
}
|
||||
FieldCapabilitiesIndexResponse response = (FieldCapabilitiesIndexResponse) element;
|
||||
for (String field : response.get().keySet()) {
|
||||
Map<String, FieldCapabilities.Builder> typeMap = responseMapBuilder.get(field);
|
||||
if (typeMap == null) {
|
||||
typeMap = new HashMap<> ();
|
||||
responseMapBuilder.put(field, typeMap);
|
||||
}
|
||||
FieldCapabilities fieldCap = response.getField(field);
|
||||
FieldCapabilities.Builder builder = typeMap.get(fieldCap.getType());
|
||||
if (builder == null) {
|
||||
builder = new FieldCapabilities.Builder(field, fieldCap.getType());
|
||||
typeMap.put(fieldCap.getType(), builder);
|
||||
}
|
||||
builder.add(response.getIndexName(),
|
||||
fieldCap.isSearchable(), fieldCap.isAggregatable());
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Map<String, FieldCapabilities>> responseMap = new HashMap<>();
|
||||
for (Map.Entry<String, Map<String, FieldCapabilities.Builder>> entry :
|
||||
responseMapBuilder.entrySet()) {
|
||||
Map<String, FieldCapabilities> typeMap = new HashMap<>();
|
||||
boolean multiTypes = entry.getValue().size() > 1;
|
||||
for (Map.Entry<String, FieldCapabilities.Builder> fieldEntry :
|
||||
entry.getValue().entrySet()) {
|
||||
typeMap.put(fieldEntry.getKey(), fieldEntry.getValue().build(multiTypes));
|
||||
}
|
||||
responseMap.put(entry.getKey(), typeMap);
|
||||
}
|
||||
|
||||
return new FieldCapabilitiesResponse(responseMap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* 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.action.support.ActionFilters;
|
||||
import org.elasticsearch.action.support.single.shard.TransportSingleShardAction;
|
||||
import org.elasticsearch.cluster.ClusterState;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||
import org.elasticsearch.cluster.routing.ShardsIterator;
|
||||
import org.elasticsearch.cluster.service.ClusterService;
|
||||
import org.elasticsearch.common.inject.Inject;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.index.mapper.MappedFieldType;
|
||||
import org.elasticsearch.index.mapper.MapperService;
|
||||
import org.elasticsearch.index.shard.ShardId;
|
||||
import org.elasticsearch.indices.IndicesService;
|
||||
import org.elasticsearch.threadpool.ThreadPool;
|
||||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class TransportFieldCapabilitiesIndexAction
|
||||
extends TransportSingleShardAction<FieldCapabilitiesIndexRequest,
|
||||
FieldCapabilitiesIndexResponse> {
|
||||
|
||||
private static final String ACTION_NAME = FieldCapabilitiesAction.NAME + "[index]";
|
||||
|
||||
protected final ClusterService clusterService;
|
||||
private final IndicesService indicesService;
|
||||
|
||||
@Inject
|
||||
public TransportFieldCapabilitiesIndexAction(Settings settings,
|
||||
ClusterService clusterService,
|
||||
TransportService transportService,
|
||||
IndicesService indicesService,
|
||||
ThreadPool threadPool,
|
||||
ActionFilters actionFilters,
|
||||
IndexNameExpressionResolver
|
||||
indexNameExpressionResolver) {
|
||||
super(settings,
|
||||
ACTION_NAME,
|
||||
threadPool,
|
||||
clusterService,
|
||||
transportService,
|
||||
actionFilters,
|
||||
indexNameExpressionResolver,
|
||||
FieldCapabilitiesIndexRequest::new,
|
||||
ThreadPool.Names.MANAGEMENT);
|
||||
this.clusterService = clusterService;
|
||||
this.indicesService = indicesService;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean resolveIndex(FieldCapabilitiesIndexRequest request) {
|
||||
//internal action, index already resolved
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ShardsIterator shards(ClusterState state, InternalRequest request) {
|
||||
// Will balance requests between shards
|
||||
// Resolve patterns and deduplicate
|
||||
return state.routingTable().index(request.concreteIndex()).randomAllActiveShardsIt();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FieldCapabilitiesIndexResponse shardOperation(
|
||||
final FieldCapabilitiesIndexRequest request,
|
||||
ShardId shardId) {
|
||||
MapperService mapperService =
|
||||
indicesService.indexServiceSafe(shardId.getIndex()).mapperService();
|
||||
Set<String> fieldNames = new HashSet<>();
|
||||
for (String field : request.fields()) {
|
||||
fieldNames.addAll(mapperService.simpleMatchToIndexNames(field));
|
||||
}
|
||||
Map<String, FieldCapabilities> responseMap = new HashMap<>();
|
||||
for (String field : fieldNames) {
|
||||
MappedFieldType ft = mapperService.fullName(field);
|
||||
FieldCapabilities fieldCap = new FieldCapabilities(field,
|
||||
ft.typeName(),
|
||||
ft.isSearchable(),
|
||||
ft.isAggregatable());
|
||||
responseMap.put(field, fieldCap);
|
||||
}
|
||||
return new FieldCapabilitiesIndexResponse(shardId.getIndexName(), responseMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected FieldCapabilitiesIndexResponse newResponse() {
|
||||
return new FieldCapabilitiesIndexResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClusterBlockException checkRequestBlock(ClusterState state,
|
||||
InternalRequest request) {
|
||||
return state.blocks().indexBlockedException(ClusterBlockLevel.METADATA_READ,
|
||||
request.concreteIndex());
|
||||
}
|
||||
}
|
|
@ -30,6 +30,10 @@ import org.elasticsearch.action.delete.DeleteResponse;
|
|||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainRequestBuilder;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilities;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequestBuilder;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsRequest;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsRequestBuilder;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsResponse;
|
||||
|
@ -458,6 +462,21 @@ public interface Client extends ElasticsearchClient, Releasable {
|
|||
|
||||
void fieldStats(FieldStatsRequest request, ActionListener<FieldStatsResponse> listener);
|
||||
|
||||
/**
|
||||
* Builder for the field capabilities request.
|
||||
*/
|
||||
FieldCapabilitiesRequestBuilder prepareFieldCaps();
|
||||
|
||||
/**
|
||||
* An action that returns the field capabilities from the provided request
|
||||
*/
|
||||
ActionFuture<FieldCapabilitiesResponse> fieldCaps(FieldCapabilitiesRequest request);
|
||||
|
||||
/**
|
||||
* An action that returns the field capabilities from the provided request
|
||||
*/
|
||||
void fieldCaps(FieldCapabilitiesRequest request, ActionListener<FieldCapabilitiesResponse> listener);
|
||||
|
||||
/**
|
||||
* Returns this clients settings
|
||||
*/
|
||||
|
|
|
@ -50,6 +50,9 @@ import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRespon
|
|||
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsRequest;
|
||||
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.exists.types.TypesExistsResponse;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequestBuilder;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushRequest;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushRequestBuilder;
|
||||
import org.elasticsearch.action.admin.indices.flush.FlushResponse;
|
||||
|
@ -817,5 +820,4 @@ public interface IndicesAdminClient extends ElasticsearchClient {
|
|||
* Swaps the index pointed to by an alias given all provided conditions are satisfied
|
||||
*/
|
||||
void rolloverIndex(RolloverRequest request, ActionListener<RolloverResponse> listener);
|
||||
|
||||
}
|
||||
|
|
|
@ -272,6 +272,10 @@ import org.elasticsearch.action.explain.ExplainAction;
|
|||
import org.elasticsearch.action.explain.ExplainRequest;
|
||||
import org.elasticsearch.action.explain.ExplainRequestBuilder;
|
||||
import org.elasticsearch.action.explain.ExplainResponse;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesAction;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequestBuilder;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsAction;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsRequest;
|
||||
import org.elasticsearch.action.fieldstats.FieldStatsRequestBuilder;
|
||||
|
@ -667,6 +671,21 @@ public abstract class AbstractClient extends AbstractComponent implements Client
|
|||
return new FieldStatsRequestBuilder(this, FieldStatsAction.INSTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldCaps(FieldCapabilitiesRequest request, ActionListener<FieldCapabilitiesResponse> listener) {
|
||||
execute(FieldCapabilitiesAction.INSTANCE, request, listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionFuture<FieldCapabilitiesResponse> fieldCaps(FieldCapabilitiesRequest request) {
|
||||
return execute(FieldCapabilitiesAction.INSTANCE, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldCapabilitiesRequestBuilder prepareFieldCaps() {
|
||||
return new FieldCapabilitiesRequestBuilder(this, FieldCapabilitiesAction.INSTANCE);
|
||||
}
|
||||
|
||||
static class Admin implements AdminClient {
|
||||
|
||||
private final ClusterAdmin clusterAdmin;
|
||||
|
|
|
@ -313,14 +313,14 @@ public abstract class MappedFieldType extends FieldType {
|
|||
/** Returns true if the field is searchable.
|
||||
*
|
||||
*/
|
||||
protected boolean isSearchable() {
|
||||
public boolean isSearchable() {
|
||||
return indexOptions() != IndexOptions.NONE;
|
||||
}
|
||||
|
||||
/** Returns true if the field is aggregatable.
|
||||
*
|
||||
*/
|
||||
protected boolean isAggregatable() {
|
||||
public boolean isAggregatable() {
|
||||
try {
|
||||
fielddataBuilder();
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* 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.rest.action;
|
||||
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesRequest;
|
||||
import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
|
||||
import org.elasticsearch.action.support.IndicesOptions;
|
||||
import org.elasticsearch.client.node.NodeClient;
|
||||
import org.elasticsearch.common.Strings;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.common.xcontent.XContentParser;
|
||||
import org.elasticsearch.rest.BaseRestHandler;
|
||||
import org.elasticsearch.rest.BytesRestResponse;
|
||||
import org.elasticsearch.rest.RestController;
|
||||
import org.elasticsearch.rest.RestRequest;
|
||||
import org.elasticsearch.rest.RestResponse;
|
||||
import org.elasticsearch.rest.RestStatus;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.elasticsearch.rest.RestRequest.Method.GET;
|
||||
import static org.elasticsearch.rest.RestRequest.Method.POST;
|
||||
import static org.elasticsearch.rest.RestStatus.NOT_FOUND;
|
||||
import static org.elasticsearch.rest.RestStatus.OK;
|
||||
|
||||
public class RestFieldCapabilitiesAction extends BaseRestHandler {
|
||||
public RestFieldCapabilitiesAction(Settings settings, RestController controller) {
|
||||
super(settings);
|
||||
controller.registerHandler(GET, "/_field_caps", this);
|
||||
controller.registerHandler(POST, "/_field_caps", this);
|
||||
controller.registerHandler(GET, "/{index}/_field_caps", this);
|
||||
controller.registerHandler(POST, "/{index}/_field_caps", this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestChannelConsumer prepareRequest(final RestRequest request,
|
||||
final NodeClient client) throws IOException {
|
||||
if (request.hasContentOrSourceParam() && request.hasParam("fields")) {
|
||||
throw new IllegalArgumentException("can't specify a request body and [fields]" +
|
||||
" request parameter, either specify a request body or the" +
|
||||
" [fields] request parameter");
|
||||
}
|
||||
final String[] indices = Strings.splitStringByCommaToArray(request.param("index"));
|
||||
final FieldCapabilitiesRequest fieldRequest;
|
||||
if (request.hasContentOrSourceParam()) {
|
||||
try (XContentParser parser = request.contentOrSourceParamParser()) {
|
||||
fieldRequest = FieldCapabilitiesRequest.parseFields(parser);
|
||||
}
|
||||
} else {
|
||||
fieldRequest = new FieldCapabilitiesRequest();
|
||||
fieldRequest.fields(Strings.splitStringByCommaToArray(request.param("fields")));
|
||||
}
|
||||
fieldRequest.indices(indices);
|
||||
fieldRequest.indicesOptions(
|
||||
IndicesOptions.fromRequest(request, fieldRequest.indicesOptions())
|
||||
);
|
||||
return channel -> client.fieldCaps(fieldRequest,
|
||||
new RestBuilderListener<FieldCapabilitiesResponse>(channel) {
|
||||
@Override
|
||||
public RestResponse buildResponse(FieldCapabilitiesResponse response,
|
||||
XContentBuilder builder) throws Exception {
|
||||
RestStatus status = OK;
|
||||
builder.startObject();
|
||||
response.toXContent(builder, request);
|
||||
builder.endObject();
|
||||
return new BytesRestResponse(status, builder);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class FieldCapabilitiesRequestTests extends ESTestCase {
|
||||
private FieldCapabilitiesRequest randomRequest() {
|
||||
FieldCapabilitiesRequest request = new FieldCapabilitiesRequest();
|
||||
int size = randomIntBetween(1, 20);
|
||||
String[] randomFields = new String[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
randomFields[i] = randomAsciiOfLengthBetween(5, 10);
|
||||
}
|
||||
request.fields(randomFields);
|
||||
return request;
|
||||
}
|
||||
|
||||
public void testFieldCapsRequestSerialization() throws IOException {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
FieldCapabilitiesRequest request = randomRequest();
|
||||
BytesStreamOutput output = new BytesStreamOutput();
|
||||
request.writeTo(output);
|
||||
output.flush();
|
||||
StreamInput input = output.bytes().streamInput();
|
||||
FieldCapabilitiesRequest deserialized = new FieldCapabilitiesRequest();
|
||||
deserialized.readFrom(input);
|
||||
assertEquals(deserialized, request);
|
||||
assertEquals(deserialized.hashCode(), request.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* 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.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class FieldCapabilitiesResponseTests extends ESTestCase {
|
||||
private FieldCapabilitiesResponse randomResponse() {
|
||||
Map<String, Map<String, FieldCapabilities> > fieldMap = new HashMap<> ();
|
||||
int numFields = randomInt(10);
|
||||
for (int i = 0; i < numFields; i++) {
|
||||
String fieldName = randomAsciiOfLengthBetween(5, 10);
|
||||
int numIndices = randomIntBetween(1, 5);
|
||||
Map<String, FieldCapabilities> indexFieldMap = new HashMap<> ();
|
||||
for (int j = 0; j < numIndices; j++) {
|
||||
String index = randomAsciiOfLengthBetween(10, 20);
|
||||
indexFieldMap.put(index, FieldCapabilitiesTests.randomFieldCaps());
|
||||
}
|
||||
fieldMap.put(fieldName, indexFieldMap);
|
||||
}
|
||||
return new FieldCapabilitiesResponse(fieldMap);
|
||||
}
|
||||
|
||||
public void testSerialization() throws IOException {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
FieldCapabilitiesResponse response = randomResponse();
|
||||
BytesStreamOutput output = new BytesStreamOutput();
|
||||
response.writeTo(output);
|
||||
output.flush();
|
||||
StreamInput input = output.bytes().streamInput();
|
||||
FieldCapabilitiesResponse deserialized = new FieldCapabilitiesResponse();
|
||||
deserialized.readFrom(input);
|
||||
assertEquals(deserialized, response);
|
||||
assertEquals(deserialized.hashCode(), response.hashCode());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* 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.io.stream.Writeable;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class FieldCapabilitiesTests extends AbstractWireSerializingTestCase<FieldCapabilities> {
|
||||
@Override
|
||||
protected FieldCapabilities createTestInstance() {
|
||||
return randomFieldCaps();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<FieldCapabilities> instanceReader() {
|
||||
return FieldCapabilities::new;
|
||||
}
|
||||
|
||||
public void testBuilder() {
|
||||
FieldCapabilities.Builder builder = new FieldCapabilities.Builder("field", "type");
|
||||
builder.add("index1", true, false);
|
||||
builder.add("index2", true, false);
|
||||
builder.add("index3", true, false);
|
||||
|
||||
{
|
||||
FieldCapabilities cap1 = builder.build(false);
|
||||
assertThat(cap1.isSearchable(), equalTo(true));
|
||||
assertThat(cap1.isAggregatable(), equalTo(false));
|
||||
assertNull(cap1.indices());
|
||||
assertNull(cap1.nonSearchableIndices());
|
||||
assertNull(cap1.nonAggregatableIndices());
|
||||
|
||||
FieldCapabilities cap2 = builder.build(true);
|
||||
assertThat(cap2.isSearchable(), equalTo(true));
|
||||
assertThat(cap2.isAggregatable(), equalTo(false));
|
||||
assertThat(cap2.indices().length, equalTo(3));
|
||||
assertThat(cap2.indices(), equalTo(new String[]{"index1", "index2", "index3"}));
|
||||
assertNull(cap2.nonSearchableIndices());
|
||||
assertNull(cap2.nonAggregatableIndices());
|
||||
}
|
||||
|
||||
builder = new FieldCapabilities.Builder("field", "type");
|
||||
builder.add("index1", false, true);
|
||||
builder.add("index2", true, false);
|
||||
builder.add("index3", false, false);
|
||||
{
|
||||
FieldCapabilities cap1 = builder.build(false);
|
||||
assertThat(cap1.isSearchable(), equalTo(false));
|
||||
assertThat(cap1.isAggregatable(), equalTo(false));
|
||||
assertNull(cap1.indices());
|
||||
assertThat(cap1.nonSearchableIndices(), equalTo(new String[]{"index1", "index3"}));
|
||||
assertThat(cap1.nonAggregatableIndices(), equalTo(new String[]{"index2", "index3"}));
|
||||
|
||||
FieldCapabilities cap2 = builder.build(true);
|
||||
assertThat(cap2.isSearchable(), equalTo(false));
|
||||
assertThat(cap2.isAggregatable(), equalTo(false));
|
||||
assertThat(cap2.indices().length, equalTo(3));
|
||||
assertThat(cap2.indices(), equalTo(new String[]{"index1", "index2", "index3"}));
|
||||
assertThat(cap1.nonSearchableIndices(), equalTo(new String[]{"index1", "index3"}));
|
||||
assertThat(cap1.nonAggregatableIndices(), equalTo(new String[]{"index2", "index3"}));
|
||||
}
|
||||
}
|
||||
|
||||
static FieldCapabilities randomFieldCaps() {
|
||||
String[] indices = null;
|
||||
if (randomBoolean()) {
|
||||
indices = new String[randomIntBetween(1, 5)];
|
||||
for (int i = 0; i < indices.length; i++) {
|
||||
indices[i] = randomAsciiOfLengthBetween(5, 20);
|
||||
}
|
||||
}
|
||||
String[] nonSearchableIndices = null;
|
||||
if (randomBoolean()) {
|
||||
nonSearchableIndices = new String[randomIntBetween(0, 5)];
|
||||
for (int i = 0; i < nonSearchableIndices.length; i++) {
|
||||
nonSearchableIndices[i] = randomAsciiOfLengthBetween(5, 20);
|
||||
}
|
||||
}
|
||||
String[] nonAggregatableIndices = null;
|
||||
if (randomBoolean()) {
|
||||
nonAggregatableIndices = new String[randomIntBetween(0, 5)];
|
||||
for (int i = 0; i < nonAggregatableIndices.length; i++) {
|
||||
nonAggregatableIndices[i] = randomAsciiOfLengthBetween(5, 20);
|
||||
}
|
||||
}
|
||||
return new FieldCapabilities(randomAsciiOfLengthBetween(5, 20),
|
||||
randomAsciiOfLengthBetween(5, 20), randomBoolean(), randomBoolean(),
|
||||
indices, nonSearchableIndices, nonAggregatableIndices);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
[[search-field-caps]]
|
||||
== Field Capabilities API
|
||||
|
||||
experimental[]
|
||||
|
||||
The field capabilities API allows to retrieve the capabilities of fields among multiple indices.
|
||||
|
||||
The field capabilities api by default executes on all indices:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
GET _field_caps?fields=rating
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
|
||||
... but the request can also be restricted to specific indices:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
GET twitter/_field_caps?fields=rating
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
// TEST[setup:twitter]
|
||||
|
||||
Alternatively the `fields` option can also be defined in the request body:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
POST _field_caps
|
||||
{
|
||||
"fields" : ["rating"]
|
||||
}
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
|
||||
This is equivalent to the previous request.
|
||||
|
||||
Supported request options:
|
||||
|
||||
[horizontal]
|
||||
`fields`:: A list of fields to compute stats for. The field name supports wildcard notation. For example, using `text_*`
|
||||
will cause all fields that match the expression to be returned.
|
||||
|
||||
[float]
|
||||
=== Field Capabilities
|
||||
|
||||
The field capabilities api returns the following information per field:
|
||||
|
||||
[horizontal]
|
||||
`is_searchable`::
|
||||
|
||||
Whether this field is indexed for search on all indices.
|
||||
|
||||
`is_aggregatable`::
|
||||
|
||||
Whether this field can be aggregated on all indices.
|
||||
|
||||
`indices`::
|
||||
|
||||
The list of indices where this field has the same type,
|
||||
or null if all indices have the same type for the field.
|
||||
|
||||
`non_searchable_indices`::
|
||||
|
||||
The list of indices where this field is not searchable,
|
||||
or null if all indices have the same definition for the field.
|
||||
|
||||
`non_aggregatable_indices`::
|
||||
|
||||
The list of indices where this field is not aggregatable,
|
||||
or null if all indices have the same definition for the field.
|
||||
|
||||
|
||||
[float]
|
||||
=== Response format
|
||||
|
||||
Request:
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
GET _field_caps?fields=rating,title
|
||||
--------------------------------------------------
|
||||
// CONSOLE
|
||||
|
||||
[source,js]
|
||||
--------------------------------------------------
|
||||
{
|
||||
"fields": {
|
||||
"rating": { <1>
|
||||
"long": {
|
||||
"is_searchable": true,
|
||||
"is_aggregatable": false,
|
||||
"indices": ["index1", "index2"],
|
||||
"non_aggregatable_indices": ["index1"] <2>
|
||||
},
|
||||
"keyword": {
|
||||
"is_searchable": false,
|
||||
"is_aggregatable": true,
|
||||
"indices": ["index3", "index4"],
|
||||
"non_searchable_indices": ["index4"] <3>
|
||||
}
|
||||
},
|
||||
"title": { <4>
|
||||
"text": {
|
||||
"is_searchable": true,
|
||||
"is_aggregatable": false
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
--------------------------------------------------
|
||||
// NOTCONSOLE
|
||||
|
||||
<1> The field `rating` is defined as a long in `index1` and `index2`
|
||||
and as a `keyword` in `index3` and `index4`.
|
||||
<2> The field `rating` is not aggregatable in `index1`.
|
||||
<3> The field `rating` is not searchable in `index4`.
|
||||
<4> The field `title` is defined as `text` in all indices.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"field_caps": {
|
||||
"documentation": "http://www.elastic.co/guide/en/elasticsearch/reference/master/search-field-caps.html",
|
||||
"methods": ["GET", "POST"],
|
||||
"url": {
|
||||
"path": "/_field_caps",
|
||||
"paths": [
|
||||
"/_field_caps",
|
||||
"/{index}/_field_caps"
|
||||
],
|
||||
"parts": {
|
||||
"index": {
|
||||
"type" : "list",
|
||||
"description" : "A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices"
|
||||
}
|
||||
},
|
||||
"params": {
|
||||
"fields": {
|
||||
"type" : "list",
|
||||
"description" : "A comma-separated list of field names"
|
||||
},
|
||||
"ignore_unavailable": {
|
||||
"type" : "boolean",
|
||||
"description" : "Whether specified concrete indices should be ignored when unavailable (missing or closed)"
|
||||
},
|
||||
"allow_no_indices": {
|
||||
"type" : "boolean",
|
||||
"description" : "Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)"
|
||||
},
|
||||
"expand_wildcards": {
|
||||
"type" : "enum",
|
||||
"options" : ["open","closed","none","all"],
|
||||
"default" : "open",
|
||||
"description" : "Whether to expand wildcard expression to concrete indices that are open, closed or both."
|
||||
}
|
||||
}
|
||||
},
|
||||
"body": {
|
||||
"description": "Field json objects containing an array of field names",
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
---
|
||||
setup:
|
||||
- do:
|
||||
indices.create:
|
||||
index: test1
|
||||
body:
|
||||
mappings:
|
||||
t:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
keyword:
|
||||
type: keyword
|
||||
number:
|
||||
type: double
|
||||
geo:
|
||||
type: geo_point
|
||||
object:
|
||||
type: object
|
||||
properties:
|
||||
nested1 :
|
||||
type : text
|
||||
index: false
|
||||
nested2:
|
||||
type: float
|
||||
doc_values: false
|
||||
- do:
|
||||
indices.create:
|
||||
index: test2
|
||||
body:
|
||||
mappings:
|
||||
t:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
keyword:
|
||||
type: keyword
|
||||
number:
|
||||
type: double
|
||||
geo:
|
||||
type: geo_point
|
||||
object:
|
||||
type: object
|
||||
properties:
|
||||
nested1 :
|
||||
type : text
|
||||
index: true
|
||||
nested2:
|
||||
type: float
|
||||
doc_values: true
|
||||
- do:
|
||||
indices.create:
|
||||
index: test3
|
||||
body:
|
||||
mappings:
|
||||
t:
|
||||
properties:
|
||||
text:
|
||||
type: text
|
||||
keyword:
|
||||
type: keyword
|
||||
number:
|
||||
type: long
|
||||
geo:
|
||||
type: keyword
|
||||
object:
|
||||
type: object
|
||||
properties:
|
||||
nested1 :
|
||||
type : long
|
||||
index: false
|
||||
nested2:
|
||||
type: keyword
|
||||
doc_values: false
|
||||
|
||||
---
|
||||
"Get simple field caps":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: this uses a new API that has been added in 6.0
|
||||
|
||||
- do:
|
||||
field_caps:
|
||||
index: 'test1,test2,test3'
|
||||
fields: [text, keyword, number, geo]
|
||||
|
||||
- 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.keyword.keyword.searchable: true}
|
||||
- match: {fields.keyword.keyword.aggregatable: true}
|
||||
- is_false: fields.text.keyword.indices
|
||||
- is_false: fields.text.keyword.non_searchable_indices
|
||||
- is_false: fields.text.keyword.non_aggregatable_indices
|
||||
- match: {fields.number.double.searchable: true}
|
||||
- match: {fields.number.double.aggregatable: true}
|
||||
- match: {fields.number.double.indices: ["test1", "test2"]}
|
||||
- is_false: fields.number.double.non_searchable_indices
|
||||
- is_false: fields.number.double.non_aggregatable_indices
|
||||
- match: {fields.number.long.searchable: true}
|
||||
- match: {fields.number.long.aggregatable: true}
|
||||
- match: {fields.number.long.indices: ["test3"]}
|
||||
- is_false: fields.number.long.non_searchable_indices
|
||||
- is_false: fields.number.long.non_aggregatable_indices
|
||||
- match: {fields.geo.geo_point.searchable: true}
|
||||
- match: {fields.geo.geo_point.aggregatable: true}
|
||||
- match: {fields.geo.geo_point.indices: ["test1", "test2"]}
|
||||
- is_false: fields.geo.geo_point.non_searchable_indices
|
||||
- is_false: fields.geo.geo_point.non_aggregatable_indices
|
||||
- match: {fields.geo.keyword.searchable: true}
|
||||
- match: {fields.geo.keyword.aggregatable: true}
|
||||
- match: {fields.geo.keyword.indices: ["test3"]}
|
||||
- is_false: fields.geo.keyword.non_searchable_indices
|
||||
- is_false: fields.geo.keyword.on_aggregatable_indices
|
||||
---
|
||||
"Get nested field caps":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: this uses a new API that has been added in 6.0
|
||||
|
||||
- do:
|
||||
field_caps:
|
||||
index: 'test1,test2,test3'
|
||||
fields: object*
|
||||
|
||||
- match: {fields.object\.nested1.long.searchable: false}
|
||||
- match: {fields.object\.nested1.long.aggregatable: true}
|
||||
- match: {fields.object\.nested1.long.indices: ["test3"]}
|
||||
- is_false: fields.object\.nested1.long.non_searchable_indices
|
||||
- is_false: fields.object\.nested1.long.non_aggregatable_indices
|
||||
- match: {fields.object\.nested1.text.searchable: false}
|
||||
- match: {fields.object\.nested1.text.aggregatable: false}
|
||||
- match: {fields.object\.nested1.text.indices: ["test1", "test2"]}
|
||||
- match: {fields.object\.nested1.text.non_searchable_indices: ["test1"]}
|
||||
- is_false: fields.object\.nested1.text.non_aggregatable_indices
|
||||
- match: {fields.object\.nested2.float.searchable: true}
|
||||
- match: {fields.object\.nested2.float.aggregatable: false}
|
||||
- match: {fields.object\.nested2.float.indices: ["test1", "test2"]}
|
||||
- match: {fields.object\.nested2.float.non_aggregatable_indices: ["test1"]}
|
||||
- is_false: fields.object\.nested2.float.non_searchable_indices
|
||||
- match: {fields.object\.nested2.keyword.searchable: true}
|
||||
- match: {fields.object\.nested2.keyword.aggregatable: false}
|
||||
- match: {fields.object\.nested2.keyword.indices: ["test3"]}
|
||||
- is_false: fields.object\.nested2.keyword.non_aggregatable_indices
|
||||
- is_false: fields.object\.nested2.keyword.non_searchable_indices
|
||||
---
|
||||
"Get prefix field caps":
|
||||
- skip:
|
||||
version: " - 5.99.99"
|
||||
reason: this uses a new API that has been added in 6.0
|
||||
|
||||
- do:
|
||||
field_caps:
|
||||
index: _all
|
||||
fields: "n*"
|
||||
- match: {fields.number.double.searchable: true}
|
||||
- match: {fields.number.double.aggregatable: true}
|
||||
- match: {fields.number.double.indices: ["test1", "test2"]}
|
||||
- is_false: fields.number.double.non_searchable_indices
|
||||
- is_false: fields.number.double.non_aggregatable_indices
|
||||
- match: {fields.number.long.searchable: true}
|
||||
- match: {fields.number.long.aggregatable: true}
|
||||
- match: {fields.number.long.indices: ["test3"]}
|
||||
- is_false: fields.number.long.non_searchable_indices
|
||||
- is_false: fields.number.long.non_aggregatable_indices
|
Loading…
Reference in New Issue