This is an automated email from the ASF dual-hosted git repository.

huajianlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 64eddc5814a [opt](nereids) rewrite PreferPushDownProject to slots 
before filter-join pushdown (#61635)
64eddc5814a is described below

commit 64eddc5814a3b9513102104e73daaf6320d46b21
Author: 924060929 <[email protected]>
AuthorDate: Wed Mar 25 16:22:11 2026 +0800

    [opt](nereids) rewrite PreferPushDownProject to slots before filter-join 
pushdown (#61635)
    
    Push PreferPushDownProject expressions to the matching join child as
    aliases and replace them with slots in filter predicates. This keeps OR
    predicates above join while exposing nested-field access below join for
    storage-level pruning acceleration.
    
    Plan tree demo:
    
    Before:
    ```
      LogicalFilter((match_any(info['context'], 'abc') OR r.k2 > 60))
        LogicalJoin(INNER)
          left:  LogicalOlapScan(student)
          right: LogicalOlapScan(score)
    ```
    
    After:
    ```
    
      LogicalFilter((slot#x OR r.k2 > 60))
        LogicalJoin(INNER)
          left:  LogicalProject(student.*, match_any(info['context'], 'abc') AS 
slot#x)
                      LogicalOlapScan(student)
          right: LogicalOlapScan(score)
    ```
---
 .../nereids/rules/rewrite/PushDownProject.java     | 19 ++++++++
 .../nereids/rules/rewrite/PushDownProjectTest.java | 51 ++++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownProject.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownProject.java
index 832f9c25e77..432d25586a0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownProject.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PushDownProject.java
@@ -30,6 +30,7 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
@@ -50,6 +51,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.function.Function;
 
 /** push down project if the expression instance of PreferPushDownProject */
@@ -60,6 +62,9 @@ public class PushDownProject implements RewriteRuleFactory, 
NormalizeToSlot {
             RuleType.PUSH_DOWN_PROJECT_THROUGH_JOIN.build(
                 logicalJoin().thenApply(this::pushDownJoinExpressions)
             ),
+            RuleType.PUSH_DOWN_PROJECT_THROUGH_JOIN.build(
+                
logicalFilter(logicalJoin()).thenApply(this::pushDownFilterExpressions)
+            ),
             RuleType.PUSH_DOWN_PROJECT_THROUGH_JOIN.build(
                 
logicalProject(logicalJoin()).thenApply(this::defaultPushDownProject)
             ),
@@ -137,6 +142,20 @@ public class PushDownProject implements 
RewriteRuleFactory, NormalizeToSlot {
         ).withChildren(newLeft, newRight);
     }
 
+    private Plan 
pushDownFilterExpressions(MatchingContext<LogicalFilter<LogicalJoin<Plan, 
Plan>>> ctx) {
+        LogicalFilter<LogicalJoin<Plan, Plan>> filter = ctx.root;
+        LogicalJoin<Plan, Plan> join = filter.child();
+        PushdownProjectHelper pushdownProjectHelper = new 
PushdownProjectHelper(ctx.statementContext, join);
+        Pair<Boolean, Set<Expression>> pushPredicates
+                = 
pushdownProjectHelper.pushDownExpressions(filter.getConjuncts());
+        if (!pushPredicates.first) {
+            return filter;
+        }
+
+        LogicalJoin<Plan, Plan> newJoin = 
join.withChildren(pushdownProjectHelper.buildNewChildren());
+        return 
filter.withConjuncts(pushPredicates.second).withChildren(ImmutableList.of(newJoin));
+    }
+
     // return:
     //   key: rewrite the PreferPushDownProject to slot
     //   value: the pushed down project outputs which contains the 
Alias(PreferPushDownProject)
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownProjectTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownProjectTest.java
index 47398e3ef9a..0a0b425e817 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownProjectTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PushDownProjectTest.java
@@ -18,23 +18,37 @@
 package org.apache.doris.nereids.rules.rewrite;
 
 import org.apache.doris.nereids.StatementContext;
+import org.apache.doris.nereids.trees.expressions.Add;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.MatchAny;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.PreferPushDownProject;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.scalar.ElementAt;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.RelationId;
 import org.apache.doris.nereids.trees.plans.algebra.SetOperation.Qualifier;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
 import org.apache.doris.nereids.types.TinyIntType;
+import org.apache.doris.nereids.util.LogicalPlanBuilder;
 import org.apache.doris.nereids.util.MemoPatternMatchSupported;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.nereids.util.PlanConstructor;
+import org.apache.doris.qe.ConnectContext;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Test;
 
@@ -82,6 +96,7 @@ public class PushDownProjectTest implements 
MemoPatternMatchSupported {
     private final LogicalOneRowRelation rel1 = new LogicalOneRowRelation(new 
RelationId(1), rel1Output);
     private final LogicalOneRowRelation rel2 = new LogicalOneRowRelation(new 
RelationId(2), rel2Output);
     private final List<Plan> children = Lists.newArrayList(rel1, rel2);
+    private final ConnectContext connectContext = 
MemoTestUtils.createConnectContext();
 
     @Test
     public void testPushDownProjectThroughUnionOnlyHasChildren() {
@@ -189,4 +204,40 @@ public class PushDownProjectTest implements 
MemoPatternMatchSupported {
                         ).when(p -> p.getProjects().stream().noneMatch(ne -> 
ne.containsType(ElementAt.class)))
                 );
     }
+
+    @Test
+    public void shouldRewritePreferPushDownProjectInOrFilterToSlot() {
+        LogicalPlan rStudent = new 
LogicalOlapScan(PlanConstructor.getNextRelationId(), PlanConstructor.student,
+                ImmutableList.of(""));
+        LogicalPlan rScore = new 
LogicalOlapScan(PlanConstructor.getNextRelationId(), PlanConstructor.score,
+                ImmutableList.of(""));
+        Expression preferPushDownProjectExpr = new MatchAny(
+                new Add(rStudent.getOutput().get(0), Literal.of(1)),
+                Literal.of("abc"));
+        Expression rightSidePredicate = new 
GreaterThan(rScore.getOutput().get(2), Literal.of(60));
+        Expression orPredicate = new Or(preferPushDownProjectExpr, 
rightSidePredicate);
+
+        LogicalPlan plan = new LogicalPlanBuilder(rStudent)
+                .joinEmptyOn(rScore, JoinType.INNER_JOIN)
+                .filter(orPredicate)
+                .build();
+
+        PlanChecker.from(connectContext, plan)
+                .applyTopDown(new PushDownProject())
+                .matchesFromRoot(logicalFilter(
+                        logicalJoin(
+                                logicalProject(logicalOlapScan())
+                                        .when(project -> 
project.getProjects().stream()
+                                                
.filter(Alias.class::isInstance)
+                                                .map(Alias.class::cast)
+                                                .map(Alias::child)
+                                                
.anyMatch(PreferPushDownProject.class::isInstance)),
+                                logicalOlapScan()))
+                        .when(filter -> {
+                            Expression rewrittenPredicate = 
ImmutableList.copyOf(filter.getConjuncts()).get(0);
+                            return rewrittenPredicate instanceof Or
+                                    && 
rewrittenPredicate.anyMatch(SlotReference.class::isInstance)
+                                    && 
!rewrittenPredicate.anyMatch(PreferPushDownProject.class::isInstance);
+                        }));
+    }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to