diff options
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); } /** |