Skip to content

Commit 42a8007

Browse files
committed
Move some checks from Or to FullTextFunction
1 parent c97aeb7 commit 42a8007

File tree

3 files changed

+27
-68
lines changed

3 files changed

+27
-68
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -248,38 +248,46 @@ private static void checkFullTextQueryFunctions(LogicalPlan plan, Failures failu
248248
* @param condition condition to check for disjunctions of full text searches
249249
* @param failures failures collection to add to
250250
*/
251-
public static void checkFullTextSearchDisjunctions(Expression condition, Failures failures) {
251+
private static void checkFullTextSearchDisjunctions(Expression condition, Failures failures) {
252252
Holder<Boolean> isInvalid = new Holder<>(false);
253253
condition.forEachDown(Or.class, or -> {
254254
if (isInvalid.get()) {
255255
// Exit early if we already have a failures
256256
return;
257257
}
258-
boolean hasFullText = or.anyMatch(FullTextFunction.class::isInstance);
259-
if (hasFullText) {
260-
boolean hasOnlyFullText = onlyFullTextFunctionsInExpression(or);
261-
if (hasOnlyFullText == false) {
262-
isInvalid.set(true);
263-
failures.add(
264-
fail(
265-
or,
266-
"Invalid condition when using METADATA _score [{}]. Full text functions can be used in an OR condition, "
267-
+ "but only if just full text functions are used in the OR condition",
268-
or.sourceText()
269-
)
270-
);
271-
}
258+
if (checkDisjunctionPushable(or) == false) {
259+
isInvalid.set(true);
260+
failures.add(
261+
fail(
262+
or,
263+
"Invalid condition when using METADATA _score [{}]. Full text functions can be used in an OR condition, "
264+
+ "but only if just full text functions are used in the OR condition",
265+
or.sourceText()
266+
)
267+
);
272268
}
273269
});
274270
}
275271

272+
/**
273+
* Checks if a disjunction is pushable from the point of view of FullTextFunctions. Either it has no FullTextFunctions or
274+
* all it contains are FullTextFunctions.
275+
*
276+
* @param or disjunction to check
277+
* @return true if the disjunction is pushable, false otherwise
278+
*/
279+
public static boolean checkDisjunctionPushable(Or or) {
280+
boolean hasFullText = or.anyMatch(FullTextFunction.class::isInstance);
281+
return hasFullText == false || onlyFullTextFunctionsInExpression(or);
282+
}
283+
276284
/**
277285
* Checks whether an expression contains just full text functions or negations (NOT) and combinations (AND, OR) of full text functions
278286
*
279287
* @param expression expression to check
280288
* @return true if all children are full text functions or negations of full text functions, false otherwise
281289
*/
282-
private static boolean onlyFullTextFunctionsInExpression(Expression expression) {
290+
public static boolean onlyFullTextFunctionsInExpression(Expression expression) {
283291
if (expression instanceof FullTextFunction) {
284292
return true;
285293
} else if (expression instanceof Not) {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/logical/Or.java

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,17 @@
88

99
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
1010
import org.elasticsearch.common.io.stream.StreamInput;
11-
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware;
12-
import org.elasticsearch.xpack.esql.common.Failures;
1311
import org.elasticsearch.xpack.esql.core.expression.Expression;
14-
import org.elasticsearch.xpack.esql.core.expression.MetadataAttribute;
1512
import org.elasticsearch.xpack.esql.core.expression.predicate.Negatable;
1613
import org.elasticsearch.xpack.esql.core.tree.NodeInfo;
1714
import org.elasticsearch.xpack.esql.core.tree.Source;
1815
import org.elasticsearch.xpack.esql.expression.function.fulltext.FullTextFunction;
1916
import org.elasticsearch.xpack.esql.expression.predicate.Predicates;
2017
import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates;
21-
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
2218

2319
import java.io.IOException;
24-
import java.util.function.BiConsumer;
2520

26-
import static org.elasticsearch.xpack.esql.common.Failure.fail;
27-
28-
public class Or extends BinaryLogic implements Negatable<BinaryLogic>, PostAnalysisPlanVerificationAware {
21+
public class Or extends BinaryLogic implements Negatable<BinaryLogic> {
2922
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Or", Or::new);
3023

3124
public Or(Source source, Expression left, Expression right) {
@@ -69,48 +62,6 @@ protected Expression canonicalize() {
6962

7063
@Override
7164
public boolean translatable(LucenePushdownPredicates pushdownPredicates) {
72-
return super.translatable(pushdownPredicates) && checkPushableFullTextSearchFunctions();
73-
}
74-
75-
@Override
76-
public BiConsumer<LogicalPlan, Failures> postAnalysisPlanVerification() {
77-
return (plan, failures) -> {
78-
boolean usesScore = plan.output()
79-
.stream()
80-
.anyMatch(attr -> attr instanceof MetadataAttribute ma && ma.name().equals(MetadataAttribute.SCORE));
81-
if (usesScore && checkPushableFullTextSearchFunctions() == false) {
82-
failures.add(
83-
fail(
84-
this,
85-
"Invalid condition when using METADATA _score [{}]. Full text functions can be used in an OR condition, "
86-
+ "but only if just full text functions are used in the OR condition",
87-
sourceText()
88-
)
89-
);
90-
}
91-
};
92-
}
93-
94-
private boolean checkPushableFullTextSearchFunctions() {
95-
boolean hasFullText = anyMatch(FullTextFunction.class::isInstance);
96-
return hasFullText == false || onlyFullTextFunctionsInExpression(this);
97-
}
98-
99-
/**
100-
* Checks whether an expression contains just full text functions or negations (NOT) and combinations (AND, OR) of full text functions
101-
*
102-
* @param expression expression to check
103-
* @return true if all children are full text functions or negations of full text functions, false otherwise
104-
*/
105-
private static boolean onlyFullTextFunctionsInExpression(Expression expression) {
106-
if (expression instanceof FullTextFunction) {
107-
return true;
108-
} else if (expression instanceof Not) {
109-
return onlyFullTextFunctionsInExpression(expression.children().get(0));
110-
} else if (expression instanceof BinaryLogic binaryLogic) {
111-
return onlyFullTextFunctionsInExpression(binaryLogic.left()) && onlyFullTextFunctionsInExpression(binaryLogic.right());
112-
}
113-
114-
return false;
65+
return super.translatable(pushdownPredicates) && FullTextFunction.checkDisjunctionPushable(this);
11566
}
11667
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@
3838
import org.elasticsearch.xpack.esql.core.expression.Expressions;
3939
import org.elasticsearch.xpack.esql.core.expression.Literal;
4040
import org.elasticsearch.xpack.esql.core.expression.ReferenceAttribute;
41-
import org.elasticsearch.xpack.esql.core.expression.predicate.logical.Or;
4241
import org.elasticsearch.xpack.esql.core.tree.Source;
4342
import org.elasticsearch.xpack.esql.core.type.DataType;
4443
import org.elasticsearch.xpack.esql.core.type.EsField;
4544
import org.elasticsearch.xpack.esql.core.util.Holder;
4645
import org.elasticsearch.xpack.esql.enrich.ResolvedEnrichPolicy;
4746
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
4847
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
48+
import org.elasticsearch.xpack.esql.expression.predicate.logical.Or;
4949
import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan;
5050
import org.elasticsearch.xpack.esql.index.EsIndex;
5151
import org.elasticsearch.xpack.esql.index.IndexResolution;

0 commit comments

Comments
 (0)