|
@@ -19,6 +19,7 @@
|
|
|
|
|
|
package org.elasticsearch.action.fieldcaps;
|
|
|
|
|
|
+import org.elasticsearch.Version;
|
|
|
import org.elasticsearch.common.ParseField;
|
|
|
import org.elasticsearch.common.Strings;
|
|
|
import org.elasticsearch.common.io.stream.StreamInput;
|
|
@@ -34,20 +35,33 @@ import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.Comparator;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.HashSet;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
import java.util.Objects;
|
|
|
+import java.util.Set;
|
|
|
+import java.util.function.Function;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* Describes the capabilities of a field optionally merged across multiple indices.
|
|
|
*/
|
|
|
public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
+
|
|
|
private static final ParseField TYPE_FIELD = new ParseField("type");
|
|
|
private static final ParseField SEARCHABLE_FIELD = new ParseField("searchable");
|
|
|
private static final ParseField AGGREGATABLE_FIELD = new ParseField("aggregatable");
|
|
|
private static final ParseField INDICES_FIELD = new ParseField("indices");
|
|
|
private static final ParseField NON_SEARCHABLE_INDICES_FIELD = new ParseField("non_searchable_indices");
|
|
|
private static final ParseField NON_AGGREGATABLE_INDICES_FIELD = new ParseField("non_aggregatable_indices");
|
|
|
+ private static final ParseField META_FIELD = new ParseField("meta");
|
|
|
+
|
|
|
+ private static Map<String, Set<String>> mapToMapOfSets(Map<String, String> map) {
|
|
|
+ final Function<Map.Entry<String, String>, String> entryValueFunction = Map.Entry::getValue;
|
|
|
+ return map.entrySet().stream().collect(
|
|
|
+ Collectors.toUnmodifiableMap(Map.Entry::getKey, entryValueFunction.andThen(Set::of)));
|
|
|
+ }
|
|
|
|
|
|
private final String name;
|
|
|
private final String type;
|
|
@@ -58,19 +72,23 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
private final String[] nonSearchableIndices;
|
|
|
private final String[] nonAggregatableIndices;
|
|
|
|
|
|
+ private final Map<String, Set<String>> meta;
|
|
|
+
|
|
|
/**
|
|
|
- * Constructor
|
|
|
+ * Constructor for a single index.
|
|
|
* @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 meta Metadata about the field.
|
|
|
*/
|
|
|
- public FieldCapabilities(String name, String type, boolean isSearchable, boolean isAggregatable) {
|
|
|
- this(name, type, isSearchable, isAggregatable, null, null, null);
|
|
|
+ public FieldCapabilities(String name, String type, boolean isSearchable, boolean isAggregatable,
|
|
|
+ Map<String, String> meta) {
|
|
|
+ this(name, type, isSearchable, isAggregatable, null, null, null, mapToMapOfSets(Objects.requireNonNull(meta)));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Constructor
|
|
|
+ * Constructor for a set of indices.
|
|
|
* @param name The name of the field
|
|
|
* @param type The type associated with the field.
|
|
|
* @param isSearchable Whether this field is indexed for search.
|
|
@@ -81,12 +99,14 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
* 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.
|
|
|
+ * @param meta Merged metadata across indices.
|
|
|
*/
|
|
|
public FieldCapabilities(String name, String type,
|
|
|
boolean isSearchable, boolean isAggregatable,
|
|
|
String[] indices,
|
|
|
String[] nonSearchableIndices,
|
|
|
- String[] nonAggregatableIndices) {
|
|
|
+ String[] nonAggregatableIndices,
|
|
|
+ Map<String, Set<String>> meta) {
|
|
|
this.name = name;
|
|
|
this.type = type;
|
|
|
this.isSearchable = isSearchable;
|
|
@@ -94,6 +114,7 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
this.indices = indices;
|
|
|
this.nonSearchableIndices = nonSearchableIndices;
|
|
|
this.nonAggregatableIndices = nonAggregatableIndices;
|
|
|
+ this.meta = Objects.requireNonNull(meta);
|
|
|
}
|
|
|
|
|
|
public FieldCapabilities(StreamInput in) throws IOException {
|
|
@@ -104,6 +125,11 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
this.indices = in.readOptionalStringArray();
|
|
|
this.nonSearchableIndices = in.readOptionalStringArray();
|
|
|
this.nonAggregatableIndices = in.readOptionalStringArray();
|
|
|
+ if (in.getVersion().onOrAfter(Version.V_8_0_0)) {
|
|
|
+ meta = in.readMap(StreamInput::readString, i -> i.readSet(StreamInput::readString));
|
|
|
+ } else {
|
|
|
+ meta = Collections.emptyMap();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -115,6 +141,9 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
out.writeOptionalStringArray(indices);
|
|
|
out.writeOptionalStringArray(nonSearchableIndices);
|
|
|
out.writeOptionalStringArray(nonAggregatableIndices);
|
|
|
+ if (out.getVersion().onOrAfter(Version.V_8_0_0)) {
|
|
|
+ out.writeMap(meta, StreamOutput::writeString, (o, set) -> o.writeCollection(set, StreamOutput::writeString));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@Override
|
|
@@ -132,6 +161,17 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
if (nonAggregatableIndices != null) {
|
|
|
builder.field(NON_AGGREGATABLE_INDICES_FIELD.getPreferredName(), nonAggregatableIndices);
|
|
|
}
|
|
|
+ if (meta.isEmpty() == false) {
|
|
|
+ builder.startObject("meta");
|
|
|
+ List<Map.Entry<String, Set<String>>> entries = new ArrayList<>(meta.entrySet());
|
|
|
+ entries.sort(Comparator.comparing(Map.Entry::getKey)); // provide predictable order
|
|
|
+ for (Map.Entry<String, Set<String>> entry : entries) {
|
|
|
+ List<String> values = new ArrayList<>(entry.getValue());
|
|
|
+ values.sort(String::compareTo); // provide predictable order
|
|
|
+ builder.field(entry.getKey(), values);
|
|
|
+ }
|
|
|
+ builder.endObject();
|
|
|
+ }
|
|
|
builder.endObject();
|
|
|
return builder;
|
|
|
}
|
|
@@ -150,7 +190,8 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
(boolean) a[2],
|
|
|
a[3] != null ? ((List<String>) a[3]).toArray(new String[0]) : null,
|
|
|
a[4] != null ? ((List<String>) a[4]).toArray(new String[0]) : null,
|
|
|
- a[5] != null ? ((List<String>) a[5]).toArray(new String[0]) : null));
|
|
|
+ a[5] != null ? ((List<String>) a[5]).toArray(new String[0]) : null,
|
|
|
+ a[6] != null ? ((Map<String, Set<String>>) a[6]) : Collections.emptyMap()));
|
|
|
|
|
|
static {
|
|
|
PARSER.declareString(ConstructingObjectParser.constructorArg(), TYPE_FIELD);
|
|
@@ -159,6 +200,8 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), INDICES_FIELD);
|
|
|
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), NON_SEARCHABLE_INDICES_FIELD);
|
|
|
PARSER.declareStringArray(ConstructingObjectParser.optionalConstructorArg(), NON_AGGREGATABLE_INDICES_FIELD);
|
|
|
+ PARSER.declareObject(ConstructingObjectParser.optionalConstructorArg(),
|
|
|
+ (parser, context) -> parser.map(HashMap::new, p -> Set.copyOf(p.list())), META_FIELD);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -213,6 +256,13 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
return nonAggregatableIndices;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Return merged metadata across indices.
|
|
|
+ */
|
|
|
+ public Map<String, Set<String>> meta() {
|
|
|
+ return meta;
|
|
|
+ }
|
|
|
+
|
|
|
@Override
|
|
|
public boolean equals(Object o) {
|
|
|
if (this == o) return true;
|
|
@@ -224,12 +274,13 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
Objects.equals(type, that.type) &&
|
|
|
Arrays.equals(indices, that.indices) &&
|
|
|
Arrays.equals(nonSearchableIndices, that.nonSearchableIndices) &&
|
|
|
- Arrays.equals(nonAggregatableIndices, that.nonAggregatableIndices);
|
|
|
+ Arrays.equals(nonAggregatableIndices, that.nonAggregatableIndices) &&
|
|
|
+ Objects.equals(meta, that.meta);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public int hashCode() {
|
|
|
- int result = Objects.hash(name, type, isSearchable, isAggregatable);
|
|
|
+ int result = Objects.hash(name, type, isSearchable, isAggregatable, meta);
|
|
|
result = 31 * result + Arrays.hashCode(indices);
|
|
|
result = 31 * result + Arrays.hashCode(nonSearchableIndices);
|
|
|
result = 31 * result + Arrays.hashCode(nonAggregatableIndices);
|
|
@@ -247,6 +298,7 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
private boolean isSearchable;
|
|
|
private boolean isAggregatable;
|
|
|
private List<IndexCaps> indiceList;
|
|
|
+ private Map<String, Set<String>> meta;
|
|
|
|
|
|
Builder(String name, String type) {
|
|
|
this.name = name;
|
|
@@ -254,15 +306,38 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
this.isSearchable = true;
|
|
|
this.isAggregatable = true;
|
|
|
this.indiceList = new ArrayList<>();
|
|
|
+ this.meta = new HashMap<>();
|
|
|
}
|
|
|
|
|
|
- void add(String index, boolean search, boolean agg) {
|
|
|
+ private void add(String index, boolean search, boolean agg) {
|
|
|
IndexCaps indexCaps = new IndexCaps(index, search, agg);
|
|
|
indiceList.add(indexCaps);
|
|
|
this.isSearchable &= search;
|
|
|
this.isAggregatable &= agg;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Collect capabilities of an index.
|
|
|
+ */
|
|
|
+ void add(String index, boolean search, boolean agg, Map<String, String> meta) {
|
|
|
+ add(index, search, agg);
|
|
|
+ for (Map.Entry<String, String> entry : meta.entrySet()) {
|
|
|
+ this.meta.computeIfAbsent(entry.getKey(), key -> new HashSet<>())
|
|
|
+ .add(entry.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Merge another capabilities instance.
|
|
|
+ */
|
|
|
+ void merge(String index, boolean search, boolean agg, Map<String, Set<String>> meta) {
|
|
|
+ add(index, search, agg);
|
|
|
+ for (Map.Entry<String, Set<String>> entry : meta.entrySet()) {
|
|
|
+ this.meta.computeIfAbsent(entry.getKey(), key -> new HashSet<>())
|
|
|
+ .addAll(entry.getValue());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
List<String> getIndices() {
|
|
|
return indiceList.stream().map(c -> c.name).collect(Collectors.toList());
|
|
|
}
|
|
@@ -305,8 +380,12 @@ public class FieldCapabilities implements Writeable, ToXContentObject {
|
|
|
} else {
|
|
|
nonAggregatableIndices = null;
|
|
|
}
|
|
|
+ final Function<Map.Entry<String, Set<String>>, Set<String>> entryValueFunction = Map.Entry::getValue;
|
|
|
+ Map<String, Set<String>> immutableMeta = meta.entrySet().stream()
|
|
|
+ .collect(Collectors.toUnmodifiableMap(
|
|
|
+ Map.Entry::getKey, entryValueFunction.andThen(Set::copyOf)));
|
|
|
return new FieldCapabilities(name, type, isSearchable, isAggregatable,
|
|
|
- indices, nonSearchableIndices, nonAggregatableIndices);
|
|
|
+ indices, nonSearchableIndices, nonAggregatableIndices, immutableMeta);
|
|
|
}
|
|
|
}
|
|
|
|