1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.searchdefinition.processing;
import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.search.query.profile.QueryProfileRegistry;
import com.yahoo.searchdefinition.RankProfile;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.Search;
import com.yahoo.searchlib.rankingexpression.RankingExpression;
import com.yahoo.searchlib.rankingexpression.rule.ExpressionNode;
import com.yahoo.tensor.TensorType;
import com.yahoo.tensor.evaluation.TypeContext;
import com.yahoo.vespa.model.container.search.QueryProfiles;
/**
* Validates the types of all ranking expressions under a search instance:
* Some operators constrain the types of inputs, and first-and second-phase expressions
* must return scalar values. In addition, the existence of all referred attribute, query and constant
* features is ensured.
*
* @author bratseth
*/
public class RankingExpressionTypeValidator extends Processor {
private final QueryProfileRegistry queryProfiles;
public RankingExpressionTypeValidator(Search search,
DeployLogger deployLogger,
RankProfileRegistry rankProfileRegistry,
QueryProfiles queryProfiles) {
super(search, deployLogger, rankProfileRegistry, queryProfiles);
this.queryProfiles = queryProfiles.getRegistry();
}
@Override
public void process(boolean validate) {
if ( ! validate) return;
for (RankProfile profile : rankProfileRegistry.rankProfilesOf(search)) {
try {
validate(profile);
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("In " + search + ", " + profile, e);
}
}
}
/** Throws an IllegalArgumentException if the given rank profile does not produce valid type */
private void validate(RankProfile profile) {
profile.parseExpressions();
TypeContext context = profile.typeContext(queryProfiles);
profile.getSummaryFeatures().forEach(f -> ensureValid(f, "summary feature " + f, context));
ensureValidDouble(profile.getFirstPhaseRanking(), "first-phase expression", context);
ensureValidDouble(profile.getSecondPhaseRanking(), "second-phase expression", context);
}
private TensorType ensureValid(RankingExpression expression, String expressionDescription, TypeContext context) {
if (expression == null) return null;
return ensureValid(expression.getRoot(), expressionDescription, context);
}
private TensorType ensureValid(ExpressionNode expression, String expressionDescription, TypeContext context) {
TensorType type;
try {
type = expression.type(context);
}
catch (IllegalArgumentException e) {
throw new IllegalArgumentException("The " + expressionDescription + " is invalid", e);
}
if (type == null) // Not expected to happen
throw new IllegalStateException("Could not determine the type produced by " + expressionDescription);
return type;
}
private void ensureValidDouble(RankingExpression expression, String expressionDescription, TypeContext context) {
if (expression == null) return;
TensorType type = ensureValid(expression, expressionDescription, context);
if ( ! type.equals(TensorType.empty))
throw new IllegalArgumentException("The " + expressionDescription + " must produce a double " +
"(a tensor with no dimensions), but produces " + type);
}
}
|