diff options
Diffstat (limited to 'config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java')
-rw-r--r-- | config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java | 453 |
1 files changed, 239 insertions, 214 deletions
diff --git a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java index 1b6c4aa9459..53e9cfd5601 100644 --- a/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java +++ b/config-model/src/main/java/com/yahoo/searchdefinition/RankProfile.java @@ -43,6 +43,7 @@ import java.util.Objects; import java.util.Optional; import java.util.OptionalDouble; import java.util.Set; +import java.util.function.Predicate; import java.util.function.Supplier; import java.util.logging.Level; import java.util.stream.Collectors; @@ -65,8 +66,9 @@ public class RankProfile implements Cloneable { private final ImmutableSchema schema; /** The name of the rank profile inherited by this */ - private String inheritedName = null; - private RankProfile inherited = null; + private final List<String> inheritedNames = new ArrayList<>(); + /** Stores the resolved inherited profiles, or null when not resolved. */ + private List<RankProfile> inherited; /** The match settings of this profile */ private MatchPhaseSettings matchPhaseSettings = null; @@ -96,10 +98,10 @@ public class RankProfile implements Cloneable { private double rankScoreDropLimit = -Double.MAX_VALUE; private Set<ReferenceNode> summaryFeatures; - private String inheritedSummaryFeatures; + private String inheritedSummaryFeaturesProfileName; private Set<ReferenceNode> matchFeatures; - private String inheritedMatchFeatures; + private String inheritedMatchFeaturesProfileName; private Set<ReferenceNode> rankFeatures; @@ -199,31 +201,28 @@ public class RankProfile implements Cloneable { } /** - * Sets the name of the rank profile this inherits. Both rank profiles must be present in the same search - * definition + * Adds a profile to those inherited by this. + * The profile must belong to this schema (directly or by inheritance). */ - public void setInherited(String inheritedName) { - this.inheritedName = inheritedName; - } - - /** Returns the name of the profile this one inherits, or null if none is inherited */ - public String getInheritedName() { return inheritedName; } - - /** Returns the inherited rank profile, or null if there is none */ - private RankProfile getInherited() { - if (inheritedName == null) return null; - if (inherited == null) { - inherited = resolveInherited(); - if (inherited == null) { - String msg = "rank-profile '" + name() + "' inherits '" + inheritedName + - "', but this is not found in " + - ((schema() != null) ? schema() : " global rank profiles"); - throw new IllegalArgumentException(msg); - } else { - List<String> children = new ArrayList<>(); - children.add(createFullyQualifiedName()); - verifyNoInheritanceCycle(children, inherited); - } + public void inherit(String inheritedName) { + inheritedNames.add(inheritedName); + } + + /** Returns the names of the profiles this inherits, if any. */ + public List<String> inheritedNames() { return inheritedNames; } + + /** Returns the rank profiles inherited by this. */ + private List<RankProfile> inherited() { + if (inheritedNames.isEmpty()) return List.of(); + if (inherited != null) return inherited; + + for (String inheritedName : inheritedNames) { + inherited = (schema() != null) ? resolveInheritedProfiles(schema) + : List.of(rankProfileRegistry.getGlobal(inheritedName)); + + List<String> children = new ArrayList<>(); + children.add(createFullyQualifiedName()); + inherited.forEach(profile -> verifyNoInheritanceCycle(children, profile)); } return inherited; } @@ -237,15 +236,26 @@ public class RankProfile implements Cloneable { private void verifyNoInheritanceCycle(List<String> children, RankProfile parent) { children.add(parent.createFullyQualifiedName()); String root = children.get(0); - if (root.equals(parent.createFullyQualifiedName())) { + if (root.equals(parent.createFullyQualifiedName())) throw new IllegalArgumentException("There is a cycle in the inheritance for rank-profile '" + root + "' = " + children); + for (RankProfile parentInherited : parent.inherited()) + verifyNoInheritanceCycle(children, parentInherited); + } + + private List<RankProfile> resolveInheritedProfiles(ImmutableSchema schema) { + List<RankProfile> inherited = new ArrayList<>(); + for (String inheritedName : inheritedNames) { + RankProfile inheritedProfile = resolveInheritedProfile(schema, inheritedName); + if (inheritedProfile == null) + throw new IllegalArgumentException("rank-profile '" + name() + "' inherits '" + inheritedName + + "', but this is not found in " + + ((schema() != null) ? schema() : " global rank profiles")); + inherited.add(inheritedProfile); } - if (parent.getInherited() != null) { - verifyNoInheritanceCycle(children, parent.getInherited()); - } + return inherited; } - private RankProfile resolveInherited(ImmutableSchema schema) { + private RankProfile resolveInheritedProfile(ImmutableSchema schema, String inheritedName) { SDDocumentType documentType = schema.getDocument(); if (documentType != null) { if (name.equals(inheritedName)) { @@ -260,25 +270,11 @@ public class RankProfile implements Cloneable { return rankProfileRegistry.get(schema.getName(), inheritedName); } - private RankProfile resolveInherited() { - if (inheritedName == null) return null; - return (schema() != null) - ? resolveInherited(schema) - : rankProfileRegistry.getGlobal(inheritedName); - } - - /** - * Returns whether this profile inherits (directly or indirectly) the given profile - * - * @param name the profile name to compare this to. - * @return whether or not this inherits from the named profile. - */ + /** Returns whether this profile inherits (directly or indirectly) the given profile name. */ public boolean inherits(String name) { - RankProfile parent = getInherited(); - while (parent != null) { - if (parent.name().equals(name)) - return true; - parent = parent.getInherited(); + for (RankProfile inheritedProfile : inherited()) { + if (inheritedProfile.name().equals(name)) return true; + if (inheritedProfile.inherits(name)) return true; } return false; } @@ -289,10 +285,20 @@ public class RankProfile implements Cloneable { } public MatchPhaseSettings getMatchPhaseSettings() { - MatchPhaseSettings settings = this.matchPhaseSettings; - if (settings != null) return settings; - if (getInherited() != null) return getInherited().getMatchPhaseSettings(); - return null; + if (matchPhaseSettings != null) return matchPhaseSettings; + return inheritedWith(p -> p.getMatchPhaseSettings() != null,"match phase settings") + .map(p -> p.getMatchPhaseSettings()).orElse(null); + } + + /** Returns the single profile having the property checked by the given filter, or empty if none */ + private Optional<RankProfile> inheritedWith(Predicate<RankProfile> property, String propertyDescription) { + List<RankProfile> matchingInherited = + inherited().stream().filter(profile -> property.test(profile)).collect(Collectors.toList()); + if (matchingInherited.isEmpty()) return Optional.empty(); + if (matchingInherited.size() == 1) return Optional.of(matchingInherited.get(0)); + throw new IllegalArgumentException("Only one of the profiles inherited by " + this + " can contain " + + propertyDescription + ", but it is present in all of " + matchingInherited); + } public void addRankSetting(RankSetting rankSetting) { @@ -306,15 +312,14 @@ public class RankProfile implements Cloneable { /** * Returns the a rank setting of a field, or null if there is no such rank setting in this profile * - * @param field the field whose settings to return. - * @param type the type that the field is required to be. - * @return the rank setting found, or null. + * @param field the field whose settings to return + * @param type the type that the field is required to be + * @return the rank setting found, or null */ RankSetting getDeclaredRankSetting(String field, RankSetting.Type type) { for (Iterator<RankSetting> i = declaredRankSettingIterator(); i.hasNext(); ) { RankSetting setting = i.next(); - if (setting.getFieldName().equals(field) && - setting.getType().equals(type)) { + if (setting.getFieldName().equals(field) && setting.getType() == type) { return setting; } } @@ -333,9 +338,8 @@ public class RankProfile implements Cloneable { RankSetting rankSetting = getDeclaredRankSetting(field, type); if (rankSetting != null) return rankSetting; - if (getInherited() != null) return getInherited().getRankSetting(field, type); - - return null; + return inheritedWith(p -> p.getRankSetting(field, type) != null, "rank setting " + type + " on " + field) + .map(p -> p.getRankSetting(field, type)).orElse(null); } /** @@ -361,12 +365,20 @@ public class RankProfile implements Cloneable { * Changes to the returned set will not be reflected in this rank profile. */ public Set<RankSetting> rankSettings() { - Set<RankSetting> allSettings = new LinkedHashSet<>(rankSettings); - RankProfile parent = getInherited(); - if (parent != null) - allSettings.addAll(parent.rankSettings()); + Set<RankSetting> settings = new LinkedHashSet<>(); + for (RankProfile inheritedProfile : inherited()) { + for (RankSetting setting : inheritedProfile.rankSettings()) { + if (settings.contains(setting)) + throw new IllegalArgumentException(setting + " is present in " + inheritedProfile + " inherited by " + + this + ", but is also present in another profile inherited by it"); + settings.add(setting); + } + } - return allSettings; + // TODO: Here we do things in the wrong order to not break tests. Reverse this. + Set<RankSetting> finalSettings = new LinkedHashSet<>(rankSettings); + finalSettings.addAll(settings); + return finalSettings; } public void addConstant(String name, Value value) { @@ -385,14 +397,20 @@ public class RankProfile implements Cloneable { /** Returns an unmodifiable view of the constants available in this */ public Map<String, Value> getConstants() { - if (constants.isEmpty()) - return getInherited() != null ? getInherited().getConstants() : Collections.emptyMap(); - if (getInherited() == null || getInherited().getConstants().isEmpty()) - return Collections.unmodifiableMap(constants); - - Map<String, Value> combinedConstants = new HashMap<>(getInherited().getConstants()); - combinedConstants.putAll(constants); - return combinedConstants; + if (inherited().isEmpty()) return new HashMap<>(constants); + + Map<String, Value> allConstants = new HashMap<>(); + for (var inheritedProfile : inherited()) { + for (var constant : inheritedProfile.getConstants().entrySet()) { + if (allConstants.containsKey(constant.getKey())) + throw new IllegalArgumentException("Constant '" + constant.getKey() + "' is present in " + + inheritedProfile + " inherited by " + + this + ", but is also present in another profile inherited by it"); + allConstants.put(constant.getKey(), constant.getValue()); + } + } + allConstants.putAll(constants); + return allConstants; } public void addAttributeType(String attributeName, String attributeType) { @@ -423,9 +441,8 @@ public class RankProfile implements Cloneable { public RankingExpressionFunction getFirstPhase() { if (firstPhaseRanking != null) return firstPhaseRanking; - RankProfile inherited = getInherited(); - if (inherited != null) return inherited.getFirstPhase(); - return null; + return inheritedWith(p -> p.getFirstPhase() != null, "first-phase expression") + .map(p -> p.getFirstPhase()).orElse(null); } void setFirstPhaseRanking(RankingExpression rankingExpression) { @@ -452,9 +469,8 @@ public class RankProfile implements Cloneable { public RankingExpressionFunction getSecondPhase() { if (secondPhaseRanking != null) return secondPhaseRanking; - RankProfile inherited = getInherited(); - if (inherited != null) return inherited.getSecondPhase(); - return null; + return inheritedWith(p -> p.getSecondPhase() != null, "second-phase expression") + .map(p -> p.getSecondPhase()).orElse(null); } public void setSecondPhaseRanking(String expression) { @@ -466,75 +482,80 @@ public class RankProfile implements Cloneable { } } - /** Returns a read-only view of the summary features to use in this profile. This is never null */ - public Set<ReferenceNode> getSummaryFeatures() { - if (inheritedSummaryFeatures != null && summaryFeatures != null) { - Set<ReferenceNode> combined = new HashSet<>(); - combined.addAll(getInherited().getSummaryFeatures()); - combined.addAll(summaryFeatures); - return Collections.unmodifiableSet(combined); - } - if (summaryFeatures != null) return Collections.unmodifiableSet(summaryFeatures); - if (getInherited() != null) return getInherited().getSummaryFeatures(); - return Set.of(); - } - - private void addSummaryFeature(ReferenceNode feature) { - if (summaryFeatures == null) - summaryFeatures = new LinkedHashSet<>(); - summaryFeatures.add(feature); - } - - /** Adds the content of the given feature list to the internal list of summary features. */ - public void addSummaryFeatures(FeatureList features) { - for (ReferenceNode feature : features) { - addSummaryFeature(feature); - } - } + // TODO: Below we have duplicate methods for summary and match features: Encapsulate this in a single parametrized + // class instead (and probably make rank features work the same). /** * Sets the name this should inherit the summary features of. - * Without setting this, this will either have the summary features of the parent, + * Without setting this, this will either have the summary features of the single parent setting them, * or if summary features are set in this, only have the summary features in this. * With this set the resulting summary features of this will be the superset of those defined in this and * the final (with inheritance included) summary features of the given parent. - * The profile must be the profile which is directly inherited by this. - * + * The profile must be one which is directly inherited by this. */ public void setInheritedSummaryFeatures(String parentProfile) { - if ( ! parentProfile.equals(inheritedName)) - throw new IllegalArgumentException("This can only inherit the summary features of its parent, '" + - inheritedName + ", but attempting to inherit '" + parentProfile); - this.inheritedSummaryFeatures = parentProfile; + if ( ! inheritedNames().contains(parentProfile)) + throw new IllegalArgumentException("This can only inherit the summary features of a directly inherited profile, '" + + ", but attempting to inherit '" + parentProfile); + this.inheritedSummaryFeaturesProfileName = parentProfile; } /** * Sets the name of a profile this should inherit the match features of. - * Without setting this, this will either have the match features of the parent, + * Without setting this, this will either have the match features of the single parent setting them, * or if match features are set in this, only have the match features in this. * With this set the resulting match features of this will be the superset of those defined in this and * the final (with inheritance included) match features of the given parent. - * The profile must be the profile which is directly inherited by this. + * The profile must be one which which is directly inherited by this. * */ public void setInheritedMatchFeatures(String parentProfile) { - if ( ! parentProfile.equals(inheritedName)) - throw new IllegalArgumentException("This rank profile ("+name+") can only inherit the match features of its parent, '" + - inheritedName + ", but attemtping to inherit '" + parentProfile); - this.inheritedMatchFeatures = parentProfile; + if ( ! inheritedNames().contains(parentProfile)) + throw new IllegalArgumentException("This can only inherit the match features of a directly inherited profile, '" + + ", but attempting to inherit '" + parentProfile); + this.inheritedMatchFeaturesProfileName = parentProfile; + } + + /** Returns a read-only view of the summary features to use in this profile. This is never null */ + public Set<ReferenceNode> getSummaryFeatures() { + if (inheritedSummaryFeaturesProfileName != null && summaryFeatures != null) { + Set<ReferenceNode> combined = new HashSet<>(); + RankProfile inherited = inherited().stream() + .filter(p -> p.name().equals(inheritedSummaryFeaturesProfileName)) + .findAny() + .orElseThrow(); + combined.addAll(inherited.getSummaryFeatures()); + combined.addAll(summaryFeatures); + return Collections.unmodifiableSet(combined); + } + if (summaryFeatures != null) return Collections.unmodifiableSet(summaryFeatures); + return inheritedWith(p -> ! p.getSummaryFeatures().isEmpty(), "summary features") + .map(p -> p.getSummaryFeatures()) + .orElse(Set.of()); } /** Returns a read-only view of the match features to use in this profile. This is never null */ public Set<ReferenceNode> getMatchFeatures() { - if (inheritedMatchFeatures != null && matchFeatures != null) { + if (inheritedMatchFeaturesProfileName != null && matchFeatures != null) { Set<ReferenceNode> combined = new HashSet<>(); - combined.addAll(getInherited().getMatchFeatures()); + RankProfile inherited = inherited().stream() + .filter(p -> p.name().equals(inheritedMatchFeaturesProfileName)) + .findAny() + .orElseThrow(); + combined.addAll(inherited.getMatchFeatures()); combined.addAll(matchFeatures); return Collections.unmodifiableSet(combined); } if (matchFeatures != null) return Collections.unmodifiableSet(matchFeatures); - if (getInherited() != null) return getInherited().getMatchFeatures(); - return Set.of(); + return inheritedWith(p -> ! p.getMatchFeatures().isEmpty(), "match features") + .map(p -> p.getMatchFeatures()) + .orElse(Set.of()); + } + + private void addSummaryFeature(ReferenceNode feature) { + if (summaryFeatures == null) + summaryFeatures = new LinkedHashSet<>(); + summaryFeatures.add(feature); } private void addMatchFeature(ReferenceNode feature) { @@ -543,6 +564,13 @@ public class RankProfile implements Cloneable { matchFeatures.add(feature); } + /** Adds the content of the given feature list to the internal list of summary features. */ + public void addSummaryFeatures(FeatureList features) { + for (ReferenceNode feature : features) { + addSummaryFeature(feature); + } + } + /** Adds the content of the given feature list to the internal list of match features. */ public void addMatchFeatures(FeatureList features) { for (ReferenceNode feature : features) { @@ -553,8 +581,8 @@ public class RankProfile implements Cloneable { /** Returns a read-only view of the rank features to use in this profile. This is never null */ public Set<ReferenceNode> getRankFeatures() { if (rankFeatures != null) return Collections.unmodifiableSet(rankFeatures); - if (getInherited() != null) return getInherited().getRankFeatures(); - return Collections.emptySet(); + return inheritedWith(p -> ! p.getRankFeatures().isEmpty(), "summary-features") + .map(p -> p.getRankFeatures()).orElse(Set.of()); } private void addRankFeature(ReferenceNode feature) { @@ -585,12 +613,15 @@ public class RankProfile implements Cloneable { /** Returns a read only map view of the rank properties to use in this profile. This is never null. */ public Map<String, List<RankProperty>> getRankPropertyMap() { - if (rankProperties.size() == 0 && getInherited() == null) return Collections.emptyMap(); - if (rankProperties.size() == 0) return getInherited().getRankPropertyMap(); - if (getInherited() == null) return Collections.unmodifiableMap(rankProperties); + if (rankProperties.size() == 0 && inherited().isEmpty()) return Map.of(); + if (inherited().isEmpty()) return Collections.unmodifiableMap(rankProperties); + + var inheritedProperties = inheritedWith(p -> ! p.getRankPropertyMap().isEmpty(), "rank-properties") + .map(p -> p.getRankPropertyMap()).orElse(Map.of()); + if (rankProperties.isEmpty()) return inheritedProperties; // Neither is null - Map<String, List<RankProperty>> combined = new LinkedHashMap<>(getInherited().getRankPropertyMap()); + Map<String, List<RankProperty>> combined = new LinkedHashMap<>(inheritedProperties); combined.putAll(rankProperties); // Don't combine values across inherited properties return Collections.unmodifiableMap(combined); } @@ -604,57 +635,44 @@ public class RankProfile implements Cloneable { rankProperties.computeIfAbsent(rankProperty.getName(), (String key) -> new ArrayList<>(1)).add(rankProperty); } - @Override - public String toString() { - return "rank profile '" + name() + "'"; - } + public void setRerankCount(int rerankCount) { this.rerankCount = rerankCount; } public int getRerankCount() { - return (rerankCount < 0 && (getInherited() != null)) - ? getInherited().getRerankCount() - : rerankCount; + if (rerankCount >= 0) return rerankCount; + return inheritedWith(p -> p.getRerankCount() >= 0, "rerank-count") + .map(p -> p.getRerankCount()).orElse(-1); } + public void setNumThreadsPerSearch(int numThreads) { this.numThreadsPerSearch = numThreads; } + public int getNumThreadsPerSearch() { - return (numThreadsPerSearch < 0 && (getInherited() != null)) - ? getInherited().getNumThreadsPerSearch() - : numThreadsPerSearch; + if (numThreadsPerSearch >= 0) return numThreadsPerSearch; + return inheritedWith(p -> p.getNumThreadsPerSearch() >= 0, "num-threads-per-search") + .map(p -> p.getNumThreadsPerSearch()).orElse(-1); } - public void setNumThreadsPerSearch(int numThreads) { - this.numThreadsPerSearch = numThreads; - } + public void setMinHitsPerThread(int minHits) { this.minHitsPerThread = minHits; } public int getMinHitsPerThread() { - return (minHitsPerThread < 0 && (getInherited() != null)) - ? getInherited().getMinHitsPerThread() - : minHitsPerThread; - } - - public void setMinHitsPerThread(int minHits) { - this.minHitsPerThread = minHits; + if (minHitsPerThread >= 0) return minHitsPerThread; + return inheritedWith(p -> p.getMinHitsPerThread() >= 0, "min-hits-per-search") + .map(p -> p.getMinHitsPerThread()).orElse(-1); } - public void setNumSearchPartitions(int numSearchPartitions) { - this.numSearchPartitions = numSearchPartitions; - } + public void setNumSearchPartitions(int numSearchPartitions) { this.numSearchPartitions = numSearchPartitions; } public int getNumSearchPartitions() { - return (numSearchPartitions < 0 && (getInherited() != null)) - ? getInherited().getNumSearchPartitions() - : numSearchPartitions; + if (numSearchPartitions >= 0) return numSearchPartitions; + return inheritedWith(p -> p.getNumSearchPartitions() >= 0, "num-search-partitions") + .map(p -> p.getNumSearchPartitions()).orElse(-1); } - public OptionalDouble getTermwiseLimit() { - return ((termwiseLimit == null) && (getInherited() != null)) - ? getInherited().getTermwiseLimit() - : (termwiseLimit != null) ? OptionalDouble.of(termwiseLimit) : OptionalDouble.empty(); - } public void setTermwiseLimit(double termwiseLimit) { this.termwiseLimit = termwiseLimit; } - /** Sets the rerank count. Set to -1 to use inherited */ - public void setRerankCount(int rerankCount) { - this.rerankCount = rerankCount; + public OptionalDouble getTermwiseLimit() { + if (termwiseLimit != null) return OptionalDouble.of(termwiseLimit); + return inheritedWith(p -> p.getTermwiseLimit().isPresent(), "termwise-limit") + .map(p -> p.getTermwiseLimit()).orElse(OptionalDouble.empty()); } /** Whether we should ignore the default rank features. Set to null to use inherited */ @@ -664,10 +682,26 @@ public class RankProfile implements Cloneable { public boolean getIgnoreDefaultRankFeatures() { if (ignoreDefaultRankFeatures != null) return ignoreDefaultRankFeatures; - return (getInherited() != null) && getInherited().getIgnoreDefaultRankFeatures(); + return inheritedWith(p -> p.ignoreDefaultRankFeatures != null, "ignore-default-rank-features") + .map(p -> p.getIgnoreDefaultRankFeatures()).orElse(false); + } + + public void setKeepRankCount(int rerankArraySize) { this.keepRankCount = rerankArraySize; } + + public int getKeepRankCount() { + if (keepRankCount >= 0) return keepRankCount; + return inheritedWith(p -> p.getKeepRankCount() >= 0, "keep-rank-count") + .map(p -> p.getKeepRankCount()).orElse(-1); + } + + public void setRankScoreDropLimit(double rankScoreDropLimit) { this.rankScoreDropLimit = rankScoreDropLimit; } + + public double getRankScoreDropLimit() { + if (rankScoreDropLimit > -Double.MAX_VALUE) return rankScoreDropLimit; + return inheritedWith(p -> p.getRankScoreDropLimit() > -Double.MAX_VALUE, "rank.score-drop-limit") + .map(p -> p.getRankScoreDropLimit()).orElse(rankScoreDropLimit); } - /** Adds a function */ public void addFunction(String name, List<String> arguments, String expression, boolean inline) { try { addFunction(parseRankingExpression(name, arguments, expression), inline); @@ -729,10 +763,11 @@ public class RankProfile implements Cloneable { public RankingExpressionFunction findFunction(String name) { RankingExpressionFunction function = functions.get(name); - return ((function == null) && (getInherited() != null)) - ? getInherited().findFunction(name) - : function; + if (function != null) return function; + return inheritedWith(p -> p.findFunction(name) != null, "function '" + name + "'") + .map(p -> p.findFunction(name)).orElse(null); } + /** Returns an unmodifiable snapshot of the functions in this */ public Map<String, RankingExpressionFunction> getFunctions() { updateCachedFunctions(); @@ -749,59 +784,42 @@ public class RankProfile implements Cloneable { } private Map<String, RankingExpressionFunction> gatherAllFunctions() { - if (functions.isEmpty() && getInherited() == null) return Collections.emptyMap(); - if (functions.isEmpty()) return getInherited().getFunctions(); - if (getInherited() == null) return Collections.unmodifiableMap(new LinkedHashMap<>(functions)); - - // Neither is null - Map<String, RankingExpressionFunction> allFunctions = new LinkedHashMap<>(getInherited().getFunctions()); + if (functions.isEmpty() && inherited().isEmpty()) return Map.of(); + if (inherited().isEmpty()) return Collections.unmodifiableMap(new LinkedHashMap<>(functions)); + + // Combine + Map<String, RankingExpressionFunction> allFunctions = new LinkedHashMap<>(); + for (var inheritedProfile : inherited()) { + for (var function : inheritedProfile.getFunctions().entrySet()) { + if (allFunctions.containsKey(function.getKey())) + throw new IllegalArgumentException(this + " inherits " + inheritedProfile + " which contains " + + function.getValue() + ", but this function is already " + + "defined in another profile this inherits"); + allFunctions.put(function.getKey(), function.getValue()); + } + } allFunctions.putAll(functions); return Collections.unmodifiableMap(allFunctions); } private boolean needToUpdateFunctionCache() { - if (getInherited() != null) - return (allFunctionsCached == null) || getInherited().needToUpdateFunctionCache(); + if (inherited().stream().anyMatch(profile -> profile.needToUpdateFunctionCache())) return true; return allFunctionsCached == null; } - public int getKeepRankCount() { - if (keepRankCount >= 0) return keepRankCount; - if (getInherited() != null) return getInherited().getKeepRankCount(); - return -1; - } - - public void setKeepRankCount(int rerankArraySize) { - this.keepRankCount = rerankArraySize; - } - - public double getRankScoreDropLimit() { - if (rankScoreDropLimit >- Double.MAX_VALUE) return rankScoreDropLimit; - if (getInherited() != null) return getInherited().getRankScoreDropLimit(); - return rankScoreDropLimit; - } + public Set<String> filterFields() { return filterFields; } - public void setRankScoreDropLimit(double rankScoreDropLimit) { - this.rankScoreDropLimit = rankScoreDropLimit; - } + /** Returns all filter fields in this profile and any profile it inherits. */ + public Set<String> allFilterFields() { + Set<String> inheritedFilterFields = + inheritedWith(p -> ! p.allFilterFields().isEmpty(), "filter fields") + .map(p -> p.allFilterFields()).orElse(Set.of()); - public Set<String> filterFields() { - return filterFields; - } + if (inheritedFilterFields.isEmpty()) return Collections.unmodifiableSet(filterFields); - /** - * Returns all filter fields in this profile and any profile it inherits. - * - * @return the set of all filter fields - */ - public Set<String> allFilterFields() { - RankProfile parent = getInherited(); - Set<String> retval = new LinkedHashSet<>(); - if (parent != null) { - retval.addAll(parent.allFilterFields()); - } - retval.addAll(filterFields()); - return retval; + Set<String> combined = new LinkedHashSet<>(inheritedFilterFields); + combined.addAll(filterFields()); + return combined; } private ExpressionFunction parseRankingExpression(String name, List<String> arguments, String expression) throws ParseException { @@ -892,7 +910,7 @@ public class RankProfile implements Cloneable { secondPhaseRanking = compile(this.getSecondPhase(), queryProfiles, featureTypes, importedModels, getConstants(), inlineFunctions, expressionTransforms); // Function compiling second pass: compile all functions and insert previously compiled inline functions - // TODO This merges all functions from inherited profiles too and erases inheritance information. Not good. + // TODO: This merges all functions from inherited profiles too and erases inheritance information. Not good. functions = compileFunctions(this::getFunctions, queryProfiles, featureTypes, importedModels, inlineFunctions, expressionTransforms); allFunctionsCached = null; } @@ -947,6 +965,7 @@ public class RankProfile implements Cloneable { Map<String, RankingExpressionFunction> inlineFunctions, ExpressionTransforms expressionTransforms) { if (function == null) return null; + RankProfileTransformContext context = new RankProfileTransformContext(this, queryProfiles, featureTypes, @@ -1063,6 +1082,11 @@ public class RankProfile implements Cloneable { }); } + @Override + public String toString() { + return "rank profile '" + name() + "'"; + } + /** * A rank setting. The identity of a rank setting is its field name and type (not value). * A rank setting is immutable. @@ -1105,8 +1129,9 @@ public class RankProfile implements Cloneable { return name; } + @Override public String toString() { - return "type: " + name; + return "type " + name; } } @@ -1219,7 +1244,7 @@ public class RankProfile implements Cloneable { @Override public String toString() { - return "function " + function; + return function.toString(); } } |