summaryrefslogtreecommitdiffstats
path: root/container-search/src/main/java/com/yahoo/search/schema
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@vespa.ai>2023-06-12 15:14:11 +0200
committerJon Bratseth <bratseth@vespa.ai>2023-06-12 15:14:11 +0200
commit2092f374fc4f3de42da75f4660849af0f00f7a81 (patch)
tree1047036d19f30481925ab38b0601a85000121ad3 /container-search/src/main/java/com/yahoo/search/schema
parent537d80f3aad6351322ded0f3e300722cbcdba5d7 (diff)
Move to SchemaInfo
Add the missing constructs to SchemaInfo to be able to use it in place of IndexFacts for validation, and rewrite QueryValidator to use it. The new validation (for prefix search on indexes) is disabled until this is verified, so this should be a no-op.
Diffstat (limited to 'container-search/src/main/java/com/yahoo/search/schema')
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/Cluster.java79
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/DocumentSummary.java5
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/Field.java177
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/FieldInfo.java27
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/FieldSet.java102
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/RankProfile.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/Schema.java45
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/SchemaInfo.java93
-rw-r--r--container-search/src/main/java/com/yahoo/search/schema/SchemaInfoConfigurer.java16
9 files changed, 512 insertions, 34 deletions
diff --git a/container-search/src/main/java/com/yahoo/search/schema/Cluster.java b/container-search/src/main/java/com/yahoo/search/schema/Cluster.java
new file mode 100644
index 00000000000..f5ea4fdffc7
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/schema/Cluster.java
@@ -0,0 +1,79 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.schema;
+
+import com.yahoo.api.annotations.Beta;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Information about the search aspects of a content cluster.
+ *
+ * @author bratseth
+ */
+@Beta
+public class Cluster {
+
+ private final String name;
+ private final boolean isStreaming;
+ private final Set<String> schemas;
+
+ private Cluster(Builder builder) {
+ this.name = builder.name;
+ this.isStreaming = builder.isStreaming;
+ this.schemas = Set.copyOf(builder.schemas);
+ }
+
+ public String name() { return name; }
+
+ /** Returns true if this cluster uses streaming search. */
+ public boolean isStreaming() { return isStreaming; }
+
+ /** Returns the names of the subset of all schemas that are present in this cluster. */
+ public Set<String> schemas() { return schemas; }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Cluster other)) return false;
+ if ( ! this.name.equals(other.name)) return false;
+ if ( this.isStreaming != other.isStreaming()) return false;
+ if ( ! this.schemas.equals(other.schemas)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, isStreaming, schemas);
+ }
+
+ @Override
+ public String toString() { return "cluster '" + name + "'"; }
+
+ public static class Builder {
+
+ private final String name;
+ private boolean isStreaming = false;
+ private final Set<String> schemas = new HashSet<>();
+
+ public Builder(String name) {
+ this.name = name;
+ }
+
+ public Builder setStreaming(boolean isStreaming) {
+ this.isStreaming = isStreaming;
+ return this;
+ }
+
+ public Builder addSchema(String schema) {
+ schemas.add(schema);
+ return this;
+ }
+
+ public Cluster build() {
+ return new Cluster(this);
+ }
+
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/schema/DocumentSummary.java b/container-search/src/main/java/com/yahoo/search/schema/DocumentSummary.java
index 0aec6b0a4f6..a4f208710a0 100644
--- a/container-search/src/main/java/com/yahoo/search/schema/DocumentSummary.java
+++ b/container-search/src/main/java/com/yahoo/search/schema/DocumentSummary.java
@@ -1,11 +1,11 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.schema;
-import java.util.ArrayList;
+import com.yahoo.api.annotations.Beta;
+
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -15,6 +15,7 @@ import java.util.Objects;
*
* @author bratseth
*/
+@Beta
public class DocumentSummary {
private final String name;
diff --git a/container-search/src/main/java/com/yahoo/search/schema/Field.java b/container-search/src/main/java/com/yahoo/search/schema/Field.java
new file mode 100644
index 00000000000..ad949d5bad9
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/schema/Field.java
@@ -0,0 +1,177 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.schema;
+
+import com.yahoo.api.annotations.Beta;
+import com.yahoo.tensor.TensorType;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A field in a schema.
+ *
+ * @author bratseth
+ */
+@Beta
+public class Field implements FieldInfo {
+
+ private final String name;
+ private final Type type;
+ private final boolean isAttribute;
+ private final boolean isIndex;
+ private final Set<String> aliases;
+
+ public Field(Builder builder) {
+ this.name = builder.name;
+ this.type = builder.type;
+ this.isAttribute = builder.isAttribute;
+ this.isIndex = builder.isIndex;
+ this.aliases = Set.copyOf(builder.aliases);
+ }
+
+ @Override
+ public String name() { return name; }
+
+ @Override
+ public Type type() { return type; }
+
+ public Set<String> aliases() { return aliases; }
+
+ /** Returns whether this field is an attribute, i.e. does indexing: attribute. */
+ @Override
+ public boolean isAttribute() { return isAttribute; }
+
+ /** Returns whether this field is an index, i.e. does indexing: index. */
+ @Override
+ public boolean isIndex() { return isIndex; }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Field other)) return false;
+ if ( ! this.name.equals(other.name)) return false;
+ if ( this.isAttribute != other.isAttribute) return false;
+ if ( this.isIndex != other.isIndex) return false;
+ if ( ! this.aliases.equals(other.aliases)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, type, isAttribute, isIndex, aliases);
+ }
+
+ @Override
+ public String toString() { return "field '" + name + "'"; }
+
+ public static class Type {
+
+ private final Kind kind;
+
+ /** The kind of type this is. */
+ public enum Kind {
+ ANNOTATIONREFERENCE, ARRAY, BOOL, BYTE, DOUBLE, FLOAT, INT, LONG, MAP, POSITION, PREDICATE, RAW, REFERENCE, STRING, STRUCT, TENSOR, URL, WEIGHTEDSET;
+ }
+
+ private Type(Kind kind) {
+ this.kind = kind;
+ }
+
+ /**
+ * Returns the kind of type this is.
+ * Structured types have additional information in the subclass specific to that kind of type.
+ */
+ public Kind kind() { return kind; }
+
+ /** Creates this from a type string on the syntax following "field [name] type " in a schema definition. */
+ public static Type from(String typeString) {
+ if (typeString.startsWith("annotationreference<"))
+ return new Type(Kind.ANNOTATIONREFERENCE); // TODO: Model as subclass
+ if (typeString.startsWith("array<"))
+ return new Type(Kind.ARRAY); // TODO: Model as subclass
+ if (typeString.equals("bool"))
+ return new Type(Kind.BOOL);
+ if (typeString.equals("byte"))
+ return new Type(Kind.BYTE);
+ if (typeString.equals("double"))
+ return new Type(Kind.DOUBLE);
+ if (typeString.equals("float"))
+ return new Type(Kind.FLOAT);
+ if (typeString.equals("int"))
+ return new Type(Kind.INT);
+ if (typeString.equals("long"))
+ return new Type(Kind.LONG);
+ if (typeString.startsWith("map<"))
+ return new Type(Kind.MAP); // TODO: Model as subclass
+ if (typeString.equals("position"))
+ return new Type(Kind.POSITION);
+ if (typeString.equals("predicate"))
+ return new Type(Kind.PREDICATE);
+ if (typeString.equals("raw"))
+ return new Type(Kind.RAW);
+ if (typeString.startsWith("reference<"))
+ return new Type(Kind.REFERENCE); // TODO: Model as subclass
+ if (typeString.equals("string"))
+ return new Type(Kind.STRING);
+ if (typeString.startsWith("tensor<") || typeString.startsWith("tensor("))
+ return new TensorFieldType(TensorType.fromSpec(typeString));
+ if (typeString.equals("url"))
+ return new Type(Kind.URL);
+ if (typeString.startsWith("weightedset<"))
+ return new Type(Kind.WEIGHTEDSET); // TODO: Model as subclass
+ else
+ return new Type(Kind.STRUCT); // TODO: Model as a subclass
+ }
+
+ }
+
+ public static class TensorFieldType extends Type {
+
+ private final TensorType tensorType;
+
+ public TensorFieldType(TensorType tensorType) {
+ super(Kind.TENSOR);
+ this.tensorType = tensorType;
+ }
+
+ public TensorType tensorType() { return tensorType; }
+
+ }
+
+ public static class Builder {
+
+ private final String name;
+ private final Type type;
+ private final Set<String> aliases = new HashSet<>();
+ private boolean isAttribute;
+ private boolean isIndex;
+
+ public Builder(String name, String typeString) {
+ this.name = name;
+ this.type = Type.from(typeString);
+ }
+
+ public Builder addAlias(String alias) {
+ aliases.add(alias);
+ return this;
+ }
+
+ public Builder setAttribute(boolean isAttribute) {
+ this.isAttribute = isAttribute;
+ return this;
+ }
+
+ public Builder setIndex(boolean isIndex) {
+ this.isIndex = isIndex;
+ return this;
+ }
+
+ public Field build() {
+ return new Field(this);
+ }
+
+ }
+
+
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/schema/FieldInfo.java b/container-search/src/main/java/com/yahoo/search/schema/FieldInfo.java
new file mode 100644
index 00000000000..c3f6f22f1b9
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/schema/FieldInfo.java
@@ -0,0 +1,27 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.schema;
+
+import com.yahoo.api.annotations.Beta;
+
+import java.util.Set;
+
+/**
+ * Information about a field or field set.
+ *
+ * @author bratseth
+ */
+@Beta
+public interface FieldInfo {
+
+ /** Returns the name of this field or field set. */
+ String name();
+
+ Field.Type type();
+
+ /** Returns whether this field or field set is attribute(s), i.e. does indexing: attribute. */
+ boolean isAttribute();
+
+ /** Returns whether this field is index(es), i.e. does indexing: index. */
+ boolean isIndex();
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/schema/FieldSet.java b/container-search/src/main/java/com/yahoo/search/schema/FieldSet.java
new file mode 100644
index 00000000000..33c57dd1238
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/schema/FieldSet.java
@@ -0,0 +1,102 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.schema;
+
+import com.yahoo.api.annotations.Beta;
+
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A set of fields which can be queried as one.
+ *
+ * @author bratseth
+ */
+@Beta
+public class FieldSet implements FieldInfo {
+
+ private final String name;
+ private final Set<String> fieldNames;
+
+ // Assigned when this is added to a schema
+ private Schema schema = null;
+
+ private FieldSet(Builder builder) {
+ this.name = builder.name;
+ this.fieldNames = Set.copyOf(builder.fieldNames);
+ }
+
+ @Override
+ public String name() { return name; }
+
+ @Override
+ public Field.Type type() {
+ if (schema == null || fieldNames.isEmpty()) return null;
+ return randomFieldInThis().type();
+ }
+
+ /** Returns whether this field or field set is attribute(s), i.e. does indexing: attribute. */
+ @Override
+ public boolean isAttribute() {
+ if (schema == null || fieldNames.isEmpty()) return false;
+ return randomFieldInThis().isAttribute();
+ }
+
+ /** Returns whether this field is index(es), i.e. does indexing: index. */
+ @Override
+ public boolean isIndex() {
+ if (schema == null || fieldNames.isEmpty()) return false;
+ return randomFieldInThis().isIndex();
+ }
+
+ void setSchema(Schema schema) {
+ if ( this.schema != null)
+ throw new IllegalStateException("Cannot add field set '" + name + "' to schema '" + schema.name() +
+ "' as it is already added to schema '" + this.schema.name() + "'");
+ this.schema = schema;
+ }
+
+ /** Use a random field in this to determine its properties. Any inconsistency will have been warned about on deploy. */
+ private Field randomFieldInThis() {
+ return schema.fields().get(fieldNames.iterator().next());
+ }
+
+ public Set<String> fieldNames() { return fieldNames; }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof FieldSet other)) return false;
+ if ( ! this.name.equals(other.name)) return false;
+ if ( ! this.fieldNames.equals(other.fieldNames)) return false;
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, fieldNames);
+ }
+
+ @Override
+ public String toString() { return "field set '" + name + "'"; }
+
+ public static class Builder {
+
+ private final String name;
+ private final Set<String> fieldNames = new HashSet<>();
+
+ public Builder(String name) {
+ this.name = name;
+ }
+
+ public Builder addField(String fieldName) {
+ fieldNames.add(fieldName);
+ return this;
+ }
+
+ public FieldSet build() {
+ return new FieldSet(this);
+ }
+
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/schema/RankProfile.java b/container-search/src/main/java/com/yahoo/search/schema/RankProfile.java
index 85bb3915975..5eba6c220bb 100644
--- a/container-search/src/main/java/com/yahoo/search/schema/RankProfile.java
+++ b/container-search/src/main/java/com/yahoo/search/schema/RankProfile.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.search.schema;
+import com.yahoo.api.annotations.Beta;
import com.yahoo.tensor.TensorType;
import java.util.Collections;
@@ -14,6 +15,7 @@ import java.util.Objects;
*
* @author bratseth
*/
+@Beta
public class RankProfile {
private final String name;
diff --git a/container-search/src/main/java/com/yahoo/search/schema/Schema.java b/container-search/src/main/java/com/yahoo/search/schema/Schema.java
index c20aa1e81bd..20a776dc53e 100644
--- a/container-search/src/main/java/com/yahoo/search/schema/Schema.java
+++ b/container-search/src/main/java/com/yahoo/search/schema/Schema.java
@@ -8,6 +8,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
/**
* Information about a schema which is part of the application running this.
@@ -20,25 +21,57 @@ import java.util.Objects;
public class Schema {
private final String name;
+ private final Map<String, Field> fields;
+ private final Map<String, FieldSet> fieldSets;
private final Map<String, RankProfile> rankProfiles;
private final Map<String, DocumentSummary> documentSummaries;
+ /** Fields indexed by both name and aliases. */
+ private final Map<String, Field> fieldsByAliases;
+
private Schema(Builder builder) {
this.name = builder.name;
+ this.fields = Collections.unmodifiableMap(builder.fields);
+ this.fieldSets = Collections.unmodifiableMap(builder.fieldSets);
this.rankProfiles = Collections.unmodifiableMap(builder.rankProfiles);
this.documentSummaries = Collections.unmodifiableMap(builder.documentSummaries);
+
+ fieldSets.values().forEach(fieldSet -> fieldSet.setSchema(this));
rankProfiles.values().forEach(rankProfile -> rankProfile.setSchema(this));
+
+ fieldsByAliases = new HashMap<>();
+ for (Field field : fields.values()) {
+ fieldsByAliases.put(field.name(), field);
+ field.aliases().forEach(alias -> fieldsByAliases.put(alias, field));
+ }
}
public String name() { return name; }
+ public Map<String, Field> fields() { return fields; }
public Map<String, RankProfile> rankProfiles() { return rankProfiles; }
public Map<String, DocumentSummary> documentSummaries() { return documentSummaries; }
+ /**
+ * Looks up a field or field set by the given name or alias in this schema.
+ *
+ * @param fieldName the name or alias of the field or field set. If this is empty, the name "default" is looked up
+ * @return information about the field or field set with the given name, or empty if no item with this name exists
+ */
+ public Optional<FieldInfo> fieldInfo(String fieldName) {
+ if (fieldName.isEmpty())
+ fieldName = "default";
+ Field field = fieldsByAliases.get(fieldName);
+ if (field != null) return Optional.of(field);
+ return Optional.ofNullable(fieldSets.get(fieldName));
+ }
+
@Override
public boolean equals(Object o) {
if (o == this) return true;
if ( ! (o instanceof Schema other)) return false;
if ( ! other.name.equals(this.name)) return false;
+ if ( ! other.fields.equals(this.fields)) return false;
+ if ( ! other.fieldSets.equals(this.fieldSets)) return false;
if ( ! other.rankProfiles.equals(this.rankProfiles)) return false;
if ( ! other.documentSummaries.equals(this.documentSummaries)) return false;
return true;
@@ -57,6 +90,8 @@ public class Schema {
public static class Builder {
private final String name;
+ private final Map<String, Field> fields = new LinkedHashMap<>();
+ private final Map<String, FieldSet> fieldSets = new LinkedHashMap<>();
private final Map<String, RankProfile> rankProfiles = new LinkedHashMap<>();
private final Map<String, DocumentSummary> documentSummaries = new LinkedHashMap<>();
@@ -64,6 +99,16 @@ public class Schema {
this.name = Objects.requireNonNull(name);
}
+ public Builder add(Field field) {
+ fields.put(field.name(), field);
+ return this;
+ }
+
+ public Builder add(FieldSet fieldSet) {
+ fieldSets.put(fieldSet.name(), fieldSet);
+ return this;
+ }
+
public Builder add(RankProfile profile) {
rankProfiles.put(profile.name(), profile);
return this;
diff --git a/container-search/src/main/java/com/yahoo/search/schema/SchemaInfo.java b/container-search/src/main/java/com/yahoo/search/schema/SchemaInfo.java
index d29964ea9c5..71bb00b39c5 100644
--- a/container-search/src/main/java/com/yahoo/search/schema/SchemaInfo.java
+++ b/container-search/src/main/java/com/yahoo/search/schema/SchemaInfo.java
@@ -17,6 +17,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
@@ -35,37 +36,38 @@ import java.util.stream.Collectors;
*/
// NOTES:
// This should replace IndexFacts, and probably DocumentDatabase.
-// It replicates the schema resolution mechanism in IndexFacts, but does not yet contain any field information.
-// To replace IndexFacts, this must accept IndexInfo and expose that information, as well as consolidation
-// given a set of possible schemas: The session mechanism is present here to make that efficient when added
-// (resolving schema subsets for every field lookup is too expensive).
+// It replicates the schema resolution mechanism in IndexFacts, but does not yet contain complete field information.
@Beta
public class SchemaInfo {
- private static final SchemaInfo empty = new SchemaInfo(List.of(), Map.of());
+ private static final SchemaInfo empty = new SchemaInfo(List.of(), List.of());
private final Map<String, Schema> schemas;
- /** The schemas contained in each content cluster indexed by cluster name */
- private final Map<String, List<String>> clusters;
+ private final Map<String, Cluster> clusters;
@Inject
- public SchemaInfo(IndexInfoConfig indexInfo, // will be used in the future
- SchemaInfoConfig schemaInfoConfig,
+ public SchemaInfo(SchemaInfoConfig schemaInfoConfig,
QrSearchersConfig qrSearchersConfig) {
this(SchemaInfoConfigurer.toSchemas(schemaInfoConfig), SchemaInfoConfigurer.toClusters(qrSearchersConfig));
}
- public SchemaInfo(List<Schema> schemas, Map<String, List<String>> clusters) {
+ public SchemaInfo(List<Schema> schemas, List<Cluster> clusters) {
Map<String, Schema> schemaMap = new LinkedHashMap<>();
schemas.forEach(schema -> schemaMap.put(schema.name(), schema));
this.schemas = Collections.unmodifiableMap(schemaMap);
- this.clusters = Collections.unmodifiableMap(clusters);
+
+ Map<String, Cluster> clusterMap = new LinkedHashMap<>();
+ clusters.forEach(cluster -> clusterMap.put(cluster.name(), cluster));
+ this.clusters = Collections.unmodifiableMap(clusterMap);
}
/** Returns all schemas configured in this application, indexed by schema name. */
public Map<String, Schema> schemas() { return schemas; }
+ /** Returns information about all clusters available for searching in this applications, indexed by cluyster name. */
+ public Map<String, Cluster> clusters() { return clusters; }
+
public Session newSession(Query query) {
return new Session(query.getModel().getSources(), query.getModel().getRestrict(), clusters, schemas);
}
@@ -75,8 +77,7 @@ public class SchemaInfo {
@Override
public boolean equals(Object o) {
if (o == this) return true;
- if ( ! (o instanceof SchemaInfo)) return false;
- SchemaInfo other = (SchemaInfo)o;
+ if ( ! (o instanceof SchemaInfo other)) return false;
if ( ! other.schemas.equals(this.schemas)) return false;
if ( ! other.clusters.equals(this.clusters)) return false;
return true;
@@ -88,15 +89,61 @@ public class SchemaInfo {
/** The schema information resolved to be relevant to this session. */
public static class Session {
+ private final boolean isStreaming;
private final Collection<Schema> schemas;
private Session(Set<String> sources,
Set<String> restrict,
- Map<String, List<String>> clusters,
+ Map<String, Cluster> clusters,
Map<String, Schema> candidates) {
+ this.isStreaming = resolveStreaming(sources, clusters);
this.schemas = resolveSchemas(sources, restrict, clusters, candidates.values());
}
+ /** Returns true if this only searches streaming clusters. */
+ public boolean isStreaming() { return isStreaming; }
+
+ /**
+ * Looks up a field or field set by the given name or alias
+ * in the schemas resolved for this query.
+ *
+ * If there are several fields or field sets by this name or alias across the schemas of this session,
+ * one is chosen by random.
+ *
+ * @param fieldName the name or alias of the field or field set. If this is empty, the name "default" is looked up.
+ * @return the appropriate field or empty if no field or field set has this name or alias
+ */
+ public Optional<FieldInfo> fieldInfo(String fieldName) {
+ for (var schema : schemas) {
+ Optional<FieldInfo> field = schema.fieldInfo(fieldName);
+ if (field.isPresent())
+ return field;
+ }
+ return Optional.empty();
+ }
+
+ private static boolean resolveStreaming(Set<String> sources, Map<String, Cluster> clusters) {
+ if (sources.isEmpty()) return clusters.values().stream().allMatch(Cluster::isStreaming);
+
+ var matchedClusters = sources.stream().map(source -> clusterOfSource(source, clusters)).filter(Objects::nonNull).toList();
+ if (matchedClusters.isEmpty()) return false;
+ return matchedClusters.stream().allMatch(Cluster::isStreaming);
+ }
+
+ /**
+ * A source name is either a cluster or a schema.
+ * Returns the cluster which either is or contains this name, if any.
+ */
+ private static Cluster clusterOfSource(String source, Map<String, Cluster> clusters) {
+ var cluster = clusters.get(source);
+ if (cluster != null) return cluster;
+ for (var c : clusters.values()) {
+ if (c.schemas().contains(source))
+ return c;
+ }
+ return null;
+ }
+
/**
* Given a search list which is a mixture of schemas and cluster
* names, and a restrict list which is a list of schemas, return a
@@ -106,7 +153,7 @@ public class SchemaInfo {
*/
private static Collection<Schema> resolveSchemas(Set<String> sources,
Set<String> restrict,
- Map<String, List<String>> clusters,
+ Map<String, Cluster> clusters,
Collection<Schema> candidates) {
if (sources.isEmpty())
return restrict.isEmpty() ? candidates : keep(restrict, candidates);
@@ -114,7 +161,7 @@ public class SchemaInfo {
Set<String> schemaNames = new HashSet<>();
for (String source : sources) {
if (clusters.containsKey(source)) // source is a cluster
- schemaNames.addAll(clusters.get(source));
+ schemaNames.addAll(clusters.get(source).schemas());
else // source is a schema
schemaNames.add(source);
}
@@ -126,13 +173,6 @@ public class SchemaInfo {
return schemas.stream().filter(schema -> names.contains(schema.name())).toList();
}
- private List<RankProfile> profilesNamed(String name) {
- return schemas.stream()
- .filter(schema -> schema.rankProfiles().containsKey(name))
- .map(schema -> schema.rankProfiles().get(name))
- .toList();
- }
-
/**
* Returns the type of the given rank feature name in the given profile,
* if it can be uniquely determined.
@@ -165,6 +205,13 @@ public class SchemaInfo {
return foundType;
}
+ private List<RankProfile> profilesNamed(String name) {
+ return schemas.stream()
+ .filter(schema -> schema.rankProfiles().containsKey(name))
+ .map(schema -> schema.rankProfiles().get(name))
+ .toList();
+ }
+
}
}
diff --git a/container-search/src/main/java/com/yahoo/search/schema/SchemaInfoConfigurer.java b/container-search/src/main/java/com/yahoo/search/schema/SchemaInfoConfigurer.java
index 6947a93a833..1b9ba397105 100644
--- a/container-search/src/main/java/com/yahoo/search/schema/SchemaInfoConfigurer.java
+++ b/container-search/src/main/java/com/yahoo/search/schema/SchemaInfoConfigurer.java
@@ -6,10 +6,7 @@ import com.yahoo.search.config.SchemaInfoConfig;
import com.yahoo.tensor.TensorType;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
/**
* Translation between schema info configuration and schema objects.
@@ -47,14 +44,15 @@ class SchemaInfoConfigurer {
return builder.build();
}
- static Map<String, List<String>> toClusters(QrSearchersConfig config) {
- Map<String, List<String>> clusters = new HashMap<>();
+ static List<Cluster> toClusters(QrSearchersConfig config) {
+ List<Cluster> clusters = new ArrayList<>();
for (int i = 0; i < config.searchcluster().size(); ++i) {
- List<String> schemas = new ArrayList<>();
String clusterName = config.searchcluster(i).name();
- for (int j = 0; j < config.searchcluster(i).searchdef().size(); ++j)
- schemas.add(config.searchcluster(i).searchdef(j));
- clusters.put(clusterName, schemas);
+ var clusterInfo = new Cluster.Builder(clusterName);
+ clusterInfo.setStreaming(config.searchcluster(i).indexingmode() == QrSearchersConfig.Searchcluster.Indexingmode.Enum.STREAMING);
+ for (var schemaDef : config.searchcluster(i).searchdef())
+ clusterInfo.addSchema(schemaDef);
+ clusters.add(clusterInfo.build());
}
return clusters;
}