diff options
author | Arne Juul <arnej@vespa.ai> | 2023-10-30 11:27:01 +0000 |
---|---|---|
committer | Arne Juul <arnej@vespa.ai> | 2023-10-30 11:51:20 +0000 |
commit | deb7ac732b85a497a545f8df15e9d4e65943031c (patch) | |
tree | 8d411a1be666e70f2fb8eefb77d0205e374b24fd /config-model | |
parent | f2d3bcf477b133cd3f6519466bd07a9a14cfdf3e (diff) |
handle functions with arguments
* should work as match-features and summary-features now
* and they will become hidden/implicit match-feature if used from global-phase
Diffstat (limited to 'config-model')
3 files changed, 150 insertions, 11 deletions
diff --git a/config-model/src/main/java/com/yahoo/schema/derived/RawRankProfile.java b/config-model/src/main/java/com/yahoo/schema/derived/RawRankProfile.java index 68fa0fe6de9..87b79ddcdc3 100644 --- a/config-model/src/main/java/com/yahoo/schema/derived/RawRankProfile.java +++ b/config-model/src/main/java/com/yahoo/schema/derived/RawRankProfile.java @@ -301,28 +301,41 @@ public class RawRankProfile implements RankProfilesConfig.Producer { private void replaceFunctionFeatures(Set<ReferenceNode> features, SerializationContext context) { if (features == null) return; - Map<String, ReferenceNode> functionFeatures = new LinkedHashMap<>(); + Set<ReferenceNode> functionFeatures = new LinkedHashSet<>(); for (Iterator<ReferenceNode> i = features.iterator(); i.hasNext(); ) { ReferenceNode referenceNode = i.next(); // Is the feature a function? ExpressionFunction function = context.getFunction(referenceNode.getName()); if (function != null) { - String propertyName = RankingExpression.propertyName(referenceNode.getName()); - String expressionString = function.getBody().getRoot().toString(context).toString(); + if (referenceNode.getOutput() != null) { + throw new IllegalArgumentException("function " + referenceNode.getName() + + " cannot provide output " + referenceNode.getOutput() + + " demanded by feature " + referenceNode); + } + int needArgs = function.arguments().size(); + var useArgs = referenceNode.getArguments(); + if (needArgs != useArgs.size()) { + throw new IllegalArgumentException("function " + referenceNode.getName() + + " needs " + needArgs + + " arguments but gets " + useArgs.size() + + " from feature " + referenceNode); + } + var instance = function.expand(context, useArgs.expressions(), new java.util.ArrayDeque<>()); + String propertyName = RankingExpression.propertyName(instance.getName()); + String expressionString = instance.getExpressionString(); context.addFunctionSerialization(propertyName, expressionString); - function.returnType().ifPresent(t -> context.addFunctionTypeSerialization(referenceNode.getName(), t)); - var backendReferenceNode = new ReferenceNode(wrapInRankingExpression(referenceNode.getName()), - referenceNode.getArguments().expressions(), - referenceNode.getOutput()); + function.returnType().ifPresent(t -> context.addFunctionTypeSerialization(instance.getName(), t)); + String backendReference = wrapInRankingExpression(instance.getName()); + var backendReferenceNode = new ReferenceNode(backendReference, List.of(), null); // tell backend to map back to the name the user expects: - featureRenames.put(backendReferenceNode.toString(), referenceNode.toString()); - functionFeatures.put(referenceNode.getName(), backendReferenceNode); + featureRenames.put(backendReference, referenceNode.toString()); + functionFeatures.add(backendReferenceNode); i.remove(); // Will add the expanded one in next block } } // Then, replace the features that were functions - for (Map.Entry<String, ReferenceNode> e : functionFeatures.entrySet()) { - features.add(e.getValue()); + for (ReferenceNode mappedFun : functionFeatures) { + features.add(mappedFun); } } diff --git a/config-model/src/test/derived/rankingexpression/rank-profiles.cfg b/config-model/src/test/derived/rankingexpression/rank-profiles.cfg index b3257c962dd..87882eef273 100644 --- a/config-model/src/test/derived/rankingexpression/rank-profiles.cfg +++ b/config-model/src/test/derived/rankingexpression/rank-profiles.cfg @@ -582,3 +582,91 @@ rankprofile[].normalizer[].name "normalize@3221316369@rrank" rankprofile[].normalizer[].input "nativeRank" rankprofile[].normalizer[].algo RRANK rankprofile[].normalizer[].kparam 60.0 +rankprofile[].name "function-with-arg-as-summary-feature" +rankprofile[].fef.property[].name "vespa.type.feature.attribute(t1)" +rankprofile[].fef.property[].value "tensor(m{},v[3])" +rankprofile[].fef.property[].name "rankingExpression(plusOne@478d886d33f680d).rankingScript" +rankprofile[].fef.property[].value "41 + 1" +rankprofile[].fef.property[].name "rankingExpression(useAttr@93d0729be0db6c70.30effc5f9cc0df93).rankingScript" +rankprofile[].fef.property[].value "attribute(foo1) * 17" +rankprofile[].fef.property[].name "rankingExpression(plusOne).rankingScript" +rankprofile[].fef.property[].value "x + 1" +rankprofile[].fef.property[].name "rankingExpression(useAttr).rankingScript" +rankprofile[].fef.property[].value "attribute(name) * weight" +rankprofile[].fef.property[].name "vespa.rank.firstphase" +rankprofile[].fef.property[].value "nativeRank" +rankprofile[].fef.property[].name "vespa.summary.feature" +rankprofile[].fef.property[].value "attribute(t1)" +rankprofile[].fef.property[].name "vespa.summary.feature" +rankprofile[].fef.property[].value "rankingExpression(plusOne@478d886d33f680d)" +rankprofile[].fef.property[].name "vespa.summary.feature" +rankprofile[].fef.property[].value "rankingExpression(useAttr@93d0729be0db6c70.30effc5f9cc0df93)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(plusOne@478d886d33f680d)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "plusOne(41)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(useAttr@93d0729be0db6c70.30effc5f9cc0df93)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "useAttr(foo1,17)" +rankprofile[].fef.property[].name "vespa.type.attribute.t1" +rankprofile[].fef.property[].value "tensor(m{},v[3])" +rankprofile[].name "function-with-arg-in-global-phase" +rankprofile[].fef.property[].name "rankingExpression(useAttr@6598f1aecaec0a2d.40876484d21a389).rankingScript" +rankprofile[].fef.property[].value "attribute(t1) * 42" +rankprofile[].fef.property[].name "rankingExpression(plusOne@31852fecfab75f29).rankingScript" +rankprofile[].fef.property[].value "2 + 1" +rankprofile[].fef.property[].name "rankingExpression(useAttr@93d0729be0db6c70.fe12ed266262cc16).rankingScript" +rankprofile[].fef.property[].value "attribute(foo1) * 1.25" +rankprofile[].fef.property[].name "rankingExpression(withIndirect@93d0729be0db6c70).rankingScript" +rankprofile[].fef.property[].value "rankingExpression(useAttr@93d0729be0db6c70.fe12ed266262cc16)" +rankprofile[].fef.property[].name "rankingExpression(plusOne@4a2b16f9107d7185).rankingScript" +rankprofile[].fef.property[].value "attribute(foo2) + 1" +rankprofile[].fef.property[].name "vespa.type.feature.useAttr(t1,42)" +rankprofile[].fef.property[].value "tensor(m{},v[3])" +rankprofile[].fef.property[].name "rankingExpression(plusOne).rankingScript" +rankprofile[].fef.property[].value "x + 1" +rankprofile[].fef.property[].name "rankingExpression(useAttr).rankingScript" +rankprofile[].fef.property[].value "attribute(name) * weight" +rankprofile[].fef.property[].name "rankingExpression(useAttr@2e0b6bb9bf541103.fe12ed266262cc16).rankingScript" +rankprofile[].fef.property[].value "attribute(name) * 1.25" +rankprofile[].fef.property[].name "rankingExpression(withIndirect).rankingScript" +rankprofile[].fef.property[].value "rankingExpression(useAttr@2e0b6bb9bf541103.fe12ed266262cc16)" +rankprofile[].fef.property[].name "vespa.rank.firstphase" +rankprofile[].fef.property[].value "nativeRank" +rankprofile[].fef.property[].name "vespa.rank.globalphase" +rankprofile[].fef.property[].value "rankingExpression(globalphase)" +rankprofile[].fef.property[].name "rankingExpression(globalphase).rankingScript" +rankprofile[].fef.property[].value "reduce(rankingExpression(useAttr@6598f1aecaec0a2d.40876484d21a389) + rankingExpression(plusOne@31852fecfab75f29) + rankingExpression(withIndirect@93d0729be0db6c70) + rankingExpression(plusOne@4a2b16f9107d7185), sum)" +rankprofile[].fef.property[].name "vespa.match.feature" +rankprofile[].fef.property[].value "rankingExpression(plusOne@4a2b16f9107d7185)" +rankprofile[].fef.property[].name "vespa.match.feature" +rankprofile[].fef.property[].value "rankingExpression(plusOne@31852fecfab75f29)" +rankprofile[].fef.property[].name "vespa.match.feature" +rankprofile[].fef.property[].value "rankingExpression(withIndirect@93d0729be0db6c70)" +rankprofile[].fef.property[].name "vespa.match.feature" +rankprofile[].fef.property[].value "rankingExpression(useAttr@6598f1aecaec0a2d.40876484d21a389)" +rankprofile[].fef.property[].name "vespa.hidden.matchfeature" +rankprofile[].fef.property[].value "plusOne(2)" +rankprofile[].fef.property[].name "vespa.hidden.matchfeature" +rankprofile[].fef.property[].value "withIndirect(foo1)" +rankprofile[].fef.property[].name "vespa.hidden.matchfeature" +rankprofile[].fef.property[].value "useAttr(t1,42)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(plusOne@4a2b16f9107d7185)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "plusOne(attribute(foo2))" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(plusOne@31852fecfab75f29)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "plusOne(2)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(withIndirect@93d0729be0db6c70)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "withIndirect(foo1)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "rankingExpression(useAttr@6598f1aecaec0a2d.40876484d21a389)" +rankprofile[].fef.property[].name "vespa.feature.rename" +rankprofile[].fef.property[].value "useAttr(t1,42)" +rankprofile[].fef.property[].name "vespa.type.attribute.t1" +rankprofile[].fef.property[].value "tensor(m{},v[3])" diff --git a/config-model/src/test/derived/rankingexpression/rankexpression.sd b/config-model/src/test/derived/rankingexpression/rankexpression.sd index 15537f1f9d0..1ccf74bfe17 100644 --- a/config-model/src/test/derived/rankingexpression/rankexpression.sd +++ b/config-model/src/test/derived/rankingexpression/rankexpression.sd @@ -469,4 +469,42 @@ schema rankexpression { match-features: nativeRank } + rank-profile function-with-arg-as-summary-feature { + function plusOne(x) { + expression: x + 1 + } + function useAttr(name, weight) { + expression: attribute(name) * weight + } + first-phase { + expression: nativeRank + } + summary-features { + attribute(t1) + plusOne(41) + useAttr(foo1, 17) + } + } + + rank-profile function-with-arg-in-global-phase { + function plusOne(x) { + expression: x + 1 + } + function useAttr(name, weight) { + expression: attribute(name) * weight + } + function withIndirect(name) { + expression: useAttr(name, 1.25) + } + first-phase { + expression: nativeRank + } + global-phase { + expression: sum(useAttr(t1, 42) + plusOne(2) + withIndirect(foo1) + plusOne(attribute(foo2))) + } + match-features { + plusOne(attribute(foo2)) + } + } + } |