aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@oath.com>2021-01-10 21:57:53 +0100
committerGitHub <noreply@github.com>2021-01-10 21:57:53 +0100
commit00d005ea8544ee5677c2f18d5358cf65afebbf32 (patch)
treecd53c27556b11fcc0ce1ef37c4ca77bcd406a4b5
parent01c884d143d1f9ee996a56d58c8c539d087cd095 (diff)
parent472de63dd01cb24a576d76b46322e25d9b1aa5b7 (diff)
Merge pull request #15969 from vespa-engine/jonmv/faster-query-profile-compilation
Jonmv/faster query profile compilation
-rw-r--r--container-search/abi-spec.json17
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java6
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/CompoundNameChildCache.java24
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java111
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/DimensionValues.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/PrefixQueryProfileVisitor.java26
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java10
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java125
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/Binding.java2
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalMap.java11
-rw-r--r--container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalValue.java14
-rw-r--r--processing/src/main/java/com/yahoo/processing/request/CompoundName.java4
12 files changed, 220 insertions, 132 deletions
diff --git a/container-search/abi-spec.json b/container-search/abi-spec.json
index e2285a10672..705318cb8de 100644
--- a/container-search/abi-spec.json
+++ b/container-search/abi-spec.json
@@ -5621,6 +5621,19 @@
],
"fields": []
},
+ "com.yahoo.search.query.profile.CompoundNameChildCache": {
+ "superClass": "java.lang.Object",
+ "interfaces": [],
+ "attributes": [
+ "public",
+ "final"
+ ],
+ "methods": [
+ "public void <init>()",
+ "public com.yahoo.processing.request.CompoundName append(com.yahoo.processing.request.CompoundName, java.lang.String)"
+ ],
+ "fields": []
+ },
"com.yahoo.search.query.profile.CopyOnWriteContent": {
"superClass": "com.yahoo.component.provider.FreezableClass",
"interfaces": [
@@ -6131,7 +6144,7 @@
],
"methods": [
"public void <init>()",
- "public void put(com.yahoo.processing.request.CompoundName, com.yahoo.search.query.profile.DimensionBinding, java.lang.Object)",
+ "public void put(com.yahoo.processing.request.CompoundName, com.yahoo.search.query.profile.compiled.Binding, java.lang.Object)",
"public com.yahoo.search.query.profile.compiled.DimensionalMap build()"
],
"fields": []
@@ -6158,7 +6171,7 @@
"methods": [
"public void <init>()",
"public java.lang.Object valueFor(com.yahoo.search.query.profile.compiled.Binding)",
- "public void add(java.lang.Object, com.yahoo.search.query.profile.DimensionBinding)",
+ "public void add(java.lang.Object, com.yahoo.search.query.profile.compiled.Binding)",
"public com.yahoo.search.query.profile.compiled.DimensionalValue build(java.util.Map)"
],
"fields": []
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
index 68bf112133a..b24bf1195eb 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/AllValuesQueryProfileVisitor.java
@@ -16,8 +16,8 @@ final class AllValuesQueryProfileVisitor extends PrefixQueryProfileVisitor {
private final Map<String, ValueWithSource> values = new HashMap<>();
/* Lists all values starting at prefix */
- public AllValuesQueryProfileVisitor(CompoundName prefix) {
- super(prefix);
+ public AllValuesQueryProfileVisitor(CompoundName prefix, CompoundNameChildCache pathCache) {
+ super(prefix, pathCache);
}
@Override
@@ -43,7 +43,7 @@ final class AllValuesQueryProfileVisitor extends PrefixQueryProfileVisitor {
QueryProfile owner,
DimensionValues variant,
DimensionBinding binding) {
- CompoundName fullName = currentPrefix.append(key);
+ CompoundName fullName = cache.append(currentPrefix, key);
ValueWithSource existing = values.get(fullName.toString());
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/CompoundNameChildCache.java b/container-search/src/main/java/com/yahoo/search/query/profile/CompoundNameChildCache.java
new file mode 100644
index 00000000000..4163e45ae61
--- /dev/null
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/CompoundNameChildCache.java
@@ -0,0 +1,24 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.search.query.profile;
+
+import com.yahoo.processing.request.CompoundName;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Cache for compound names created through {@link CompoundName#append(String)}.
+ * Creating new {@link CompoundName}s can be expensive, and since they are immutable, they
+ * are safe to cache and reuse. Use this if you will create <em>a lot</em> of them, by appending suffixes.
+ *
+ * @author jonmv
+ */
+public final class CompoundNameChildCache {
+
+ private final Map<CompoundName, Map<String, CompoundName>> cache = new HashMap<>();
+
+ public CompoundName append(CompoundName prefix, String suffix) {
+ return cache.computeIfAbsent(prefix, __ -> new HashMap<>()).computeIfAbsent(suffix, prefix::append);
+ }
+
+}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
index e0edf9f9894..43462e8f327 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionBinding.java
@@ -2,10 +2,9 @@
package com.yahoo.search.query.profile;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* An immutable, binding of a list of dimensions to dimension values
@@ -24,13 +23,13 @@ public class DimensionBinding {
private Map<String, String> context;
public static final DimensionBinding nullBinding =
- new DimensionBinding(Collections.unmodifiableList(Collections.emptyList()), DimensionValues.empty, null);
+ new DimensionBinding(List.of(), DimensionValues.empty, null);
public static final DimensionBinding invalidBinding =
- new DimensionBinding(Collections.unmodifiableList(Collections.emptyList()), DimensionValues.empty, null);
+ new DimensionBinding(List.of(), DimensionValues.empty, null);
/** Whether the value array contains only nulls */
- private boolean containsAllNulls;
+ private final boolean containsAllNulls;
// NOTE: Map must be ordered
public static DimensionBinding createFrom(Map<String, String> values) {
@@ -125,72 +124,54 @@ public class DimensionBinding {
*
* @return the combined binding, or the special invalidBinding if these two bindings are incompatible
*/
- public DimensionBinding combineWith(DimensionBinding binding) {
- List<String> combinedDimensions = combineDimensions(getDimensions(), binding.getDimensions());
- if (combinedDimensions == null) return invalidBinding;
-
- // not runtime, so assume we don't need to preserve values outside the dimensions
- Map<String, String> combinedValues = combineValues(getContext(), binding.getContext());
- if (combinedValues == null) return invalidBinding;
-
- return DimensionBinding.createFrom(combinedDimensions, combinedValues);
- }
-
- /**
- * Returns a combined list of dimensions from two separate lists,
- * or null if they are incompatible.
- * This is to combine two lists to one such that the partial order in both is preserved
- * (or return null if impossible).
- */
- private List<String> combineDimensions(List<String> d1, List<String> d2) {
- if (d1.equals(d2)) return d1;
- if (d1.isEmpty()) return d2;
- if (d2.isEmpty()) return d1;
-
- List<String> combined = new ArrayList<>();
- int d1Index = 0, d2Index = 0;
- while (d1Index < d1.size() && d2Index < d2.size()) {
- if (d1.get(d1Index).equals(d2.get(d2Index))) { // agreement on next element
- combined.add(d1.get(d1Index));
- d1Index++;
- d2Index++;
+ public DimensionBinding combineWith(DimensionBinding other) {
+ List<String> d1 = getDimensions();
+ List<String> d2 = other.getDimensions();
+ DimensionValues v1 = getValues();
+ DimensionValues v2 = other.getValues();
+ List<String> dimensions = new ArrayList<>();
+ List<String> values = new ArrayList<>();
+ int i1 = 0, i2 = 0;
+ while (i1 < d1.size() && i2 < d2.size()) {
+ if (d1.get(i1).equals(d2.get(i2))) { // agreement on next dimension
+ String s1 = v1.get(i1), s2 = v2.get(i2);
+ if (s1 == null)
+ values.add(s2);
+ else if (s2 == null || s1.equals(s2))
+ values.add(s1);
+ else
+ return invalidBinding; // disagreement on next value
+
+ dimensions.add(d1.get(i1));
+ i1++;
+ i2++;
}
- else if ( ! d2.contains(d1.get(d1Index))) { // next in d1 is independent from d2
- combined.add(d1.get(d1Index++));
+ else if ( ! d2.contains(d1.get(i1))) { // next dimension in d1 is independent from d2
+ dimensions.add(d1.get(i1));
+ values.add(v1.get(i1));
+ i1++;
}
- else if ( ! d1.contains(d2.get(d2Index))) { // next in d2 is independent from d1
- combined.add(d2.get(d2Index++));
+ else if ( ! d1.contains(d2.get(i2))) { // next dimension in d2 is independent from d1
+ dimensions.add(d2.get(i2));
+ values.add(v2.get(i2));
+ i2++;
}
else {
- return null; // not independent and no agreement
+ return invalidBinding; // not independent and no agreement
}
}
- if (d1Index < d1.size())
- combined.addAll(d1.subList(d1Index, d1.size()));
- else if (d2Index < d2.size())
- combined.addAll(d2.subList(d2Index, d2.size()));
-
- return combined;
- }
-
- /**
- * Returns a combined map of dimension values from two separate maps,
- * or null if they are incompatible.
- */
- private Map<String, String> combineValues(Map<String, String> m1, Map<String, String> m2) {
- if (m1.isEmpty()) return m2;
- if (m2.isEmpty()) return m1;
- Map<String, String> combinedValues = null;
- for (Map.Entry<String, String> m2Entry : m2.entrySet()) {
- if (m2Entry.getValue() == null) continue;
- String m1Value = m1.get(m2Entry.getKey());
- if (m1Value != null && ! m1Value.equals(m2Entry.getValue()))
- return null; // conflicting values of a key
- if (combinedValues == null)
- combinedValues = new LinkedHashMap<>(m1);
- combinedValues.put(m2Entry.getKey(), m2Entry.getValue());
+ while (i1 < d1.size()) {
+ dimensions.add(d1.get(i1));
+ values.add(v1.get(i1));
+ i1++;
+ }
+ while (i2 < d2.size()) {
+ dimensions.add(d2.get(i2));
+ values.add(v2.get(i2));
+ i2++;
}
- return combinedValues == null ? m1 : combinedValues;
+
+ return DimensionBinding.createFrom(dimensions, DimensionValues.createFrom(values.toArray(new String[0])));
}
/** Returns true if this == invalidBinding */
@@ -223,7 +204,7 @@ public class DimensionBinding {
@Override
public int hashCode() {
- return dimensions.hashCode() + 17 * values.hashCode();
+ return Objects.hash(dimensions, values);
}
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionValues.java b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionValues.java
index 7c3307223c3..f3c4548c491 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/DimensionValues.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/DimensionValues.java
@@ -80,7 +80,7 @@ public class DimensionValues implements Comparable<DimensionValues> {
public boolean equals(Object o) {
if (this == o) return true;
if ( ! (o instanceof DimensionValues)) return false;
- DimensionValues other = (DimensionValues)o;
+ DimensionValues other = (DimensionValues) o;
for (int i = 0; i < this.size() || i < other.size(); i++) {
if (get(i) == null) {
if (other.get(i) != null) return false;
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/PrefixQueryProfileVisitor.java b/container-search/src/main/java/com/yahoo/search/query/profile/PrefixQueryProfileVisitor.java
index 690a48f8124..b53fc4f96f2 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/PrefixQueryProfileVisitor.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/PrefixQueryProfileVisitor.java
@@ -3,6 +3,11 @@ package com.yahoo.search.query.profile;
import com.yahoo.processing.request.CompoundName;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* A query profile visitor which keeps track of name prefixes and can skip values outside a given prefix
*
@@ -10,18 +15,22 @@ import com.yahoo.processing.request.CompoundName;
*/
abstract class PrefixQueryProfileVisitor extends QueryProfileVisitor {
+ protected final CompoundNameChildCache cache;
+
/** Only call onValue/onQueryProfile for nodes having this prefix */
private final CompoundName prefix;
/** The current prefix, relative to prefix. */
protected CompoundName currentPrefix = CompoundName.empty;
+ private final Deque<CompoundName> currentPrefixes = new ArrayDeque<>();
private int prefixComponentIndex = -1;
- public PrefixQueryProfileVisitor(CompoundName prefix) {
+ public PrefixQueryProfileVisitor(CompoundName prefix, CompoundNameChildCache cache) {
if (prefix == null)
prefix = CompoundName.empty;
this.prefix = prefix;
+ this.cache = cache;
}
@Override
@@ -40,18 +49,19 @@ abstract class PrefixQueryProfileVisitor extends QueryProfileVisitor {
@Override
public final boolean enter(String name) {
- prefixComponentIndex++;
- if (prefixComponentIndex-1 < prefix.size()) return true; // we're in the given prefix, which should not be included in the name
- currentPrefix = currentPrefix.append(name);
+ if (prefixComponentIndex++ < prefix.size()) return true; // we're in the given prefix, which should not be included in the name
+ if ( ! name.isEmpty()) {
+ currentPrefixes.push(currentPrefix);
+ currentPrefix = cache.append(currentPrefix, name);
+ }
return true;
}
@Override
public final void leave(String name) {
- prefixComponentIndex--;
- if (prefixComponentIndex < prefix.size()) return; // we're in the given prefix, which should not be included in the name
- if ( ! name.isEmpty() && ! currentPrefix.isEmpty())
- currentPrefix = currentPrefix.first(currentPrefix.size() - 1);
+ if (--prefixComponentIndex < prefix.size()) return; // we're in the given prefix, which should not be included in the name
+ if ( ! name.isEmpty())
+ currentPrefix = currentPrefixes.pop();
}
/**
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
index be4a683d9d2..4371955ae63 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfile.java
@@ -18,7 +18,6 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -266,10 +265,13 @@ public class QueryProfile extends FreezableSimpleComponent implements Cloneable
}
AllValuesQueryProfileVisitor visitValues(CompoundName prefix, Map<String, String> context) {
- DimensionBinding dimensionBinding = DimensionBinding.createFrom(getDimensions(), context);
+ return visitValues(prefix, context, new CompoundNameChildCache());
+ }
- AllValuesQueryProfileVisitor visitor = new AllValuesQueryProfileVisitor(prefix);
- accept(visitor, dimensionBinding, null);
+ AllValuesQueryProfileVisitor visitValues(CompoundName prefix, Map<String, String> context,
+ CompoundNameChildCache pathCache) {
+ AllValuesQueryProfileVisitor visitor = new AllValuesQueryProfileVisitor(prefix, pathCache);
+ accept(visitor, DimensionBinding.createFrom(getDimensions(), context), null);
return visitor;
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
index 3010c6a9d09..29a997a75dd 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/QueryProfileCompiler.java
@@ -2,17 +2,24 @@
package com.yahoo.search.query.profile;
import com.yahoo.processing.request.CompoundName;
+import com.yahoo.search.query.profile.compiled.Binding;
import com.yahoo.search.query.profile.compiled.CompiledQueryProfile;
import com.yahoo.search.query.profile.compiled.CompiledQueryProfileRegistry;
import com.yahoo.search.query.profile.compiled.DimensionalMap;
import com.yahoo.search.query.profile.compiled.ValueWithSource;
import com.yahoo.search.query.profile.types.QueryProfileType;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.function.BiConsumer;
import java.util.logging.Logger;
-import java.util.stream.Collectors;
/**
* Compile a set of query profiles into compiled profiles.
@@ -42,17 +49,20 @@ public class QueryProfileCompiler {
variants.add(new DimensionBindingForPath(DimensionBinding.nullBinding, CompoundName.empty)); // if this contains no variants
log.fine(() -> "Compiling " + in + " having " + variants.size() + " variants");
- for (DimensionBindingForPath variant : variants) {
- log.finer(() -> " Compiling variant " + variant);
- for (Map.Entry<String, ValueWithSource> entry : in.visitValues(variant.path(), variant.binding().getContext()).valuesWithSource().entrySet()) {
- CompoundName fullName = variant.path().append(entry.getKey());
- values.put(fullName, variant.binding(), entry.getValue());
+ CompoundNameChildCache pathCache = new CompoundNameChildCache();
+ Map<DimensionBinding, Binding> bindingCache = new HashMap<>();
+ for (var variant : variants) {
+ log.finer(() -> "Compiling variant " + variant);
+ Binding variantBinding = bindingCache.computeIfAbsent(variant.binding(), Binding::createFrom);
+ for (var entry : in.visitValues(variant.path(), variant.binding().getContext(), pathCache).valuesWithSource().entrySet()) {
+ CompoundName fullName = pathCache.append(variant.path, entry.getKey());
+ values.put(fullName, variantBinding, entry.getValue());
if (entry.getValue().isUnoverridable())
- unoverridables.put(fullName, variant.binding(), Boolean.TRUE);
+ unoverridables.put(fullName, variantBinding, Boolean.TRUE);
if (entry.getValue().isQueryProfile())
- references.put(fullName, variant.binding(), Boolean.TRUE);
+ references.put(fullName, variantBinding, Boolean.TRUE);
if (entry.getValue().queryProfileType() != null)
- types.put(fullName, variant.binding(), entry.getValue().queryProfileType());
+ types.put(fullName, variantBinding, entry.getValue().queryProfileType());
}
}
@@ -96,17 +106,31 @@ public class QueryProfileCompiler {
* I.e if we have the variants [-,b=b1], [a=a1,-], [a=a2,-],
* this returns the variants [a=a1,b=b1], [a=a2,b=b1]
*
- * This is necessary because left-specified values takes precedence, such that resolving [a=a1,b=b1] would
- * lead us to the compiled profile [a=a1,-], which may contain default values for properties where
+ * This is necessary because left-specified values takes precedence, and resolving [a=a1,b=b1] would
+ * otherwise lead us to the compiled profile [a=a1,-], which may contain default values for properties where
* we should have preferred variant values in [-,b=b1].
*/
private static Set<DimensionBindingForPath> wildcardExpanded(Set<DimensionBindingForPath> variants) {
Set<DimensionBindingForPath> expanded = new HashSet<>();
- for (var variant : variants) {
- if (hasWildcardBeforeEnd(variant.binding()))
- expanded.addAll(wildcardExpanded(variant, variants));
- }
+ PathTree trie = new PathTree();
+ for (var variant : variants)
+ trie.add(variant);
+
+ // Visit all variant prefixes, grouped on path, and all their unique child bindings.
+ trie.forEachPrefixAndChildren((prefixes, childBindings) -> {
+ Set<DimensionBinding> processed = new HashSet<>();
+ for (DimensionBindingForPath prefix : prefixes)
+ if (processed.add(prefix.binding())) // Only compute once for similar bindings, since path is equal.
+ if (hasWildcardBeforeEnd(prefix.binding()))
+ for (DimensionBinding childBinding : childBindings)
+ if (childBinding != prefix.binding()) {
+ DimensionBinding combined = prefix.binding().combineWith(childBinding);
+ if ( ! combined.isInvalid())
+ expanded.add(new DimensionBindingForPath(combined, prefix.path()));
+ }
+ });
+
return expanded;
}
@@ -118,20 +142,6 @@ public class QueryProfileCompiler {
return false;
}
- private static Set<DimensionBindingForPath> wildcardExpanded(DimensionBindingForPath variantToExpand,
- Set<DimensionBindingForPath> variants) {
- Set<DimensionBindingForPath> expanded = new HashSet<>();
- for (var variant : variants) {
- if (variant.binding().isNull()) continue;
- if ( ! variant.path().hasPrefix(variantToExpand.path())) continue;
- DimensionBinding combined = variantToExpand.binding().combineWith(variant.binding());
- if ( ! combined.isInvalid() )
- expanded.add(new DimensionBindingForPath(combined, variantToExpand.path()));
- }
- return expanded;
- }
-
-
/** Generates a set of all the (legal) combinations of the variants in the given sets */
private static Set<DimensionBindingForPath> combined(Set<DimensionBindingForPath> v1s,
Set<DimensionBindingForPath> v2s) {
@@ -214,4 +224,61 @@ public class QueryProfileCompiler {
}
+
+ /**
+ * Simple trie for CompoundName paths.
+ *
+ * @author jonmv
+ */
+ static class PathTree {
+
+ private final Node root = new Node(0);
+
+ void add(DimensionBindingForPath entry) {
+ root.add(entry);
+ }
+
+ /** Performs action on sets of path prefixes against all their (common) children. */
+ void forEachPrefixAndChildren(BiConsumer<Collection<DimensionBindingForPath>, Collection<DimensionBinding>> action) {
+ root.visit(action);
+ }
+
+ private static class Node {
+
+ private final int depth;
+ private final SortedMap<String, Node> children = new TreeMap<>();
+ private final List<DimensionBindingForPath> elements = new ArrayList<>();
+
+ private Node(int depth) {
+ this.depth = depth;
+ }
+
+ /** Performs action on the elements of this against all child element bindings, then returns the union of these two sets. */
+ Set<DimensionBinding> visit(BiConsumer<Collection<DimensionBindingForPath>, Collection<DimensionBinding>> action) {
+ Set<DimensionBinding> allChildren = new HashSet<>();
+ for (Node child : children.values())
+ allChildren.addAll(child.visit(action));
+
+ for (DimensionBindingForPath element : elements)
+ if ( ! element.binding().isNull())
+ allChildren.add(element.binding());
+
+ action.accept(elements, allChildren);
+
+ return allChildren;
+ }
+
+ void add(DimensionBindingForPath entry) {
+ if (depth == entry.path().size())
+ elements.add(entry);
+ else
+ children.computeIfAbsent(entry.path().get(depth),
+ __ -> new Node(depth + 1)).add(entry);
+ }
+
+ }
+
+ }
+
+
}
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/Binding.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/Binding.java
index e873c80add1..46430a3041a 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/Binding.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/Binding.java
@@ -33,7 +33,7 @@ public class Binding implements Comparable<Binding> {
private final int hashCode;
- public static final Binding nullBinding = new Binding(Integer.MAX_VALUE, Collections.<String,String>emptyMap());
+ public static final Binding nullBinding = new Binding(Integer.MAX_VALUE, Map.of());
public static Binding createFrom(DimensionBinding dimensionBinding) {
if (dimensionBinding.getDimensions().size() > maxDimensions)
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalMap.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalMap.java
index b6bd6dc5a6a..6dc5f61c1f6 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalMap.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalMap.java
@@ -46,14 +46,9 @@ public class DimensionalMap<VALUE> {
private final Map<CompoundName, DimensionalValue.Builder<VALUE>> entries = new HashMap<>();
- // TODO: DimensionBinding -> Binding?
- public void put(CompoundName key, DimensionBinding binding, VALUE value) {
- DimensionalValue.Builder<VALUE> entry = entries.get(key);
- if (entry == null) {
- entry = new DimensionalValue.Builder<>();
- entries.put(key, entry);
- }
- entry.add(value, binding);
+ public void put(CompoundName key, Binding binding, VALUE value) {
+ entries.computeIfAbsent(key, __ -> new DimensionalValue.Builder<>())
+ .add(value, binding);
}
public DimensionalMap<VALUE> build() {
diff --git a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalValue.java b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalValue.java
index fb62cfca7d3..afe07b09d41 100644
--- a/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalValue.java
+++ b/container-search/src/main/java/com/yahoo/search/query/profile/compiled/DimensionalValue.java
@@ -76,15 +76,11 @@ public class DimensionalValue<VALUE> {
return null;
}
- public void add(VALUE value, DimensionBinding variantBinding) {
+ public void add(VALUE value, Binding variantBinding) {
// Note: We know we can index by the value because its possible types are constrained
// to what query profiles allow: String, primitives and query profiles (wrapped as a ValueWithSource)
- Value.Builder<VALUE> variant = buildableVariants.get(value);
- if (variant == null) {
- variant = new Value.Builder<>(value);
- buildableVariants.put(value, variant);
- }
- variant.addVariant(variantBinding, value);
+ buildableVariants.computeIfAbsent(value, Value.Builder::new)
+ .addVariant(variantBinding, value);
}
public DimensionalValue<VALUE> build(Map<CompoundName, DimensionalValue.Builder<VALUE>> entries) {
@@ -156,8 +152,8 @@ public class DimensionalValue<VALUE> {
/** Add a binding this holds for */
@SuppressWarnings("unchecked")
- public void addVariant(DimensionBinding binding, VALUE newValue) {
- variants.add(Binding.createFrom(binding));
+ public void addVariant(Binding binding, VALUE newValue) {
+ variants.add(binding);
// We're combining values for efficiency, so remove incorrect provenance info
if (value instanceof ValueWithSource) {
diff --git a/processing/src/main/java/com/yahoo/processing/request/CompoundName.java b/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
index 09c0879fdbf..432c7473c2b 100644
--- a/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
+++ b/processing/src/main/java/com/yahoo/processing/request/CompoundName.java
@@ -140,7 +140,7 @@ public final class CompoundName {
if (nameParts.length == 0) return this;
if (isEmpty()) return fromComponents(nameParts);
- List<String> newCompounds = new ArrayList<>(nameParts.length+compounds.size());
+ List<String> newCompounds = new ArrayList<>(nameParts.length + compounds.size());
newCompounds.addAll(Arrays.asList(nameParts));
newCompounds.addAll(this.compounds);
return new CompoundName(newCompounds);
@@ -192,7 +192,7 @@ public final class CompoundName {
this + "' only have " + compounds.size() + " components.");
if (n == 1) return rest();
if (compounds.size() == n) return empty;
- return rest.rest(n-1);
+ return rest.rest(n - 1);
}
/**