Skip to content

Commit adfb565

Browse files
committed
Ensure filters are pushed down before completion when it is possible.
1 parent 9d3200a commit adfb565

File tree

2 files changed

+45
-0
lines changed

2 files changed

+45
-0
lines changed

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFilters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.elasticsearch.xpack.esql.plan.logical.Project;
2525
import org.elasticsearch.xpack.esql.plan.logical.RegexExtract;
2626
import org.elasticsearch.xpack.esql.plan.logical.UnaryPlan;
27+
import org.elasticsearch.xpack.esql.plan.logical.inference.Completion;
2728
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
2829
import org.elasticsearch.xpack.esql.plan.logical.join.JoinTypes;
2930

@@ -70,6 +71,10 @@ protected LogicalPlan rule(Filter filter) {
7071
// Push down filters that do not rely on attributes created by RegexExtract
7172
var attributes = AttributeSet.of(Expressions.asAttributes(re.extractedFields()));
7273
plan = maybePushDownPastUnary(filter, re, attributes::contains, NO_OP);
74+
} else if (child instanceof Completion completion) {
75+
// Push down filters that do not rely on attributes created by Cpmpletion
76+
var attributes = AttributeSet.of(completion.generatedAttributes());
77+
plan = maybePushDownPastUnary(filter, completion, attributes::contains, NO_OP);
7378
} else if (child instanceof Enrich enrich) {
7479
// Push down filters that do not rely on attributes created by Enrich
7580
var attributes = AttributeSet.of(Expressions.asAttributes(enrich.enrichFields()));

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
package org.elasticsearch.xpack.esql.optimizer.rules.logical;
99

1010
import org.elasticsearch.index.IndexMode;
11+
import org.elasticsearch.index.query.QueryBuilder;
1112
import org.elasticsearch.test.ESTestCase;
1213
import org.elasticsearch.xpack.esql.core.expression.Alias;
1314
import org.elasticsearch.xpack.esql.core.expression.Attribute;
1415
import org.elasticsearch.xpack.esql.core.expression.Expression;
1516
import org.elasticsearch.xpack.esql.core.expression.FieldAttribute;
17+
import org.elasticsearch.xpack.esql.core.type.DataType;
1618
import org.elasticsearch.xpack.esql.expression.function.aggregate.Count;
19+
import org.elasticsearch.xpack.esql.expression.function.fulltext.Match;
1720
import org.elasticsearch.xpack.esql.expression.function.scalar.math.Pow;
1821
import org.elasticsearch.xpack.esql.expression.function.scalar.string.RLike;
1922
import org.elasticsearch.xpack.esql.expression.function.scalar.string.WildcardLike;
@@ -28,6 +31,7 @@
2831
import org.elasticsearch.xpack.esql.plan.logical.Filter;
2932
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
3033
import org.elasticsearch.xpack.esql.plan.logical.Project;
34+
import org.elasticsearch.xpack.esql.plan.logical.inference.Completion;
3135
import org.elasticsearch.xpack.esql.plan.logical.local.EsqlProject;
3236

3337
import java.util.ArrayList;
@@ -45,9 +49,12 @@
4549
import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOf;
4650
import static org.elasticsearch.xpack.esql.EsqlTestUtils.greaterThanOrEqualOf;
4751
import static org.elasticsearch.xpack.esql.EsqlTestUtils.lessThanOf;
52+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.randomLiteral;
53+
import static org.elasticsearch.xpack.esql.EsqlTestUtils.referenceAttribute;
4854
import static org.elasticsearch.xpack.esql.EsqlTestUtils.rlike;
4955
import static org.elasticsearch.xpack.esql.EsqlTestUtils.wildcardLike;
5056
import static org.elasticsearch.xpack.esql.core.tree.Source.EMPTY;
57+
import static org.mockito.Mockito.mock;
5158

5259
public class PushDownAndCombineFiltersTests extends ESTestCase {
5360

@@ -238,6 +245,39 @@ public void testSelectivelyPushDownFilterPastFunctionAgg() {
238245
assertEquals(expected, new PushDownAndCombineFilters().apply(fb));
239246
}
240247

248+
public void testPushDownFilterPastCompletion() {
249+
FieldAttribute a = getFieldAttribute("a");
250+
FieldAttribute b = getFieldAttribute("b");
251+
EsRelation relation = relation(List.of(a, b));
252+
253+
GreaterThan conditionA = greaterThanOf(getFieldAttribute("a"), ONE);
254+
Filter filterA = new Filter(EMPTY, relation, conditionA);
255+
256+
Completion completion = completion(filterA);
257+
258+
LessThan conditionB = lessThanOf(getFieldAttribute("b"), TWO);
259+
Match conditionCompletion = new Match(EMPTY, completion.targetField(), randomLiteral(DataType.TEXT), mock(Expression.class), mock(QueryBuilder.class));
260+
Filter filterB = new Filter(EMPTY, completion, new And(EMPTY, conditionB, conditionCompletion));
261+
262+
LogicalPlan expectedOptimizedPlan = new Filter(EMPTY,
263+
new Completion(
264+
EMPTY,
265+
new Filter(EMPTY, relation, new And(EMPTY, conditionA, conditionB)),
266+
completion.inferenceId(),
267+
completion.prompt(),
268+
completion.targetField()
269+
),
270+
conditionCompletion
271+
);
272+
273+
assertEquals(expectedOptimizedPlan, new PushDownAndCombineFilters().apply(filterB));
274+
}
275+
276+
private static Completion completion(LogicalPlan child) {
277+
return new Completion(EMPTY, child, randomLiteral(DataType.TEXT), randomLiteral(DataType.TEXT), referenceAttribute(randomIdentifier(), DataType.TEXT));
278+
}
279+
280+
241281
private static EsRelation relation() {
242282
return relation(List.of());
243283
}

0 commit comments

Comments
 (0)