You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by mo...@apache.org on 2022/12/23 07:31:48 UTC

[doris] branch master updated: [Enhancement](Nereids) change expression to conjuncts in filter (#14807)

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

morrysnow 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 4b7f279cf9 [Enhancement](Nereids) change expression to conjuncts in filter (#14807)
4b7f279cf9 is described below

commit 4b7f279cf95712772046bdf69a1266d192122206
Author: mch_ucchi <41...@users.noreply.github.com>
AuthorDate: Fri Dec 23 15:31:40 2022 +0800

    [Enhancement](Nereids) change expression to conjuncts in filter (#14807)
---
 .../org/apache/doris/nereids/CascadesContext.java  |  2 +-
 .../glue/translator/PhysicalPlanTranslator.java    |  8 +--
 .../doris/nereids/parser/LogicalPlanBuilder.java   | 10 +--
 .../doris/nereids/processor/post/Validator.java    |  9 ++-
 .../nereids/rules/analysis/AnalyzeSubquery.java    | 24 +++----
 .../doris/nereids/rules/analysis/BindFunction.java |  9 +--
 .../nereids/rules/analysis/BindSlotReference.java  | 23 +++++--
 .../doris/nereids/rules/analysis/CheckPolicy.java  |  3 +-
 .../nereids/rules/analysis/FillUpMissingSlots.java | 10 +--
 .../expression/rewrite/ExpressionRewrite.java      | 10 ++-
 .../LogicalFilterToPhysicalFilter.java             |  2 +-
 .../mv/AbstractSelectMaterializedIndexRule.java    |  9 +--
 .../mv/SelectMaterializedIndexWithAggregate.java   | 16 ++---
 .../SelectMaterializedIndexWithoutAggregate.java   |  8 +--
 .../rewrite/logical/ApplyPullFilterOnAgg.java      |  7 +-
 .../logical/ApplyPullFilterOnProjectUnderAgg.java  |  2 +-
 .../rules/rewrite/logical/EliminateFilter.java     | 22 +++++--
 .../rules/rewrite/logical/EliminateOuterJoin.java  | 10 +--
 .../rules/rewrite/logical/ExistsApplyToJoin.java   |  5 +-
 ...xtractSingleTableExpressionFromDisjunction.java | 11 ++--
 .../rules/rewrite/logical/InferPredicates.java     |  3 +-
 .../rules/rewrite/logical/MergeFilters.java        | 10 +--
 .../rewrite/logical/PruneOlapScanPartition.java    |  7 +-
 .../rules/rewrite/logical/PruneOlapScanTablet.java |  3 +-
 .../rewrite/logical/PushApplyUnderFilter.java      |  9 ++-
 .../rewrite/logical/PushFilterInsideJoin.java      |  5 +-
 .../logical/PushdownFilterThroughAggregation.java  | 18 +++---
 .../rewrite/logical/PushdownFilterThroughJoin.java | 10 +--
 .../logical/PushdownFilterThroughProject.java      |  2 +-
 .../logical/PushdownFilterThroughRepeat.java       | 15 ++---
 .../logical/PushdownFilterThroughSetOperation.java | 14 ++--
 .../logical/PushdownJoinOtherCondition.java        |  5 +-
 .../nereids/rules/rewrite/logical/ReorderJoin.java | 10 +--
 .../doris/nereids/stats/StatsCalculator.java       |  2 +-
 .../doris/nereids/trees/plans/algebra/Filter.java  | 10 +--
 .../nereids/trees/plans/logical/LogicalFilter.java | 71 +++++++++++----------
 .../nereids/trees/plans/logical/LogicalHaving.java | 37 +++++------
 .../trees/plans/physical/PhysicalFilter.java       | 45 ++++++-------
 .../apache/doris/nereids/util/ExpressionUtils.java | 29 ++++++++-
 .../org/apache/doris/nereids/util/PlanUtils.java   |  8 +--
 .../java/org/apache/doris/nereids/util/Utils.java  |  3 +-
 .../nereids/datasets/ssb/SSBJoinReorderTest.java   |  2 +-
 .../translator/PhysicalPlanTranslatorTest.java     |  3 +-
 .../org/apache/doris/nereids/memo/MemoTest.java    |  5 +-
 .../pattern/GroupExpressionMatchingTest.java       |  5 +-
 .../nereids/rules/analysis/BindFunctionTest.java   |  2 +-
 .../nereids/rules/analysis/CheckAnalysisTest.java  |  3 +-
 .../nereids/rules/analysis/CheckRowPolicyTest.java |  4 +-
 .../rules/analysis/FillUpMissingSlotsTest.java     | 50 +++++++--------
 .../ReplaceExpressionByChildOutputTest.java        |  3 +-
 ...ctSingleTableExpressionFromDisjunctionTest.java | 25 ++++----
 .../rules/rewrite/logical/InferPredicatesTest.java | 74 +++++++++++-----------
 .../rules/rewrite/logical/MergeFiltersTest.java    | 14 ++--
 .../logical/PruneOlapScanPartitionTest.java        | 13 ++--
 .../rewrite/logical/PruneOlapScanTabletTest.java   |  7 +-
 .../PushdownFilterThroughAggregationTest.java      | 10 +--
 .../logical/PushdownFilterThroughJoinTest.java     | 32 +++++-----
 .../logical/PushdownJoinOtherConditionTest.java    |  9 +--
 .../doris/nereids/stats/StatsCalculatorTest.java   | 10 +--
 .../doris/nereids/trees/plans/PlanEqualsTest.java  | 13 ++--
 .../nereids/trees/plans/PlanToStringTest.java      |  3 +-
 .../doris/nereids/util/LogicalPlanBuilder.java     | 10 ++-
 .../org/apache/doris/nereids/util/PlanChecker.java |  2 +-
 .../apache/doris/nereids/util/PlanUtilsTest.java   |  8 +--
 64 files changed, 448 insertions(+), 375 deletions(-)

diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
index b05741b5b9..26243a9b55 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/CascadesContext.java
@@ -261,7 +261,7 @@ public class CascadesContext {
     }
 
     private Set<UnboundRelation> extractUnboundRelationFromFilter(LogicalFilter filter) {
-        Set<SubqueryExpr> subqueryExprs = filter.getPredicates()
+        Set<SubqueryExpr> subqueryExprs = filter.getPredicate()
                 .collect(SubqueryExpr.class::isInstance);
         Set<UnboundRelation> relations = new HashSet<>();
         for (SubqueryExpr expr : subqueryExprs) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
index bf1d327830..2a1cce94b6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslator.java
@@ -1081,7 +1081,7 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, Pla
     public PlanFragment visitPhysicalFilter(PhysicalFilter<? extends Plan> filter, PlanTranslatorContext context) {
         if (filter.child(0) instanceof PhysicalHashJoin) {
             PhysicalHashJoin join = (PhysicalHashJoin<?, ?>) filter.child(0);
-            join.getFilterConjuncts().addAll(ExpressionUtils.extractConjunction(filter.getPredicates()));
+            join.getFilterConjuncts().addAll(filter.getConjuncts());
         }
         PlanFragment inputFragment = filter.child(0).accept(this, context);
 
@@ -1107,9 +1107,9 @@ public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, Pla
     private void addConjunctsToPlanNode(PhysicalFilter<? extends Plan> filter,
             PlanNode planNode,
             PlanTranslatorContext context) {
-        Expression expression = filter.getPredicates();
-        List<Expression> expressionList = ExpressionUtils.extractConjunction(expression);
-        expressionList.stream().map(e -> ExpressionTranslator.translate(e, context)).forEach(planNode::addConjunct);
+        filter.getConjuncts().stream()
+                .map(e -> ExpressionTranslator.translate(e, context))
+                .forEach(planNode::addConjunct);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 2801b3454d..e5f71076c3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -1166,7 +1166,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
                     List<NamedExpression> projects = getNamedExpressions(selectColumnCtx.namedExpressionSeq());
                     project = new LogicalProject<>(projects, Collections.emptyList(), aggregate, isDistinct);
                 }
-                return new LogicalHaving<>(getExpression((havingClause.get().booleanExpression())), project);
+                return new LogicalHaving<>(ExpressionUtils.extractConjunctionToSet(
+                        getExpression((havingClause.get().booleanExpression()))), project);
             } else {
                 LogicalPlan having = withHaving(aggregate, havingClause);
                 return withProjection(having, selectColumnCtx, aggClause, isDistinct);
@@ -1308,8 +1309,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
     private LogicalPlan withFilter(LogicalPlan input, Optional<WhereClauseContext> whereCtx) {
         return input.optionalMap(whereCtx, () ->
-            new LogicalFilter<>(getExpression((whereCtx.get().booleanExpression())), input)
-        );
+            new LogicalFilter<>(ExpressionUtils.extractConjunctionToSet(
+                    getExpression(whereCtx.get().booleanExpression())), input));
     }
 
     private LogicalPlan withAggregate(LogicalPlan input, SelectColumnClauseContext selectCtx,
@@ -1343,7 +1344,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
             if (!(input instanceof Aggregate)) {
                 throw new ParseException("Having clause should be applied against an aggregation.", havingCtx.get());
             }
-            return new LogicalHaving<>(getExpression((havingCtx.get().booleanExpression())), input);
+            return new LogicalHaving<>(ExpressionUtils.extractConjunctionToSet(
+                    getExpression((havingCtx.get().booleanExpression()))), input);
         });
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
index af878129c4..fea3dfb22f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/post/Validator.java
@@ -27,7 +27,9 @@ import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
 
 import com.google.common.base.Preconditions;
 
+import java.util.Collection;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * validator plan.
@@ -50,7 +52,8 @@ public class Validator extends PlanPostProcessor {
 
     @Override
     public Plan visitPhysicalFilter(PhysicalFilter<? extends Plan> filter, CascadesContext context) {
-        Preconditions.checkArgument(filter.getPredicates() != BooleanLiteral.TRUE);
+        Preconditions.checkArgument(!filter.getConjuncts().isEmpty()
+                && filter.getPredicate() != BooleanLiteral.TRUE);
 
         Plan child = filter.child();
         // Forbidden filter-project, we must make filter-project -> project-filter.
@@ -60,7 +63,9 @@ public class Validator extends PlanPostProcessor {
 
         // Check filter is from child output.
         Set<Slot> childOutputSet = child.getOutputSet();
-        Set<Slot> slotsUsedByFilter = filter.getPredicates().collect(Slot.class::isInstance);
+        Set<Slot> slotsUsedByFilter = filter.getConjuncts().stream()
+                .<Set<Slot>>map(expr -> expr.collect(Slot.class::isInstance))
+                .flatMap(Collection::stream).collect(Collectors.toSet());
         for (Slot slot : slotsUsedByFilter) {
             Preconditions.checkState(childOutputSet.contains(slot));
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java
index 4293c5962d..d2d427f4ff 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/AnalyzeSubquery.java
@@ -28,12 +28,14 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SubqueryExpr;
 import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalApply;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -52,18 +54,17 @@ public class AnalyzeSubquery implements AnalysisRuleFactory {
         return ImmutableList.of(
             RuleType.ANALYZE_FILTER_SUBQUERY.build(
                 logicalFilter().thenApply(ctx -> {
-                    LogicalFilter filter = ctx.root;
-                    Set<SubqueryExpr> subqueryExprs = filter.getPredicates()
-                            .collect(SubqueryExpr.class::isInstance);
+                    LogicalFilter<GroupPlan> filter = ctx.root;
+                    Set<SubqueryExpr> subqueryExprs = filter.getPredicate().collect(SubqueryExpr.class::isInstance);
                     if (subqueryExprs.isEmpty()) {
                         return filter;
                     }
 
                     // first step: Replace the subquery of predicate in LogicalFilter
                     // second step: Replace subquery with LogicalApply
-                    return new LogicalFilter<>(new ReplaceSubquery().replace(filter.getPredicates()),
+                    return new LogicalFilter<>(new ReplaceSubquery().replace(filter.getConjuncts()),
                             analyzedSubquery(
-                            subqueryExprs, (LogicalPlan) filter.child(), ctx.cascadesContext
+                            subqueryExprs, filter.child(), ctx.cascadesContext
                     ));
                 })
             )
@@ -110,23 +111,24 @@ public class AnalyzeSubquery implements AnalysisRuleFactory {
      *      2.filter(True);
      *      3.filter(True);
      */
-    private static class ReplaceSubquery extends DefaultExpressionRewriter {
-        public Expression replace(Expression expression) {
-            return (Expression) expression.accept(this, null);
+    private static class ReplaceSubquery extends DefaultExpressionRewriter<Void> {
+        public Set<Expression> replace(Set<Expression> expressions) {
+            return expressions.stream().map(expr -> expr.accept(this, null))
+                    .collect(ImmutableSet.toImmutableSet());
         }
 
         @Override
-        public Object visitExistsSubquery(Exists exists, Object context) {
+        public Expression visitExistsSubquery(Exists exists, Void context) {
             return BooleanLiteral.TRUE;
         }
 
         @Override
-        public Object visitInSubquery(InSubquery in, Object context) {
+        public Expression visitInSubquery(InSubquery in, Void context) {
             return BooleanLiteral.TRUE;
         }
 
         @Override
-        public Object visitScalarSubquery(ScalarSubquery scalar, Object context) {
+        public Expression visitScalarSubquery(ScalarSubquery scalar, Void context) {
             return scalar.getQueryPlan().getOutput().get(0);
         }
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
index 0ddb802554..d6ecf5d16f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindFunction.java
@@ -48,6 +48,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
 import org.apache.doris.nereids.trees.plans.logical.LogicalTVFRelation;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Locale;
@@ -100,15 +101,15 @@ public class BindFunction implements AnalysisRuleFactory {
             RuleType.BINDING_FILTER_FUNCTION.build(
                logicalFilter().thenApply(ctx -> {
                    LogicalFilter<GroupPlan> filter = ctx.root;
-                   List<Expression> predicates = bind(filter.getExpressions(), ctx.connectContext.getEnv());
-                   return new LogicalFilter<>(predicates.get(0), filter.child());
+                   List<Expression> conjuncts = bind(filter.getExpressions(), ctx.connectContext.getEnv());
+                   return new LogicalFilter<>(ImmutableSet.copyOf(conjuncts), filter.child());
                })
             ),
             RuleType.BINDING_HAVING_FUNCTION.build(
                 logicalHaving().thenApply(ctx -> {
                     LogicalHaving<GroupPlan> having = ctx.root;
-                    List<Expression> predicates = bind(having.getExpressions(), ctx.connectContext.getEnv());
-                    return new LogicalHaving<>(predicates.get(0), having.child());
+                    List<Expression> conjuncts = bind(having.getExpressions(), ctx.connectContext.getEnv());
+                    return new LogicalHaving<>(ImmutableSet.copyOf(conjuncts), having.child());
                 })
             ),
             RuleType.BINDING_SORT_FUNCTION.build(
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
index f7a126a411..975a538e99 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindSlotReference.java
@@ -67,6 +67,7 @@ import org.apache.doris.planner.PlannerContext;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
 import org.apache.commons.lang.StringUtils;
 
 import java.util.ArrayList;
@@ -117,9 +118,9 @@ public class BindSlotReference implements AnalysisRuleFactory {
             RuleType.BINDING_FILTER_SLOT.build(
                 logicalFilter().when(Plan::canBind).thenApply(ctx -> {
                     LogicalFilter<GroupPlan> filter = ctx.root;
-                    Expression boundPredicates = bind(filter.getPredicates(), filter.children(),
-                            filter, ctx.cascadesContext);
-                    return new LogicalFilter<>(boundPredicates, filter.child());
+                    Set<Expression> boundConjuncts
+                            = bind(filter.getConjuncts(), filter.children(), filter, ctx.cascadesContext);
+                    return new LogicalFilter<>(boundConjuncts, filter.child());
                 })
             ),
             RuleType.BINDING_JOIN_SLOT.build(
@@ -275,10 +276,11 @@ public class BindSlotReference implements AnalysisRuleFactory {
                     Set<Slot> boundSlots = Stream.concat(Stream.of(childPlan), childPlan.children().stream())
                             .flatMap(plan -> plan.getOutput().stream())
                             .collect(Collectors.toSet());
-                    Expression boundPredicates = new SlotBinder(
-                            toScope(new ArrayList<>(boundSlots)), having, ctx.cascadesContext
-                    ).bind(having.getPredicates());
-                    return new LogicalHaving<>(boundPredicates, having.child());
+                    SlotBinder binder = new SlotBinder(toScope(Lists.newArrayList(boundSlots)), having,
+                            ctx.cascadesContext);
+                    Set<Expression> boundConjuncts = having.getConjuncts().stream().map(binder::bind)
+                            .collect(Collectors.toSet());
+                    return new LogicalHaving<>(boundConjuncts, having.child());
                 })
             ),
             RuleType.BINDING_ONE_ROW_RELATION_SLOT.build(
@@ -360,6 +362,13 @@ public class BindSlotReference implements AnalysisRuleFactory {
             .collect(Collectors.toList());
     }
 
+    private <E extends Expression> Set<E> bind(Set<E> exprList, List<Plan> inputs, Plan plan,
+            CascadesContext cascadesContext) {
+        return exprList.stream()
+                .map(expr -> bind(expr, inputs, plan, cascadesContext))
+                .collect(Collectors.toSet());
+    }
+
     private <E extends Expression> E bind(E expr, List<Plan> inputs, Plan plan, CascadesContext cascadesContext) {
         List<Slot> boundedSlots = inputs.stream()
                 .flatMap(input -> input.getOutput().stream())
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckPolicy.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckPolicy.java
index 74db686e69..f062eff0b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckPolicy.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckPolicy.java
@@ -23,6 +23,7 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.logical.LogicalCheckPolicy;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.ImmutableList;
 
@@ -48,7 +49,7 @@ public class CheckPolicy implements AnalysisRuleFactory {
                     if (!filter.isPresent()) {
                         return relation;
                     }
-                    return new LogicalFilter(filter.get(), relation);
+                    return new LogicalFilter(ExpressionUtils.extractConjunctionToSet(filter.get()), relation);
                 })
             )
         );
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
index b1d01eac41..3ba73d96f8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlots.java
@@ -114,15 +114,15 @@ public class FillUpMissingSlots implements AnalysisRuleFactory {
                 logicalHaving(aggregate()).then(having -> {
                     Aggregate aggregate = having.child();
                     Resolver resolver = new Resolver(aggregate);
-                    resolver.resolve(having.getPredicates());
+                    having.getConjuncts().forEach(resolver::resolve);
                     return createPlan(resolver, having.child(), (r, a) -> {
-                        Expression newPredicates = ExpressionUtils.replace(
-                                having.getPredicates(), r.getSubstitution());
-                        return new LogicalFilter<>(newPredicates, a);
+                        Set<Expression> newConjuncts = ExpressionUtils.replace(
+                                having.getConjuncts(), r.getSubstitution());
+                        return new LogicalFilter<>(newConjuncts, a);
                     });
                 })),
             RuleType.FILL_UP_HAVING_PROJECT.build(
-                logicalHaving(logicalProject()).then(having -> new LogicalFilter<>(having.getPredicates(),
+                logicalHaving(logicalProject()).then(having -> new LogicalFilter<>(having.getConjuncts(),
                     having.child()))
             )
         );
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
index fdf73c69d7..b4c3fde013 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/ExpressionRewrite.java
@@ -28,12 +28,15 @@ 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.LogicalProject;
+import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -100,11 +103,12 @@ public class ExpressionRewrite implements RewriteRuleFactory {
         @Override
         public Rule build() {
             return logicalFilter().then(filter -> {
-                Expression newExpr = rewriter.rewrite(filter.getPredicates());
-                if (newExpr.equals(filter.getPredicates())) {
+                Set<Expression> newConjuncts = ImmutableSet.copyOf(ExpressionUtils.extractConjunction(
+                        rewriter.rewrite(filter.getPredicate())));
+                if (newConjuncts.equals(filter.getConjuncts())) {
                     return filter;
                 }
-                return new LogicalFilter<>(newExpr, filter.child());
+                return new LogicalFilter<>(newConjuncts, filter.child());
             }).toRule(RuleType.REWRITE_FILTER_EXPRESSION);
         }
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalFilterToPhysicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalFilterToPhysicalFilter.java
index 48467ad32a..3d0f77f28f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalFilterToPhysicalFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalFilterToPhysicalFilter.java
@@ -28,7 +28,7 @@ public class LogicalFilterToPhysicalFilter extends OneImplementationRuleFactory
     @Override
     public Rule build() {
         return logicalFilter().then(filter -> new PhysicalFilter<>(
-            filter.getPredicates(),
+            filter.getConjuncts(),
             filter.getLogicalProperties(),
             filter.child())
         ).toRule(RuleType.LOGICAL_FILTER_TO_PHYSICAL_FILTER_RULE);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/AbstractSelectMaterializedIndexRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/AbstractSelectMaterializedIndexRule.java
index 03fe7de431..8d26927a89 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/AbstractSelectMaterializedIndexRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/AbstractSelectMaterializedIndexRule.java
@@ -66,6 +66,7 @@ public abstract class AbstractSelectMaterializedIndexRule {
             MaterializedIndex index,
             LogicalOlapScan scan,
             Set<Slot> requiredScanOutput) {
+
         OlapTable table = scan.getTable();
         // Scan slot exprId -> slot name
         Map<ExprId, String> exprIdToName = Stream.concat(
@@ -91,7 +92,7 @@ public abstract class AbstractSelectMaterializedIndexRule {
     protected long selectBestIndex(
             List<MaterializedIndex> candidates,
             LogicalOlapScan scan,
-            List<Expression> predicates) {
+            Set<Expression> predicates) {
         OlapTable table = scan.getTable();
         // Scan slot exprId -> slot name
         Map<ExprId, String> exprIdToName = scan.getOutput()
@@ -122,7 +123,7 @@ public abstract class AbstractSelectMaterializedIndexRule {
     protected List<MaterializedIndex> matchPrefixMost(
             LogicalOlapScan scan,
             List<MaterializedIndex> candidate,
-            List<Expression> predicates,
+            Set<Expression> predicates,
             Map<ExprId, String> exprIdToName) {
         Map<Boolean, Set<String>> split = filterCanUsePrefixIndexAndSplitByEquality(predicates, exprIdToName);
         Set<String> equalColNames = split.getOrDefault(true, ImmutableSet.of());
@@ -146,8 +147,8 @@ public abstract class AbstractSelectMaterializedIndexRule {
      * when comparing the key column.
      */
     private Map<Boolean, Set<String>> filterCanUsePrefixIndexAndSplitByEquality(
-            List<Expression> conjunct, Map<ExprId, String> exprIdToColName) {
-        return conjunct.stream()
+            Set<Expression> conjuncts, Map<ExprId, String> exprIdToColName) {
+        return conjuncts.stream()
                 .map(expr -> PredicateChecker.canUsePrefixIndex(expr, exprIdToColName))
                 .filter(result -> !result.equals(PrefixIndexCheckResult.FAILURE))
                 .collect(Collectors.groupingBy(
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithAggregate.java
index 5c0a017196..f1ed0236b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithAggregate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithAggregate.java
@@ -88,7 +88,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
                     SelectResult result = select(
                             scan,
                             agg.getInputSlots(),
-                            ImmutableList.of(),
+                            ImmutableSet.of(),
                             extractAggFunctionAndReplaceSlot(agg, Optional.empty()),
                             agg.getGroupByExpressions());
                     if (result.exprRewriteMap.isEmpty()) {
@@ -153,7 +153,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
                             SelectResult result = select(
                                     scan,
                                     project.getInputSlots(),
-                                    ImmutableList.of(),
+                                    ImmutableSet.of(),
                                     extractAggFunctionAndReplaceSlot(agg,
                                             Optional.of(project)),
                                     agg.getGroupByExpressions()
@@ -233,7 +233,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
                             SelectResult result = select(
                                     scan,
                                     project.getInputSlots(),
-                                    ImmutableList.of(),
+                                    ImmutableSet.of(),
                                     extractAggFunctionAndReplaceSlot(agg, Optional.of(project)),
                                     ExpressionUtils.replace(agg.getGroupByExpressions(),
                                             project.getAliasToProducer())
@@ -277,7 +277,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
     private SelectResult select(
             LogicalOlapScan scan,
             Set<Slot> requiredScanOutput,
-            List<Expression> predicates,
+            Set<Expression> predicates,
             List<AggregateFunction> aggregateFunctions,
             List<Expression> groupingExprs) {
         Preconditions.checkArgument(scan.getOutputSet().containsAll(requiredScanOutput),
@@ -411,13 +411,13 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
     private PreAggStatus checkPreAggStatus(
             LogicalOlapScan olapScan,
             long indexId,
-            List<Expression> predicates,
+            Set<Expression> predicates,
             List<AggregateFunction> aggregateFuncs,
             List<Expression> groupingExprs) {
         CheckContext checkContext = new CheckContext(olapScan, indexId);
         return checkAggregateFunctions(aggregateFuncs, checkContext)
                 .offOrElse(() -> checkGroupingExprs(groupingExprs, checkContext))
-                .offOrElse(() -> checkPredicates(predicates, checkContext));
+                .offOrElse(() -> checkPredicates(ImmutableList.copyOf(predicates), checkContext));
     }
 
     /**
@@ -615,7 +615,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
     private AggRewriteResult rewriteAgg(MaterializedIndex index,
             LogicalOlapScan scan,
             Set<Slot> requiredScanOutput,
-            List<Expression> predicates,
+            Set<Expression> predicates,
             List<AggregateFunction> aggregateFunctions,
             List<Expression> groupingExprs) {
         ExprRewriteMap exprRewriteMap = new ExprRewriteMap();
@@ -631,7 +631,7 @@ public class SelectMaterializedIndexWithAggregate extends AbstractSelectMaterial
             // The query `select c1, count(distinct c2) from t where c2 > 0 group by c1` can't use the materialized
             // index because we have a filter `c2 > 0` for the aggregated column c2.
             Set<Slot> slotsToReplace = slotMap.keySet();
-            if (!isInputSlotsContainsAny(predicates, slotsToReplace)
+            if (!isInputSlotsContainsAny(ImmutableList.copyOf(predicates), slotsToReplace)
                     && !isInputSlotsContainsAny(groupingExprs, slotsToReplace)) {
                 ImmutableSet<Slot> newRequiredSlots = requiredScanOutput.stream()
                         .map(slot -> (Slot) ExpressionUtils.replace(slot, slotMap))
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithoutAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithoutAggregate.java
index 1976a4adfc..4d55385c07 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithoutAggregate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/mv/SelectMaterializedIndexWithoutAggregate.java
@@ -71,7 +71,7 @@ public class SelectMaterializedIndexWithoutAggregate extends AbstractSelectMater
                             LogicalProject<LogicalOlapScan> project = filter.child();
                             LogicalOlapScan scan = project.child();
                             return filter.withChildren(project.withChildren(
-                                    select(scan, project::getInputSlots, ImmutableList::of)
+                                    select(scan, project::getInputSlots, ImmutableSet::of)
                             ));
                         }).toRule(RuleType.MATERIALIZED_INDEX_FILTER_PROJECT_SCAN),
 
@@ -90,14 +90,14 @@ public class SelectMaterializedIndexWithoutAggregate extends AbstractSelectMater
                         .then(project -> {
                             LogicalOlapScan scan = project.child();
                             return project.withChildren(
-                                    select(scan, project::getInputSlots, ImmutableList::of));
+                                    select(scan, project::getInputSlots, ImmutableSet::of));
                         })
                         .toRule(RuleType.MATERIALIZED_INDEX_PROJECT_SCAN),
 
                 // only scan.
                 logicalOlapScan()
                         .when(this::shouldSelectIndex)
-                        .then(scan -> select(scan, scan::getOutputSet, ImmutableList::of))
+                        .then(scan -> select(scan, scan::getOutputSet, ImmutableSet::of))
                         .toRule(RuleType.MATERIALIZED_INDEX_SCAN)
         );
     }
@@ -113,7 +113,7 @@ public class SelectMaterializedIndexWithoutAggregate extends AbstractSelectMater
     private LogicalOlapScan select(
             LogicalOlapScan scan,
             Supplier<Set<Slot>> requiredScanOutputSupplier,
-            Supplier<List<Expression>> predicatesSupplier) {
+            Supplier<Set<Expression>> predicatesSupplier) {
         switch (scan.getTable().getKeysType()) {
             case AGG_KEYS:
             case UNIQUE_KEYS:
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java
index cd1e6fbea4..fd31a14bdf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnAgg.java
@@ -30,6 +30,8 @@ import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanUtils;
 import org.apache.doris.nereids.util.Utils;
 
+import com.google.common.collect.ImmutableSet;
+
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -63,9 +65,8 @@ public class ApplyPullFilterOnAgg extends OneRewriteRuleFactory {
         return logicalApply(group(), logicalAggregate(logicalFilter())).when(LogicalApply::isCorrelated).then(apply -> {
             LogicalAggregate<LogicalFilter<GroupPlan>> agg = apply.right();
             LogicalFilter<GroupPlan> filter = agg.child();
-            List<Expression> predicates = ExpressionUtils.extractConjunction(filter.getPredicates());
             Map<Boolean, List<Expression>> split = Utils.splitCorrelatedConjuncts(
-                    predicates, apply.getCorrelationSlot());
+                    filter.getConjuncts(), apply.getCorrelationSlot());
             List<Expression> correlatedPredicate = split.get(true);
             List<Expression> unCorrelatedPredicate = split.get(false);
 
@@ -81,7 +82,7 @@ public class ApplyPullFilterOnAgg extends OneRewriteRuleFactory {
             newAggOutput.addAll(newGroupby.stream().map(NamedExpression.class::cast).collect(Collectors.toList()));
             LogicalAggregate newAgg = new LogicalAggregate<>(
                     newGroupby, newAggOutput,
-                    PlanUtils.filterOrSelf(unCorrelatedPredicate, filter.child()));
+                    PlanUtils.filterOrSelf(ImmutableSet.copyOf(unCorrelatedPredicate), filter.child()));
             return new LogicalApply<>(apply.getCorrelationSlot(),
                     apply.getSubqueryExpr(),
                     ExpressionUtils.optionalAnd(correlatedPredicate),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java
index 77b8eef78c..4c66fd422c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ApplyPullFilterOnProjectUnderAgg.java
@@ -75,7 +75,7 @@ public class ApplyPullFilterOnProjectUnderAgg extends OneRewriteRuleFactory {
                     });
 
                     LogicalProject newProject = new LogicalProject<>(newProjects, filter.child());
-                    LogicalFilter newFilter = new LogicalFilter<>(filter.getPredicates(), newProject);
+                    LogicalFilter newFilter = new LogicalFilter<>(filter.getConjuncts(), newProject);
                     LogicalAggregate newAgg = new LogicalAggregate<>(
                             agg.getGroupByExpressions(), agg.getOutputExpressions(), newFilter);
                     return new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateFilter.java
index d068025adf..36c2a0993f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateFilter.java
@@ -20,8 +20,14 @@ package org.apache.doris.nereids.rules.rewrite.logical;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
+import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+
+import com.google.common.collect.Sets;
+
+import java.util.Set;
 
 /**
  * Eliminate filter which is FALSE or TRUE.
@@ -30,14 +36,20 @@ public class EliminateFilter extends OneRewriteRuleFactory {
     @Override
     public Rule build() {
         return logicalFilter()
-                .when(filter -> filter.getPredicates() instanceof BooleanLiteral)
+                .when(filter -> filter.getConjuncts().stream().anyMatch(BooleanLiteral.class::isInstance))
                 .then(filter -> {
-                    if (filter.getPredicates() == BooleanLiteral.FALSE) {
-                        return new LogicalEmptyRelation(filter.getOutput());
-                    } else if (filter.getPredicates() == BooleanLiteral.TRUE) {
+                    Set<Expression> newConjuncts = Sets.newHashSetWithExpectedSize(filter.getConjuncts().size());
+                    for (Expression expression : filter.getConjuncts()) {
+                        if (expression == BooleanLiteral.FALSE) {
+                            return new LogicalEmptyRelation(filter.getOutput());
+                        } else if (expression != BooleanLiteral.TRUE) {
+                            newConjuncts.add(expression);
+                        }
+                    }
+                    if (newConjuncts.isEmpty()) {
                         return filter.child();
                     } else {
-                        throw new RuntimeException("predicates is BooleanLiteral but isn't FALSE or TRUE");
+                        return new LogicalFilter<>(newConjuncts, filter.child());
                     }
                 })
                 .toRule(RuleType.ELIMINATE_FILTER);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateOuterJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateOuterJoin.java
index f88a1ab6b0..1af7e494e7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateOuterJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/EliminateOuterJoin.java
@@ -32,8 +32,8 @@ import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.Maps;
 
-import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Eliminate outer join.
@@ -46,10 +46,10 @@ public class EliminateOuterJoin extends OneRewriteRuleFactory {
                     logicalJoin().when(join -> join.getJoinType().isOuterJoin())
                 ).then(filter -> {
                     LogicalJoin<GroupPlan, GroupPlan> join = filter.child();
-                    List<Expression> conjuncts = filter.getConjuncts();
-                    List<Expression> leftPredicates = ExpressionUtils.extractCoveredConjunction(conjuncts,
+                    Set<Expression> conjuncts = filter.getConjuncts();
+                    Set<Expression> leftPredicates = ExpressionUtils.extractCoveredConjunction(conjuncts,
                             join.left().getOutputSet());
-                    List<Expression> rightPredicates = ExpressionUtils.extractCoveredConjunction(conjuncts,
+                    Set<Expression> rightPredicates = ExpressionUtils.extractCoveredConjunction(conjuncts,
                             join.right().getOutputSet());
                     boolean canFilterLeftNull = canFilterNull(leftPredicates);
                     boolean canFilterRightNull = canFilterNull(rightPredicates);
@@ -82,7 +82,7 @@ public class EliminateOuterJoin extends OneRewriteRuleFactory {
         return joinType;
     }
 
-    private boolean canFilterNull(List<Expression> predicates) {
+    private boolean canFilterNull(Set<Expression> predicates) {
         Literal nullLiteral = Literal.of(null);
         for (Expression predicate : predicates) {
             Map<Expression, Expression> replaceMap = Maps.newHashMap();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
index e54949c952..ca776b83b7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExistsApplyToJoin.java
@@ -38,6 +38,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.ArrayList;
 import java.util.Optional;
@@ -122,8 +123,8 @@ public class ExistsApplyToJoin extends OneRewriteRuleFactory {
                 ImmutableList.of(alias), newLimit);
         LogicalJoin newJoin = new LogicalJoin<>(JoinType.CROSS_JOIN,
                 (LogicalPlan) unapply.left(), newAgg);
-        return new LogicalFilter<>(new EqualTo(newAgg.getOutput().get(0),
-                new IntegerLiteral(0)), newJoin);
+        return new LogicalFilter<>(ImmutableSet.of(new EqualTo(newAgg.getOutput().get(0),
+                new IntegerLiteral(0))), newJoin);
     }
 
     private Plan unCorrelatedExist(LogicalApply unapply) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
index 9317ea6969..3f88fb2533 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunction.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import java.util.List;
@@ -76,8 +77,7 @@ public class ExtractSingleTableExpressionFromDisjunction extends OneRewriteRuleF
             //filter = [(n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY')
             //             or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')]
             //         and ...
-            List<Expression> conjuncts = ExpressionUtils.extractConjunction(filter.getPredicates())
-                    .stream().collect(Collectors.toList());
+            Set<Expression> conjuncts = filter.getConjuncts();
 
             List<Expression> redundants = Lists.newArrayList();
             for (Expression conjunct : conjuncts) {
@@ -118,10 +118,11 @@ public class ExtractSingleTableExpressionFromDisjunction extends OneRewriteRuleF
 
             }
             if (redundants.isEmpty()) {
-                return new LogicalFilter<>(filter.getPredicates(), true, filter.child());
+                return new LogicalFilter<>(filter.getConjuncts(), true, filter.child());
             } else {
-                Expression newPredicate = ExpressionUtils.and(filter.getPredicates(), ExpressionUtils.and(redundants));
-                return new LogicalFilter<>(newPredicate,
+                return new LogicalFilter<>(ImmutableSet.<Expression>builder()
+                        .addAll(filter.getConjuncts())
+                        .addAll(redundants).build(),
                         true, filter.child());
             }
         }).toRule(RuleType.EXTRACT_SINGLE_TABLE_EXPRESSION_FROM_DISJUNCTION);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicates.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicates.java
index e56abd201d..12eb6f0e0a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicates.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicates.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanRewriter;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
@@ -90,7 +91,7 @@ public class InferPredicates extends DefaultPlanRewriter<JobContext> {
         filter.getConjuncts().forEach(filterPredicates::remove);
         if (!filterPredicates.isEmpty()) {
             filterPredicates.addAll(filter.getConjuncts());
-            return new LogicalFilter<>(ExpressionUtils.and(Lists.newArrayList(filterPredicates)), filter.child());
+            return new LogicalFilter<>(ImmutableSet.copyOf(filterPredicates), filter.child());
         }
         return filter;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFilters.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFilters.java
index 4a3aba00ed..a992ca68c9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFilters.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFilters.java
@@ -23,7 +23,8 @@ import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.ImmutableSet;
 
 /**
  * this rule aims to merge consecutive filters.
@@ -48,10 +49,9 @@ public class MergeFilters extends OneRewriteRuleFactory {
     public Rule build() {
         return logicalFilter(logicalFilter()).then(filter -> {
             LogicalFilter<? extends Plan> childFilter = filter.child();
-            Expression predicates = filter.getPredicates();
-            Expression childPredicates = childFilter.getPredicates();
-            Expression mergedPredicates = ExpressionUtils.and(predicates, childPredicates);
-            return new LogicalFilter<>(mergedPredicates, childFilter.child());
+            return new LogicalFilter<>(ImmutableSet.<Expression>builder()
+                    .addAll(filter.getConjuncts())
+                    .addAll(childFilter.getConjuncts()).build(), childFilter.child());
         }).toRule(RuleType.MERGE_FILTERS);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartition.java
index b6e8887f83..cbdeb18b47 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartition.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartition.java
@@ -67,7 +67,6 @@ public class PruneOlapScanPartition extends OneRewriteRuleFactory {
         return logicalFilter(logicalOlapScan()).when(p -> !p.child().isPartitionPruned()).thenApply(ctx -> {
             LogicalFilter<LogicalOlapScan> filter = ctx.root;
             LogicalOlapScan scan = filter.child();
-            Expression predicate = filter.getPredicates();
             OlapTable table = scan.getTable();
             Set<String> partitionColumnNameSet = Utils.execWithReturnVal(table::getPartitionColumnNames);
             PartitionInfo partitionInfo = table.getPartitionInfo();
@@ -76,7 +75,7 @@ public class PruneOlapScanPartition extends OneRewriteRuleFactory {
             if (partitionColumnNameSet.isEmpty() || !partitionInfo.getType().equals(PartitionType.RANGE)) {
                 return ctx.root;
             }
-            List<Expression> expressionList = ExpressionUtils.extractConjunction(predicate);
+            Set<Expression> expressionList = filter.getConjuncts();
             // TODO: Process all partition column for now, better to process required column only.
             Map<String, ColumnRange> columnNameToRange = Maps.newHashMap();
             for (String colName : partitionColumnNameSet) {
@@ -90,11 +89,11 @@ public class PruneOlapScanPartition extends OneRewriteRuleFactory {
             Collection<Long> selectedPartitionId = Utils.execWithReturnVal(partitionPruner::prune);
             LogicalOlapScan rewrittenScan =
                     scan.withSelectedPartitionIds(new ArrayList<>(selectedPartitionId));
-            return new LogicalFilter<>(filter.getPredicates(), rewrittenScan);
+            return new LogicalFilter<>(filter.getConjuncts(), rewrittenScan);
         }).toRule(RuleType.OLAP_SCAN_PARTITION_PRUNE);
     }
 
-    private ColumnRange createColumnRange(String colName, List<Expression> expressionList) {
+    private ColumnRange createColumnRange(String colName, Set<Expression> expressionList) {
         ColumnRange result = ColumnRange.create();
         for (Expression expression : expressionList) {
             Set<SlotReference> slotReferences = expression.collect(SlotReference.class::isInstance);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTablet.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTablet.java
index 0251a8ccf5..b85218149c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTablet.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTablet.java
@@ -41,6 +41,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * prune bucket
@@ -65,7 +66,7 @@ public class PruneOlapScanTablet extends OneRewriteRuleFactory {
                 }).toRule(RuleType.OLAP_SCAN_TABLET_PRUNE);
     }
 
-    private Collection<Long> getSelectedTabletIds(List<Expression> expressions,
+    private Collection<Long> getSelectedTabletIds(Set<Expression> expressions,
             MaterializedIndex index, DistributionInfo info) {
         if (info.getType() != DistributionInfoType.HASH) {
             return index.getTabletIdsInOrder();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java
index fbc09b6e04..b8833a7a9d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushApplyUnderFilter.java
@@ -29,8 +29,11 @@ import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanUtils;
 import org.apache.doris.nereids.util.Utils;
 
+import com.google.common.collect.ImmutableSet;
+
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Exchange apply and filter.
@@ -53,9 +56,9 @@ public class PushApplyUnderFilter extends OneRewriteRuleFactory {
     public Rule build() {
         return logicalApply(group(), logicalFilter()).when(LogicalApply::isCorrelated).then(apply -> {
             LogicalFilter<GroupPlan> filter = apply.right();
-            List<Expression> predicates = ExpressionUtils.extractConjunction(filter.getPredicates());
+            Set<Expression> conjuncts = filter.getConjuncts();
             Map<Boolean, List<Expression>> split = Utils.splitCorrelatedConjuncts(
-                    predicates, apply.getCorrelationSlot());
+                    conjuncts, apply.getCorrelationSlot());
             List<Expression> correlatedPredicate = split.get(true);
             List<Expression> unCorrelatedPredicate = split.get(false);
 
@@ -64,7 +67,7 @@ public class PushApplyUnderFilter extends OneRewriteRuleFactory {
                 return apply;
             }
 
-            Plan child = PlanUtils.filterOrSelf(unCorrelatedPredicate, filter.child());
+            Plan child = PlanUtils.filterOrSelf(ImmutableSet.copyOf(unCorrelatedPredicate), filter.child());
             return new LogicalApply<>(apply.getCorrelationSlot(), apply.getSubqueryExpr(),
                     ExpressionUtils.optionalAnd(correlatedPredicate),
                     apply.left(), child);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
index da7af4c205..d67cea0e0d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushFilterInsideJoin.java
@@ -23,7 +23,8 @@ import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.util.ExpressionUtils;
+
+import com.google.common.collect.Lists;
 
 import java.util.List;
 
@@ -40,7 +41,7 @@ public class PushFilterInsideJoin extends OneRewriteRuleFactory {
                 .when(filter -> filter.child().getJoinType().isCrossJoin()
                         || filter.child().getJoinType().isInnerJoin())
                 .then(filter -> {
-                    List<Expression> otherConditions = ExpressionUtils.extractConjunction(filter.getPredicates());
+                    List<Expression> otherConditions = Lists.newArrayList(filter.getConjuncts());
                     LogicalJoin<GroupPlan, GroupPlan> join = filter.child();
                     otherConditions.addAll(join.getOtherJoinConjuncts());
                     return new LogicalJoin<>(join.getJoinType(), join.getHashJoinConjuncts(),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregation.java
index b7c111db62..575f5c419e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregation.java
@@ -26,13 +26,12 @@ import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanUtils;
 
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 
 import java.util.HashSet;
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -80,9 +79,9 @@ public class PushdownFilterThroughAggregation extends OneRewriteRuleFactory {
                 }
             }
 
-            List<Expression> pushDownPredicates = Lists.newArrayList();
-            List<Expression> filterPredicates = Lists.newArrayList();
-            ExpressionUtils.extractConjunction(filter.getPredicates()).forEach(conjunct -> {
+            Set<Expression> pushDownPredicates = Sets.newHashSet();
+            Set<Expression> filterPredicates = Sets.newHashSet();
+            filter.getConjuncts().forEach(conjunct -> {
                 Set<Slot> conjunctSlots = conjunct.getInputSlots();
                 if (canPushDownSlots.containsAll(conjunctSlots)) {
                     pushDownPredicates.add(conjunct);
@@ -96,15 +95,14 @@ public class PushdownFilterThroughAggregation extends OneRewriteRuleFactory {
     }
 
     private Plan pushDownPredicate(LogicalFilter filter, LogicalAggregate aggregate,
-            List<Expression> pushDownPredicates, List<Expression> filterPredicates) {
+            Set<Expression> pushDownPredicates, Set<Expression> filterPredicates) {
         if (pushDownPredicates.size() == 0) {
             // nothing pushed down, just return origin plan
             return filter;
         }
-        LogicalFilter bottomFilter = new LogicalFilter<>(ExpressionUtils.and(pushDownPredicates),
-                aggregate.child(0));
+        LogicalFilter bottomFilter = new LogicalFilter<>(pushDownPredicates, aggregate.child(0));
 
-        aggregate = aggregate.withChildren(Lists.newArrayList(bottomFilter));
+        aggregate = aggregate.withChildren(ImmutableList.of(bottomFilter));
         return PlanUtils.filterOrSelf(filterPredicates, aggregate);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
index 48e7141dc0..e95f7bb229 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoin.java
@@ -27,11 +27,11 @@ import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanUtils;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 import java.util.List;
 import java.util.Set;
@@ -96,7 +96,7 @@ public class PushdownFilterThroughJoin extends OneRewriteRuleFactory {
 
             LogicalJoin<GroupPlan, GroupPlan> join = filter.child();
 
-            List<Expression> predicates = ExpressionUtils.extractConjunction(filter.getPredicates());
+            Set<Expression> predicates = filter.getConjuncts();
 
             List<Expression> filterPredicates = Lists.newArrayList();
             List<Expression> joinConditions = Lists.newArrayList();
@@ -113,9 +113,9 @@ public class PushdownFilterThroughJoin extends OneRewriteRuleFactory {
                 }
             }
 
-            List<Expression> leftPredicates = Lists.newArrayList();
-            List<Expression> rightPredicates = Lists.newArrayList();
-            List<Expression> remainingPredicates = Lists.newArrayList();
+            Set<Expression> leftPredicates = Sets.newHashSet();
+            Set<Expression> rightPredicates = Sets.newHashSet();
+            Set<Expression> remainingPredicates = Sets.newHashSet();
             for (Expression p : filterPredicates) {
                 Set<Slot> slots = p.collect(SlotReference.class::isInstance);
                 if (slots.isEmpty()) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughProject.java
index 2a41a75de9..ac56572491 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughProject.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughProject.java
@@ -40,7 +40,7 @@ public class PushdownFilterThroughProject extends OneRewriteRuleFactory {
             return new LogicalProject<>(
                     project.getProjects(),
                     new LogicalFilter<>(
-                            ExpressionUtils.replace(filter.getPredicates(), project.getAliasToProducer()),
+                            ExpressionUtils.replace(filter.getConjuncts(), project.getAliasToProducer()),
                             project.child()
                     )
             );
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughRepeat.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughRepeat.java
index d726858299..0d5f2c3f64 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughRepeat.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughRepeat.java
@@ -26,13 +26,11 @@ import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalRepeat;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanUtils;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -73,9 +71,9 @@ public class PushdownFilterThroughRepeat extends OneRewriteRuleFactory {
                 return filter;
             }
 
-            List<Expression> pushedPredicates = Lists.newArrayList();
-            List<Expression> notPushedPredicates = Lists.newArrayList();
-            for (Expression conjunct : ExpressionUtils.extractConjunction(filter.getPredicates())) {
+            Set<Expression> pushedPredicates = Sets.newHashSet();
+            Set<Expression> notPushedPredicates = Sets.newHashSet();
+            for (Expression conjunct : filter.getConjuncts()) {
                 Set<Slot> conjunctSlots = conjunct.getInputSlots();
                 if (commonGroupingSetExpressions.containsAll(conjunctSlots)) {
                     pushedPredicates.add(conjunct);
@@ -88,13 +86,12 @@ public class PushdownFilterThroughRepeat extends OneRewriteRuleFactory {
     }
 
     private Plan pushDownPredicate(LogicalFilter filter, LogicalRepeat repeat,
-                                   List<Expression> pushedPredicates, List<Expression> notPushedPredicates) {
+                                   Set<Expression> pushedPredicates, Set<Expression> notPushedPredicates) {
         if (pushedPredicates.size() == 0) {
             // nothing pushed down, just return origin plan
             return filter;
         }
-        LogicalFilter bottomFilter = new LogicalFilter<>(ExpressionUtils.and(pushedPredicates),
-                repeat.child(0));
+        LogicalFilter bottomFilter = new LogicalFilter<>(pushedPredicates, repeat.child(0));
 
         repeat = repeat.withChildren(ImmutableList.of(bottomFilter));
         return PlanUtils.filterOrSelf(notPushedPredicates, repeat);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughSetOperation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughSetOperation.java
index f32a8eb53b..a5af6c93e2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughSetOperation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughSetOperation.java
@@ -29,12 +29,12 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalSetOperation;
 import org.apache.doris.nereids.trees.plans.logical.LogicalUnion;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
-import com.google.common.collect.Lists;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Convert the expression in the filter into the output column corresponding to the child node and push it down.
@@ -58,14 +58,12 @@ public class PushdownFilterThroughSetOperation extends OneRewriteRuleFactory {
                     replaceMap.put(output, child.getOutput().get(i));
                 }
 
-                List<Expression> newFilterPredicates = Lists.newArrayList();
-                ExpressionUtils.extractConjunction(filter.getPredicates()).forEach(conjunct -> {
-                    newFilterPredicates.add(ExpressionUtils.replace(conjunct, replaceMap));
-                });
-                newChildren.add(new LogicalFilter<>(ExpressionUtils.and(newFilterPredicates), child));
+                Set<Expression> newFilterPredicates = filter.getConjuncts().stream().map(conjunct ->
+                        ExpressionUtils.replace(conjunct, replaceMap)).collect(Collectors.toSet());
+                newChildren.add(new LogicalFilter<>(newFilterPredicates, child));
             }
             if (setOperation instanceof LogicalUnion && setOperation.getQualifier() == Qualifier.DISTINCT) {
-                return new LogicalFilter<>(filter.getPredicates(),
+                return new LogicalFilter<>(filter.getConjuncts(),
                         ((LogicalUnion) setOperation).withHasPushedFilter().withChildren(newChildren));
             }
             return setOperation.withNewChildren(newChildren);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
index b5fe6c5cad..2857c51782 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherCondition.java
@@ -29,6 +29,7 @@ import org.apache.doris.nereids.util.PlanUtils;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 
 import java.util.List;
 import java.util.Set;
@@ -63,8 +64,8 @@ public class PushdownJoinOtherCondition extends OneRewriteRuleFactory {
                 .then(join -> {
                     List<Expression> otherJoinConjuncts = join.getOtherJoinConjuncts();
                     List<Expression> remainingOther = Lists.newArrayList();
-                    List<Expression> leftConjuncts = Lists.newArrayList();
-                    List<Expression> rightConjuncts = Lists.newArrayList();
+                    Set<Expression> leftConjuncts = Sets.newHashSet();
+                    Set<Expression> rightConjuncts = Sets.newHashSet();
 
                     for (Expression otherConjunct : otherJoinConjuncts) {
                         if (PUSH_DOWN_LEFT_VALID_TYPE.contains(join.getJoinType())
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
index cfd5b2ca1c..891fc492e7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/ReorderJoin.java
@@ -37,10 +37,10 @@ import org.apache.doris.nereids.util.PlanUtils;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -102,7 +102,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
         // Implicit rely on {rule: MergeFilters}, so don't exist filter--filter--join.
         if (plan instanceof LogicalFilter) {
             LogicalFilter<?> filter = (LogicalFilter<?>) plan;
-            joinFilter.addAll(ExpressionUtils.extractConjunction(filter.getPredicates()));
+            joinFilter.addAll(filter.getConjuncts());
             join = (LogicalJoin<?, ?>) filter.child();
         } else {
             join = (LogicalJoin<?, ?>) plan;
@@ -205,7 +205,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
      */
     public Plan multiJoinToJoin(MultiJoin multiJoin, Map<Plan, JoinHintType> planToHintType) {
         if (multiJoin.arity() == 1) {
-            return PlanUtils.filterOrSelf(multiJoin.getJoinFilter(), multiJoin.child(0));
+            return PlanUtils.filterOrSelf(ImmutableSet.copyOf(multiJoin.getJoinFilter()), multiJoin.child(0));
         }
 
         Builder<Plan> builder = ImmutableList.builder();
@@ -267,7 +267,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
                 right = children.get(1);
             }
 
-            return PlanUtils.filterOrSelf(remainingFilter, new LogicalJoin<>(
+            return PlanUtils.filterOrSelf(ImmutableSet.copyOf(remainingFilter), new LogicalJoin<>(
                     multiJoinHandleChildren.getJoinType(),
                     ExpressionUtils.EMPTY_CONDITION, multiJoinHandleChildren.getNotInnerJoinConditions(),
                     JoinHint.fromRightPlanHintType(planToHintType.getOrDefault(right, JoinHintType.NONE)),
@@ -290,7 +290,7 @@ public class ReorderJoin extends OneRewriteRuleFactory {
             left = join;
         }
 
-        return PlanUtils.filterOrSelf(new ArrayList<>(joinFilter), left);
+        return PlanUtils.filterOrSelf(joinFilter, left);
     }
 
     /**
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
index 21bfebf78f..919e510a24 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/stats/StatsCalculator.java
@@ -324,7 +324,7 @@ public class StatsCalculator extends DefaultPlanVisitor<StatsDeriveResult, Void>
         StatsDeriveResult stats = groupExpression.childStatistics(0);
         FilterEstimation filterEstimation =
                 new FilterEstimation(stats);
-        return filterEstimation.estimate(filter.getPredicates());
+        return filterEstimation.estimate(filter.getPredicate());
     }
 
     // TODO: 1. Subtract the pruned partition
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Filter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Filter.java
index 1e4048f582..626ee2d6b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Filter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/algebra/Filter.java
@@ -20,15 +20,17 @@ package org.apache.doris.nereids.trees.plans.algebra;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
-import java.util.List;
+import com.google.common.base.Suppliers;
+
+import java.util.Set;
 
 /**
  * Common interface for logical/physical filter.
  */
 public interface Filter {
-    Expression getPredicates();
+    Set<Expression> getConjuncts();
 
-    default List<Expression> getConjuncts() {
-        return ExpressionUtils.extractConjunction(getPredicates());
+    default Expression getPredicate() {
+        return Suppliers.memoize(() -> ExpressionUtils.and(getConjuncts().toArray(new Expression[0]))).get();
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
index ba82ab0491..1c6525377e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
@@ -30,10 +30,14 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -41,52 +45,56 @@ import java.util.stream.Stream;
  * Logical filter plan.
  */
 public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> implements Filter {
-    private final Expression predicates;
+    private final Set<Expression> conjuncts;
 
     private final boolean singleTableExpressionExtracted;
 
-    public LogicalFilter(Expression predicates, CHILD_TYPE child) {
-        this(predicates, Optional.empty(), Optional.empty(), child);
+    public LogicalFilter(Set<Expression> conjuncts, CHILD_TYPE child) {
+        this(conjuncts, Optional.empty(), Optional.empty(), child);
     }
 
-    public LogicalFilter(Expression predicates,
-            boolean singleTableExpressionExtracted,
+    public LogicalFilter(Set<Expression> conjuncts, boolean singleTableExpressionExtracted,
             CHILD_TYPE child) {
-        this(predicates, Optional.empty(), singleTableExpressionExtracted,
+        this(conjuncts, Optional.empty(), singleTableExpressionExtracted,
                 Optional.empty(), child);
     }
 
-    public LogicalFilter(Expression predicates, Optional<GroupExpression> groupExpression,
+    public LogicalFilter(Set<Expression> conjuncts, Optional<GroupExpression> groupExpression,
             Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
-        this(predicates, groupExpression, false, logicalProperties, child);
+        this(conjuncts, groupExpression, false, logicalProperties, child);
     }
 
-    public LogicalFilter(Expression predicates, Optional<GroupExpression> groupExpression,
+    public LogicalFilter(Set<Expression> conjuncts, Optional<GroupExpression> groupExpression,
             boolean singleTableExpressionExtracted,
             Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
         super(PlanType.LOGICAL_FILTER, groupExpression, logicalProperties, child);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
+        this.conjuncts = ImmutableSet.copyOf(Objects.requireNonNull(conjuncts, "conjuncts can not be null"));
         this.singleTableExpressionExtracted = singleTableExpressionExtracted;
     }
 
-    public Expression getPredicates() {
-        return predicates;
+    @Override
+    public Set<Expression> getConjuncts() {
+        return conjuncts;
+    }
+
+    public List<Expression> getExpressions() {
+        return ImmutableList.of(getPredicate());
     }
 
     @Override
     public List<Plan> extraPlans() {
-        if (predicates != null) {
-            return predicates.children().stream()
-                .flatMap(m -> {
-                    if (m instanceof SubqueryExpr) {
-                        return Stream.of(
-                                new LogicalSubQueryAlias<>(m.toSql(), ((SubqueryExpr) m).getQueryPlan()));
-                    } else {
-                        return new LogicalFilter<Plan>(m, child()).extraPlans().stream();
-                    }
-                }).collect(Collectors.toList());
+        if (conjuncts != null) {
+            return conjuncts.stream().map(Expression::children)
+                    .flatMap(Collection::stream)
+                    .flatMap(m -> {
+                        if (m instanceof SubqueryExpr) {
+                            return Stream.of(new LogicalSubQueryAlias<>(m.toSql(), ((SubqueryExpr) m).getQueryPlan()));
+                        } else {
+                            return new LogicalFilter<Plan>(ImmutableSet.of(m), child()).extraPlans().stream();
+                        }
+                    }).collect(Collectors.toList());
         }
-        return ImmutableList.of();
+        return Collections.emptyList();
     }
 
     @Override
@@ -97,7 +105,7 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
     @Override
     public String toString() {
         return Utils.toSqlString("LogicalFilter",
-                "predicates", predicates
+                "predicates", getPredicate()
         );
     }
 
@@ -110,13 +118,13 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
             return false;
         }
         LogicalFilter that = (LogicalFilter) o;
-        return predicates.equals(that.predicates)
+        return conjuncts.equals(that.conjuncts)
                 && singleTableExpressionExtracted == that.singleTableExpressionExtracted;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(predicates, singleTableExpressionExtracted);
+        return Objects.hash(conjuncts, singleTableExpressionExtracted);
     }
 
     @Override
@@ -124,26 +132,21 @@ public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
         return visitor.visitLogicalFilter(this, context);
     }
 
-    @Override
-    public List<? extends Expression> getExpressions() {
-        return ImmutableList.of(predicates);
-    }
-
     @Override
     public LogicalUnary<Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 1);
-        return new LogicalFilter<>(predicates, singleTableExpressionExtracted, children.get(0));
+        return new LogicalFilter<>(conjuncts, singleTableExpressionExtracted, children.get(0));
     }
 
     @Override
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new LogicalFilter<>(predicates, groupExpression, singleTableExpressionExtracted,
+        return new LogicalFilter<>(conjuncts, groupExpression, singleTableExpressionExtracted,
                 Optional.of(getLogicalProperties()), child());
     }
 
     @Override
     public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new LogicalFilter<>(predicates, Optional.empty(),
+        return new LogicalFilter<>(conjuncts, Optional.empty(),
                 singleTableExpressionExtracted,
                 logicalProperties, child());
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
index 2b63e09031..dc39d399bf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalHaving.java
@@ -29,10 +29,12 @@ import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Logical Having plan
@@ -40,27 +42,31 @@ import java.util.Optional;
  */
 public class LogicalHaving<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> implements Filter {
 
-    private final Expression predicates;
+    private final Set<Expression> conjuncts;
 
-    public LogicalHaving(Expression predicates, CHILD_TYPE child) {
-        this(predicates, Optional.empty(), Optional.empty(), child);
+    public LogicalHaving(Set<Expression> conjuncts, CHILD_TYPE child) {
+        this(conjuncts, Optional.empty(), Optional.empty(), child);
     }
 
-    public LogicalHaving(Expression predicates, Optional<GroupExpression> groupExpression,
+    public LogicalHaving(Set<Expression> conjuncts, Optional<GroupExpression> groupExpression,
             Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
         super(PlanType.LOGICAL_HAVING, groupExpression, logicalProperties, child);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
+        this.conjuncts = ImmutableSet.copyOf(Objects.requireNonNull(conjuncts, "conjuncts can not be null"));
     }
 
     @Override
-    public Expression getPredicates() {
-        return predicates;
+    public Set<Expression> getConjuncts() {
+        return conjuncts;
+    }
+
+    public List<Expression> getExpressions() {
+        return ImmutableList.of(getPredicate());
     }
 
     @Override
     public Plan withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 1);
-        return new LogicalHaving<>(predicates, children.get(0));
+        return new LogicalHaving<>(conjuncts, children.get(0));
     }
 
     @Override
@@ -68,19 +74,14 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
         return visitor.visitLogicalHaving((LogicalHaving<Plan>) this, context);
     }
 
-    @Override
-    public List<? extends Expression> getExpressions() {
-        return ImmutableList.of(predicates);
-    }
-
     @Override
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new LogicalHaving<>(predicates, groupExpression, Optional.of(getLogicalProperties()), child());
+        return new LogicalHaving<>(conjuncts, groupExpression, Optional.of(getLogicalProperties()), child());
     }
 
     @Override
     public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new LogicalHaving<>(predicates, Optional.empty(), logicalProperties, child());
+        return new LogicalHaving<>(conjuncts, Optional.empty(), logicalProperties, child());
     }
 
     @Override
@@ -90,7 +91,7 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(predicates);
+        return Objects.hashCode(conjuncts);
     }
 
     @Override
@@ -102,11 +103,11 @@ public class LogicalHaving<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_T
             return false;
         }
         LogicalHaving other = (LogicalHaving) object;
-        return predicates.equals(other.predicates);
+        return conjuncts.equals(other.conjuncts);
     }
 
     @Override
     public String toString() {
-        return Utils.toSqlString("LogicalHaving", "predicates", predicates);
+        return Utils.toSqlString("LogicalHaving", "predicates", getPredicate());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalFilter.java
index 6d6e01ef16..6a3c22a8de 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalFilter.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalFilter.java
@@ -30,44 +30,50 @@ import org.apache.doris.statistics.StatsDeriveResult;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Physical filter plan.
  */
 public class PhysicalFilter<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> implements Filter {
+    private final Set<Expression> conjuncts;
 
-    private final Expression predicates;
-
-    public PhysicalFilter(Expression predicates, LogicalProperties logicalProperties, CHILD_TYPE child) {
-        this(predicates, Optional.empty(), logicalProperties, child);
+    public PhysicalFilter(Set<Expression> conjuncts, LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(conjuncts, Optional.empty(), logicalProperties, child);
     }
 
-    public PhysicalFilter(Expression predicates, Optional<GroupExpression> groupExpression,
+    public PhysicalFilter(Set<Expression> conjuncts, Optional<GroupExpression> groupExpression,
             LogicalProperties logicalProperties, CHILD_TYPE child) {
         super(PlanType.PHYSICAL_FILTER, groupExpression, logicalProperties, child);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
+        this.conjuncts = ImmutableSet.copyOf(Objects.requireNonNull(conjuncts, "conjuncts can not be null"));
     }
 
-    public PhysicalFilter(Expression predicates, Optional<GroupExpression> groupExpression,
+    public PhysicalFilter(Set<Expression> conjuncts, Optional<GroupExpression> groupExpression,
             LogicalProperties logicalProperties, PhysicalProperties physicalProperties,
             StatsDeriveResult statsDeriveResult, CHILD_TYPE child) {
         super(PlanType.PHYSICAL_FILTER, groupExpression, logicalProperties, physicalProperties, statsDeriveResult,
                 child);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
+        this.conjuncts = ImmutableSet.copyOf(Objects.requireNonNull(conjuncts, "conjuncts can not be null"));
+    }
+
+    @Override
+    public Set<Expression> getConjuncts() {
+        return conjuncts;
     }
 
-    public Expression getPredicates() {
-        return predicates;
+    public List<Expression> getExpressions() {
+        return ImmutableList.of(getPredicate());
     }
 
     @Override
     public String toString() {
         return Utils.toSqlString("PhysicalFilter",
-                "predicates", predicates,
+                "predicates", getPredicate(),
                 "stats", statsDeriveResult
         );
     }
@@ -81,17 +87,12 @@ public class PhysicalFilter<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD
             return false;
         }
         PhysicalFilter that = (PhysicalFilter) o;
-        return predicates.equals(that.predicates);
+        return conjuncts.equals(that.conjuncts);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(predicates);
-    }
-
-    @Override
-    public List<? extends Expression> getExpressions() {
-        return ImmutableList.of(predicates);
+        return Objects.hash(conjuncts);
     }
 
     @Override
@@ -102,23 +103,23 @@ public class PhysicalFilter<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD
     @Override
     public PhysicalFilter<Plan> withChildren(List<Plan> children) {
         Preconditions.checkArgument(children.size() == 1);
-        return new PhysicalFilter<>(predicates, getLogicalProperties(), children.get(0));
+        return new PhysicalFilter<>(conjuncts, getLogicalProperties(), children.get(0));
     }
 
     @Override
     public PhysicalFilter<CHILD_TYPE> withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new PhysicalFilter<>(predicates, groupExpression, getLogicalProperties(), child());
+        return new PhysicalFilter<>(conjuncts, groupExpression, getLogicalProperties(), child());
     }
 
     @Override
     public PhysicalFilter<CHILD_TYPE> withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new PhysicalFilter<>(predicates, Optional.empty(), logicalProperties.get(), child());
+        return new PhysicalFilter<>(conjuncts, Optional.empty(), logicalProperties.get(), child());
     }
 
     @Override
     public PhysicalFilter<CHILD_TYPE> withPhysicalPropertiesAndStats(PhysicalProperties physicalProperties,
             StatsDeriveResult statsDeriveResult) {
-        return new PhysicalFilter<>(predicates, Optional.empty(), getLogicalProperties(), physicalProperties,
+        return new PhysicalFilter<>(conjuncts, Optional.empty(), getLogicalProperties(), physicalProperties,
                 statsDeriveResult, child());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
index f177ef4532..7a7ed01f30 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/ExpressionUtils.java
@@ -63,6 +63,12 @@ public class ExpressionUtils {
         return extract(And.class, expr);
     }
 
+    public static Set<Expression> extractConjunctionToSet(Expression expr) {
+        Set<Expression> exprSet = Sets.newHashSet();
+        extract(And.class, expr, exprSet);
+        return exprSet;
+    }
+
     public static List<Expression> extractDisjunction(Expression expr) {
         return extract(Or.class, expr);
     }
@@ -89,7 +95,7 @@ public class ExpressionUtils {
         return result;
     }
 
-    private static void extract(Class<? extends Expression> type, Expression expr, List<Expression> result) {
+    private static void extract(Class<? extends Expression> type, Expression expr, Collection<Expression> result) {
         if (type.isInstance(expr)) {
             CompoundPredicate predicate = (CompoundPredicate) expr;
             extract(type, predicate.left(), result);
@@ -99,6 +105,12 @@ public class ExpressionUtils {
         }
     }
 
+    public static Set<Expression> extractToSet(Expression predicate) {
+        Set<Expression> result = Sets.newHashSet();
+        extract(predicate.getClass(), predicate, result);
+        return result;
+    }
+
     public static Optional<Expression> optionalAnd(List<Expression> expressions) {
         if (expressions.isEmpty()) {
             return Optional.empty();
@@ -126,6 +138,10 @@ public class ExpressionUtils {
         return optionalAnd(Lists.newArrayList(expressions));
     }
 
+    public static Optional<Expression> optionalAnd(Collection<Expression> collection) {
+        return optionalAnd(ImmutableList.copyOf(collection));
+    }
+
     public static Expression and(List<Expression> expressions) {
         return combine(And.class, expressions);
     }
@@ -277,6 +293,13 @@ public class ExpressionUtils {
                 .collect(ImmutableList.toImmutableList());
     }
 
+    public static Set<Expression> replace(Set<Expression> exprs,
+            Map<? extends Expression, ? extends Expression> replaceMap) {
+        return exprs.stream()
+                .map(expr -> replace(expr, replaceMap))
+                .collect(ImmutableSet.toImmutableSet());
+    }
+
     public static <E extends Expression> List<E> rewriteDownShortCircuit(
             List<E> exprs, Function<Expression, Expression> rewriteFunction) {
         return exprs.stream()
@@ -340,8 +363,8 @@ public class ExpressionUtils {
     /**
      * extract the predicate that is covered by `slots`
      */
-    public static List<Expression> extractCoveredConjunction(List<Expression> predicates, Set<Slot> slots) {
-        List<Expression> coveredPredicates = Lists.newArrayList();
+    public static Set<Expression> extractCoveredConjunction(Set<Expression> predicates, Set<Slot> slots) {
+        Set<Expression> coveredPredicates = Sets.newHashSet();
         for (Expression predicate : predicates) {
             if (slots.containsAll(predicate.getInputSlots())) {
                 coveredPredicates.add(predicate);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
index 96dc8fb99c..36c9d52b28 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/PlanUtils.java
@@ -25,6 +25,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import java.util.List;
 import java.util.Optional;
+import java.util.Set;
 
 /**
  * Util for plan
@@ -42,12 +43,11 @@ public class PlanUtils {
         return project(projectExprs, plan).map(Plan.class::cast).orElse(plan);
     }
 
-    public static Optional<LogicalFilter<? extends Plan>> filter(List<Expression> predicates, Plan plan) {
-        return ExpressionUtils.optionalAnd(predicates)
-                .map(predicate -> new LogicalFilter<>(predicate, plan));
+    public static Optional<LogicalFilter<? extends Plan>> filter(Set<Expression> predicates, Plan plan) {
+        return ExpressionUtils.optionalAnd(predicates).map(opt -> new LogicalFilter<>(predicates, plan));
     }
 
-    public static Plan filterOrSelf(List<Expression> predicates, Plan plan) {
+    public static Plan filterOrSelf(Set<Expression> predicates, Plan plan) {
         return filter(predicates, plan).map(Plan.class::cast).orElse(plan);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
index 927c553964..15391e348e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/util/Utils.java
@@ -33,6 +33,7 @@ import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
@@ -193,7 +194,7 @@ public class Utils {
     }
 
     public static Map<Boolean, List<Expression>> splitCorrelatedConjuncts(
-            List<Expression> conjuncts, List<Expression> slots) {
+            Set<Expression> conjuncts, List<Expression> slots) {
         return conjuncts.stream().collect(Collectors.partitioningBy(
                 expr -> expr.anyMatch(slots::contains)));
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/datasets/ssb/SSBJoinReorderTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/datasets/ssb/SSBJoinReorderTest.java
index 2861e9b1f7..5f4fb55b23 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/datasets/ssb/SSBJoinReorderTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/datasets/ssb/SSBJoinReorderTest.java
@@ -96,7 +96,7 @@ public class SSBJoinReorderTest extends SSBTestBase implements PatternMatchSuppo
 
         for (String expectFilterPredicate : expectFilterPredicates) {
             planChecker.matches(
-                    logicalFilter().when(filter -> filter.getPredicates().toSql().equals(expectFilterPredicate))
+                    logicalFilter().when(filter -> filter.getPredicate().toSql().equals(expectFilterPredicate))
             );
         }
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslatorTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslatorTest.java
index f9553d9b14..a836b92b6a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslatorTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/glue/translator/PhysicalPlanTranslatorTest.java
@@ -38,6 +38,7 @@ import org.apache.doris.planner.OlapScanNode;
 import org.apache.doris.planner.PlanFragment;
 import org.apache.doris.planner.PlanNode;
 
+import com.google.common.collect.ImmutableSet;
 import mockit.Injectable;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -68,7 +69,7 @@ public class PhysicalPlanTranslatorTest {
         Literal t1FilterRight = new IntegerLiteral(1);
         Expression t1FilterExpr = new GreaterThan(col1, t1FilterRight);
         PhysicalFilter<PhysicalOlapScan> filter =
-                new PhysicalFilter<>(t1FilterExpr, placeHolder, scan);
+                new PhysicalFilter<>(ImmutableSet.of(t1FilterExpr), placeHolder, scan);
         List<NamedExpression> projList = new ArrayList<>();
         projList.add(col2);
         PhysicalProject<PhysicalFilter<PhysicalOlapScan>> project = new PhysicalProject<>(projList,
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java
index 7ea4497961..5853a7dc4a 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/memo/MemoTest.java
@@ -47,6 +47,7 @@ import org.apache.doris.nereids.util.PlanConstructor;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -223,8 +224,8 @@ class MemoTest implements PatternMatchSupported {
 
         LogicalProject<LogicalOlapScan> project = new LogicalProject<>(
                 ImmutableList.of(scan.computeOutput().get(0)), scan);
-        LogicalFilter<LogicalProject<LogicalOlapScan>> filter = new LogicalFilter<>(
-                new EqualTo(scan.computeOutput().get(0), new IntegerLiteral(1)), project);
+        LogicalFilter<LogicalProject<LogicalOlapScan>> filter = new LogicalFilter<>(ImmutableSet.of(
+                new EqualTo(scan.computeOutput().get(0), new IntegerLiteral(1))), project);
 
         PlanChecker.from(connectContext, filter)
                 .checkGroupNum(3)
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
index bb6b1d4948..efec2a2c67 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/pattern/GroupExpressionMatchingTest.java
@@ -33,6 +33,7 @@ import org.apache.doris.nereids.trees.plans.logical.RelationUtil;
 import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -209,8 +210,8 @@ public class GroupExpressionMatchingTest {
     @Test
     public void testSubTreeMatch() {
         Plan root =
-                new LogicalFilter(new EqualTo(new UnboundSlot(Lists.newArrayList("a", "id")),
-                        new UnboundSlot(Lists.newArrayList("b", "id"))),
+                new LogicalFilter(ImmutableSet.of(new EqualTo(new UnboundSlot(Lists.newArrayList("a", "id")),
+                        new UnboundSlot(Lists.newArrayList("b", "id")))),
                         new LogicalJoin(JoinType.INNER_JOIN,
                                 new LogicalJoin(JoinType.LEFT_OUTER_JOIN,
                                         new UnboundRelation(RelationUtil.newRelationId(), ImmutableList.of("a")),
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindFunctionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindFunctionTest.java
index 992493a9e7..ca710da5fd 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindFunctionTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/BindFunctionTest.java
@@ -51,7 +51,7 @@ public class BindFunctionTest extends TestWithFeService implements PatternMatchS
                 .rewrite()
                 .matches(
                         logicalFilter(logicalOlapScan())
-                                .when(f -> ((LessThan) f.getPredicates()).right() instanceof DateLiteral)
+                                .when(f -> ((LessThan) f.getPredicate()).right() instanceof DateLiteral)
                 );
     }
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java
index 5b8ca074b7..62c8132cc4 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckAnalysisTest.java
@@ -30,6 +30,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOneRowRelation;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import mockit.Mocked;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -42,7 +43,7 @@ public class CheckAnalysisTest {
 
     @Test
     public void testCheckExpressionInputTypes() {
-        Plan plan = new LogicalFilter<>(new And(new IntegerLiteral(1), BooleanLiteral.TRUE), groupPlan);
+        Plan plan = new LogicalFilter<>(ImmutableSet.of(new And(new IntegerLiteral(1), BooleanLiteral.TRUE)), groupPlan);
         CheckAnalysis checkAnalysis = new CheckAnalysis();
         Assertions.assertThrows(RuntimeException.class, () ->
                 checkAnalysis.buildRules().forEach(rule -> rule.transform(plan, cascadesContext)));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
index 9e44686208..3b03d1b996 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/CheckRowPolicyTest.java
@@ -137,8 +137,8 @@ public class CheckRowPolicyTest extends TestWithFeService {
         Assertions.assertTrue(plan instanceof LogicalFilter);
         LogicalFilter filter = (LogicalFilter) plan;
         Assertions.assertEquals(filter.child(), relation);
-        Assertions.assertTrue(filter.getPredicates() instanceof EqualTo);
-        Assertions.assertTrue(filter.getPredicates().toString().contains("k1 = 1"));
+        Assertions.assertTrue(ImmutableList.copyOf(filter.getConjuncts()).get(0) instanceof EqualTo);
+        Assertions.assertTrue(filter.getConjuncts().toString().contains("k1 = 1"));
 
         dropPolicy("DROP ROW POLICY "
                 + policyName
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java
index 642b1d90b4..e325504c23 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/FillUpMissingSlotsTest.java
@@ -25,7 +25,6 @@ import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewrite;
 import org.apache.doris.nereids.rules.expression.rewrite.rules.TypeCoercion;
 import org.apache.doris.nereids.trees.expressions.Add;
 import org.apache.doris.nereids.trees.expressions.Alias;
-import org.apache.doris.nereids.trees.expressions.And;
 import org.apache.doris.nereids.trees.expressions.Cast;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
@@ -43,6 +42,7 @@ import org.apache.doris.nereids.util.PatternMatchSupported;
 import org.apache.doris.nereids.util.PlanChecker;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Test;
 
@@ -92,7 +92,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(a1, new TinyIntLiteral((byte) 0))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(a1, new TinyIntLiteral((byte) 0)))))));
 
         sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING a1 > 0";
         a1 = new SlotReference(
@@ -108,7 +108,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(value)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))))));
 
         sql = "SELECT a1 as value FROM t1 GROUP BY a1 HAVING value > 0";
         PlanChecker.from(connectContext).analyze(sql)
@@ -119,7 +119,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(value)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(value.toSlot(), new TinyIntLiteral((byte) 0)))))));
 
         sql = "SELECT SUM(a2) FROM t1 GROUP BY a1 HAVING a1 > 0";
         a1 = new SlotReference(
@@ -139,7 +139,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(sumA2, a1)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(a1, new TinyIntLiteral((byte) 0))))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(a1, new TinyIntLiteral((byte) 0)))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(sumA2.toSlot()))));
     }
 
@@ -162,7 +162,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), Literal.of(0L))))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(sumA2.toSlot(), Literal.of(0L)))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot()))));
 
         sql = "SELECT a1, SUM(a2) FROM t1 GROUP BY a1 HAVING SUM(a2) > 0";
@@ -174,7 +174,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(sumA2.toSlot(), Literal.of(0L))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(sumA2.toSlot(), Literal.of(0L)))))));
 
         sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING SUM(a2) > 0";
         a1 = new SlotReference(
@@ -193,7 +193,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, value)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), Literal.of(0L))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(value.toSlot(), Literal.of(0L)))))));
 
         sql = "SELECT a1, SUM(a2) as value FROM t1 GROUP BY a1 HAVING value > 0";
         PlanChecker.from(connectContext).analyze(sql)
@@ -203,7 +203,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, value)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(value.toSlot(), Literal.of(0L))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(value.toSlot(), Literal.of(0L)))))));
 
         sql = "SELECT a1, SUM(a2) FROM t1 GROUP BY a1 HAVING MIN(pk) > 0";
         a1 = new SlotReference(
@@ -227,7 +227,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2, minPK)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(minPK.toSlot(), Literal.of((byte) 0))))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(minPK.toSlot(), Literal.of((byte) 0)))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA2.toSlot()))));
 
         sql = "SELECT a1, SUM(a1 + a2) FROM t1 GROUP BY a1 HAVING SUM(a1 + a2) > 0";
@@ -239,7 +239,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA1A2)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(sumA1A2.toSlot(), Literal.of(0L))))));
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(sumA1A2.toSlot(), Literal.of(0L)))))));
 
         sql = "SELECT a1, SUM(a1 + a2) FROM t1 GROUP BY a1 HAVING SUM(a1 + a2 + 3) > 0";
         Alias sumA1A23 = new Alias(new ExprId(4), new Sum(new Add(new Add(a1, a2), new SmallIntLiteral((short) 3))),
@@ -251,7 +251,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA1A2, sumA1A23)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(sumA1A23.toSlot(), Literal.of(0L))))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(sumA1A23.toSlot(), Literal.of(0L)))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA1A2.toSlot()))));
 
         sql = "SELECT a1 FROM t1 GROUP BY a1 HAVING COUNT(*) > 0";
@@ -263,7 +263,7 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                             logicalAggregate(
                                 logicalOlapScan()
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, countStar)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(countStar.toSlot(), Literal.of(0L))))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(countStar.toSlot(), Literal.of(0L)))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot()))));
     }
 
@@ -296,8 +296,8 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                                     )
                                 )
                             ).when(FieldChecker.check("outputExpressions", Lists.newArrayList(a1, sumA2, sumB1)))
-                        ).when(FieldChecker.check("predicates", new GreaterThan(new Cast(a1, BigIntType.INSTANCE),
-                                sumB1.toSlot())))
+                        ).when(FieldChecker.check("conjuncts", ImmutableSet.of(new GreaterThan(new Cast(a1, BigIntType.INSTANCE),
+                                sumB1.toSlot()))))
                     ).when(FieldChecker.check("projects", Lists.newArrayList(a1.toSlot(), sumA2.toSlot()))));
     }
 
@@ -362,19 +362,15 @@ public class FillUpMissingSlotsTest extends AnalyzeCheckTestBase implements Patt
                                 )
                             ).when(FieldChecker.check("outputExpressions",
                                     Lists.newArrayList(pk1, pk11, pk2, sumA1, countA11, sumA1A2, v1, pk)))
-                        ).when(FieldChecker.check("predicates",
-                                new And(
-                                        new And(
-                                                new And(
-                                                        new And(
-                                                                new GreaterThan(pk.toSlot(), Literal.of((byte) 0)),
-                                                                new GreaterThan(countA11.toSlot(), Literal.of(0L))),
-                                                        new GreaterThan(new Add(sumA1A2.toSlot(), Literal.of(1L)), Literal.of(0L))),
-                                                new GreaterThan(new Add(v1.toSlot(), Literal.of(1L)), Literal.of(0L))
-                                        ),
+                        ).when(FieldChecker.check("conjuncts",
+                                ImmutableSet.of(
+                                        new GreaterThan(pk.toSlot(), Literal.of((byte) 0)),
+                                        new GreaterThan(countA11.toSlot(), Literal.of(0L)),
+                                        new GreaterThan(new Add(sumA1A2.toSlot(), Literal.of(1L)), Literal.of(0L)),
+                                        new GreaterThan(new Add(v1.toSlot(), Literal.of(1L)), Literal.of(0L)),
                                         new GreaterThan(v1.toSlot(), Literal.of(0L))
-                                )
-                        ))
+                                ))
+                        )
                     ).when(FieldChecker.check(
                         "projects", Lists.newArrayList(
                             pk1, pk11, pk2, sumA1, countA11, sumA1A2, v1).stream()
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ReplaceExpressionByChildOutputTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ReplaceExpressionByChildOutputTest.java
index 5206dcb402..67d043b88c 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ReplaceExpressionByChildOutputTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/analysis/ReplaceExpressionByChildOutputTest.java
@@ -35,6 +35,7 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
@@ -83,7 +84,7 @@ public class ReplaceExpressionByChildOutputTest implements PatternMatchSupported
         LogicalOlapScan logicalOlapScan = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
         LogicalAggregate<Plan> logicalAggregate = new LogicalAggregate<>(
                 ImmutableList.of(alias), ImmutableList.of(alias), logicalOlapScan);
-        LogicalHaving<Plan> logicalHaving = new LogicalHaving<>(BooleanLiteral.TRUE, logicalAggregate);
+        LogicalHaving<Plan> logicalHaving = new LogicalHaving<>(ImmutableSet.of(BooleanLiteral.TRUE), logicalAggregate);
         List<OrderKey> orderKeys = ImmutableList.of(new OrderKey(slotReference, true, true));
         LogicalSort<Plan> logicalSort = new LogicalSort<>(orderKeys, logicalHaving);
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
index d23b0a191d..eb1c619a9b 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ExtractSingleTableExpressionFromDisjunctionTest.java
@@ -29,19 +29,19 @@ import org.apache.doris.nereids.trees.plans.Plan;
 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.LogicalOlapScan;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PatternMatchSupported;
 import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
-import java.util.List;
+import java.util.Set;
 
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 public class ExtractSingleTableExpressionFromDisjunctionTest implements PatternMatchSupported {
@@ -83,18 +83,17 @@ public class ExtractSingleTableExpressionFromDisjunctionTest implements PatternM
                 )
         );
         Plan join = new LogicalJoin<>(JoinType.CROSS_JOIN, student, course);
-        LogicalFilter root = new LogicalFilter(expr, join);
+        LogicalFilter root = new LogicalFilter(ImmutableSet.of(expr), join);
         PlanChecker.from(MemoTestUtils.createConnectContext(), root)
                 .applyTopDown(new ExtractSingleTableExpressionFromDisjunction())
                 .matchesFromRoot(
                         logicalFilter()
-                                .when(filter -> verifySingleTableExpression1(filter.getPredicates()))
+                                .when(filter -> verifySingleTableExpression1(filter.getConjuncts()))
                 );
         Assertions.assertTrue(studentGender != null);
     }
 
-    private boolean verifySingleTableExpression1(Expression expr) {
-        List<Expression> conjuncts = ExpressionUtils.extractConjunction(expr);
+    private boolean verifySingleTableExpression1(Set<Expression> conjuncts) {
         Expression or1 = new Or(
                 new EqualTo(courseCid, new IntegerLiteral(1)),
                 new EqualTo(courseName, new StringLiteral("abc"))
@@ -126,18 +125,17 @@ public class ExtractSingleTableExpressionFromDisjunctionTest implements PatternM
                 )
         );
         Plan join = new LogicalJoin<>(JoinType.CROSS_JOIN, student, course);
-        LogicalFilter root = new LogicalFilter<>(expr, join);
+        LogicalFilter root = new LogicalFilter<>(ImmutableSet.of(expr), join);
         PlanChecker.from(MemoTestUtils.createConnectContext(), root)
                 .applyTopDown(new ExtractSingleTableExpressionFromDisjunction())
                 .matchesFromRoot(
                         logicalFilter()
-                                .when(filter -> verifySingleTableExpression2(filter.getPredicates()))
+                                .when(filter -> verifySingleTableExpression2(filter.getConjuncts()))
                 );
         Assertions.assertNotNull(studentGender);
     }
 
-    private boolean verifySingleTableExpression2(Expression expr) {
-        List<Expression> conjuncts = ExpressionUtils.extractConjunction(expr);
+    private boolean verifySingleTableExpression2(Set<Expression> conjuncts) {
         Expression or1 = new Or(
                 new EqualTo(courseCid, new IntegerLiteral(1)),
                 new And(
@@ -163,18 +161,17 @@ public class ExtractSingleTableExpressionFromDisjunctionTest implements PatternM
                 new EqualTo(studentGender, new IntegerLiteral(1))
         );
         Plan join = new LogicalJoin<>(JoinType.CROSS_JOIN, student, course);
-        LogicalFilter root = new LogicalFilter<>(expr, join);
+        LogicalFilter root = new LogicalFilter<>(ImmutableSet.of(expr), join);
         PlanChecker.from(MemoTestUtils.createConnectContext(), root)
                 .applyTopDown(new ExtractSingleTableExpressionFromDisjunction())
                 .matchesFromRoot(
                         logicalFilter()
-                                .when(filter -> verifySingleTableExpression3(filter.getPredicates()))
+                                .when(filter -> verifySingleTableExpression3(filter.getConjuncts()))
                 );
         Assertions.assertNotNull(studentGender);
     }
 
-    private boolean verifySingleTableExpression3(Expression expr) {
-        List<Expression> conjuncts = ExpressionUtils.extractConjunction(expr);
+    private boolean verifySingleTableExpression3(Set<Expression> conjuncts) {
         Expression or = new Or(
                 new EqualTo(studentAge, new IntegerLiteral(10)),
                 new EqualTo(studentGender, new IntegerLiteral(1))
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicatesTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicatesTest.java
index 79d7af0e49..84b3dcbf3f 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicatesTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/InferPredicatesTest.java
@@ -85,10 +85,10 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                            ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -119,7 +119,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id IN (1, 2, 3)")),
+                        ).when(filter -> filter.getPredicate().toSql().contains("id IN (1, 2, 3)")),
                         logicalOlapScan()
                     )
                 );
@@ -136,7 +136,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id IN (1, 2, 3)")),
+                        ).when(filter -> filter.getPredicate().toSql().contains("id IN (1, 2, 3)")),
                         logicalOlapScan()
                     )
                 );
@@ -154,14 +154,14 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalJoin(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                                ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         ),
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id > 1"))
+                        ).when(filter -> filter.getPredicate().toSql().contains("id > 1"))
                     )
                 );
     }
@@ -178,14 +178,14 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalJoin(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                                ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         ),
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id > 1"))
+                        ).when(filter -> filter.getPredicate().toSql().contains("id > 1"))
                     )
                 );
     }
@@ -201,10 +201,10 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                            ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -221,7 +221,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalOlapScan(),
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                        ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -238,10 +238,10 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("id > 1")),
+                            ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -258,11 +258,11 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalProject(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filer -> filer.getPredicates().toSql().contains("id > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("id > 1"))
                         ),
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                        ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -281,7 +281,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                             ),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                     )
                 );
     }
@@ -297,12 +297,12 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                             logicalOlapScan()
-                        ).when(filer -> filer.getPredicates().toSql().contains("id > 1")),
+                        ).when(filer -> filer.getPredicate().toSql().contains("id > 1")),
                         logicalAggregate(
                             logicalProject(
                                     logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                            ).when(filer -> filer.getPredicate().toSql().contains("sid > 1"))
                         ))
                     )
                 );
@@ -320,11 +320,11 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                             logicalProject(
                                     logicalFilter(
                                             logicalOlapScan()
-                                    ).when(filer -> filer.getPredicates().toSql().contains("id = 1"))
+                                    ).when(filter -> filter.getPredicate().toSql().contains("id = 1"))
                             ),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filer -> filer.getPredicates().toSql().contains("sid = 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("sid = 1"))
                     )
                 );
     }
@@ -340,11 +340,11 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                            ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                             logicalProject(
                                     logicalFilter(
                                             logicalOlapScan()
-                                    ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                                    ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                             )
                     )
                 );
@@ -361,11 +361,11 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                        ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                         logicalProject(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filer -> filer.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         )
                     )
                 );
@@ -384,7 +384,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalProject(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         )
                     )
                 );
@@ -403,7 +403,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                         logicalProject(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         )
                     )
                 );
@@ -420,11 +420,11 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                        ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                         logicalProject(
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         )
                     )
                 );
@@ -463,19 +463,19 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                     logicalJoin(
                         logicalFilter(
                                 logicalOlapScan()
-                        ).when(filter -> filter.getPredicates().toSql().contains("k1 = 3")),
+                        ).when(filter -> filter.getPredicate().toSql().contains("k1 = 3")),
                         logicalProject(
                             logicalJoin(
                                 logicalJoin(
                                    logicalProject(
                                            logicalFilter(
                                                    logicalOlapScan()
-                                           ).when(filter -> filter.getPredicates().toSql().contains("k3 = 3"))
+                                           ).when(filter -> filter.getPredicate().toSql().contains("k3 = 3"))
                                    ),
                                    logicalProject(
                                            logicalFilter(
                                                    logicalOlapScan()
-                                           ).when(filter -> filter.getPredicates().toSql().contains("k1 = 3"))
+                                           ).when(filter -> filter.getPredicate().toSql().contains("k1 = 3"))
                                    )
 
                                 ),
@@ -503,7 +503,7 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                                 logicalOlapScan(),
                                 logicalFilter(
                                         logicalOlapScan()
-                                ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                         ),
                         logicalOlapScan()
                     )
@@ -522,14 +522,14 @@ public class InferPredicatesTest extends TestWithFeService implements PatternMat
                             logicalJoin(
                                     logicalFilter(
                                             logicalOlapScan()
-                                    ).when(filter -> filter.getPredicates().toSql().contains("id > 1")),
+                                    ).when(filter -> filter.getPredicate().toSql().contains("id > 1")),
                                     logicalFilter(
                                             logicalOlapScan()
-                                    ).when(filter -> filter.getPredicates().toSql().contains("sid > 1"))
+                                    ).when(filter -> filter.getPredicate().toSql().contains("sid > 1"))
                             ),
                             logicalFilter(
                                     logicalOlapScan()
-                            ).when(filter -> filter.getPredicates().toSql().contains("id > 1"))
+                            ).when(filter -> filter.getPredicate().toSql().contains("id > 1"))
                     )
                 );
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFiltersTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFiltersTest.java
index fad00b825e..31bf853bdf 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFiltersTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/MergeFiltersTest.java
@@ -25,14 +25,15 @@ import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.RelationUtil;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.MemoTestUtils;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * MergeConsecutiveFilter ut
@@ -42,11 +43,11 @@ public class MergeFiltersTest {
     public void testMergeConsecutiveFilters() {
         UnboundRelation relation = new UnboundRelation(RelationUtil.newRelationId(), Lists.newArrayList("db", "table"));
         Expression expression1 = new IntegerLiteral(1);
-        LogicalFilter filter1 = new LogicalFilter<>(expression1, relation);
+        LogicalFilter filter1 = new LogicalFilter<>(ImmutableSet.of(expression1), relation);
         Expression expression2 = new IntegerLiteral(2);
-        LogicalFilter filter2 = new LogicalFilter<>(expression2, filter1);
+        LogicalFilter filter2 = new LogicalFilter<>(ImmutableSet.of(expression2), filter1);
         Expression expression3 = new IntegerLiteral(3);
-        LogicalFilter filter3 = new LogicalFilter<>(expression3, filter2);
+        LogicalFilter filter3 = new LogicalFilter<>(ImmutableSet.of(expression3), filter2);
 
         CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(filter3);
         List<Rule> rules = Lists.newArrayList(new MergeFilters().build());
@@ -55,9 +56,8 @@ public class MergeFiltersTest {
         Plan resultPlan = cascadesContext.getMemo().copyOut();
         System.out.println(resultPlan.treeString());
         Assertions.assertTrue(resultPlan instanceof LogicalFilter);
-        Expression allPredicates = ExpressionUtils.and(expression3,
-                ExpressionUtils.and(expression2, expression1));
-        Assertions.assertEquals(((LogicalFilter<?>) resultPlan).getPredicates(), allPredicates);
+        Set<Expression> allPredicates = ImmutableSet.of(expression1, expression2, expression3);
+        Assertions.assertEquals(ImmutableSet.copyOf(((LogicalFilter<?>) resultPlan).getConjuncts()), allPredicates);
         Assertions.assertTrue(resultPlan.child(0) instanceof UnboundRelation);
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartitionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartitionTest.java
index ca52fa2360..4c7bc3d18b 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartitionTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanPartitionTest.java
@@ -28,8 +28,6 @@ import org.apache.doris.catalog.Type;
 import org.apache.doris.common.jmockit.Deencapsulation;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.trees.expressions.And;
-import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
@@ -46,6 +44,7 @@ import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.BoundType;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Range;
 import mockit.Expectations;
@@ -91,7 +90,7 @@ class PruneOlapScanPartitionTest {
         LogicalOlapScan scan = new LogicalOlapScan(PlanConstructor.getNextRelationId(), olapTable);
         SlotReference slotRef = new SlotReference("col1", IntegerType.INSTANCE);
         Expression expression = new LessThan(slotRef, new IntegerLiteral(4));
-        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(expression, scan);
+        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(ImmutableSet.of(expression), scan);
 
         CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(filter);
         List<Rule> rules = Lists.newArrayList(new PruneOlapScanPartition().build());
@@ -103,7 +102,7 @@ class PruneOlapScanPartitionTest {
         Expression lessThan0 = new LessThan(slotRef, new IntegerLiteral(0));
         Expression greaterThan6 = new GreaterThan(slotRef, new IntegerLiteral(6));
         Or lessThan0OrGreaterThan6 = new Or(lessThan0, greaterThan6);
-        filter = new LogicalFilter<>(lessThan0OrGreaterThan6, scan);
+        filter = new LogicalFilter<>(ImmutableSet.of(lessThan0OrGreaterThan6), scan);
         scan = new LogicalOlapScan(PlanConstructor.getNextRelationId(), olapTable);
         cascadesContext = MemoTestUtils.createCascadesContext(filter);
         rules = Lists.newArrayList(new PruneOlapScanPartition().build());
@@ -117,9 +116,8 @@ class PruneOlapScanPartitionTest {
                         slotRef, new IntegerLiteral(0));
         Expression lessThanEqual5 =
                 new LessThanEqual(slotRef, new IntegerLiteral(5));
-        And greaterThanEqual0AndLessThanEqual5 = new And(greaterThanEqual0, lessThanEqual5);
         scan = new LogicalOlapScan(PlanConstructor.getNextRelationId(), olapTable);
-        filter = new LogicalFilter<>(greaterThanEqual0AndLessThanEqual5, scan);
+        filter = new LogicalFilter<>(ImmutableSet.of(greaterThanEqual0, lessThanEqual5), scan);
         cascadesContext = MemoTestUtils.createCascadesContext(filter);
         rules = Lists.newArrayList(new PruneOlapScanPartition().build());
         cascadesContext.topDownRewrite(rules);
@@ -156,8 +154,7 @@ class PruneOlapScanPartitionTest {
         LogicalOlapScan scan = new LogicalOlapScan(PlanConstructor.getNextRelationId(), olapTable);
         Expression left = new LessThan(new SlotReference("col1", IntegerType.INSTANCE), new IntegerLiteral(4));
         Expression right = new GreaterThan(new SlotReference("col2", IntegerType.INSTANCE), new IntegerLiteral(11));
-        CompoundPredicate and = new And(left, right);
-        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(and, scan);
+        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(ImmutableSet.of(left, right), scan);
         CascadesContext cascadesContext = MemoTestUtils.createCascadesContext(filter);
         List<Rule> rules = Lists.newArrayList(new PruneOlapScanPartition().build());
         cascadesContext.topDownRewrite(rules);
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTabletTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTabletTest.java
index 15cec48f21..f9278be2c2 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTabletTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PruneOlapScanTabletTest.java
@@ -31,7 +31,6 @@ import org.apache.doris.catalog.PrimitiveType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
-import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.InPredicate;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
@@ -42,11 +41,11 @@ import org.apache.doris.nereids.trees.plans.RelationId;
 import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.types.DataType;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.planner.PartitionColumnFilter;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import mockit.Expectations;
 import mockit.Mocked;
@@ -148,8 +147,8 @@ public class PruneOlapScanTabletTest {
             }
         };
 
-        Expression expr = ExpressionUtils.and(greaterThanEqual, lessThanEqual, inPredicate1, inPredicate2, inPredicate3, equalTo);
-        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(expr,
+        LogicalFilter<LogicalOlapScan> filter = new LogicalFilter<>(
+                ImmutableSet.of(greaterThanEqual, lessThanEqual, inPredicate1, inPredicate2, inPredicate3, equalTo),
                 new LogicalOlapScan(RelationId.createGenerator().getNextId(), olapTable));
 
         Assertions.assertEquals(0, filter.child().getSelectedTabletIds().size());
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregationTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregationTest.java
index 33daf8c60a..bc6a08d994 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregationTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughAggregationTest.java
@@ -35,6 +35,7 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import org.junit.jupiter.api.Test;
 
 public class PushdownFilterThroughAggregationTest implements PatternMatchSupported {
@@ -78,7 +79,7 @@ public class PushdownFilterThroughAggregationTest implements PatternMatchSupport
                                 logicalAggregate(
                                         logicalFilter(
                                                 logicalOlapScan()
-                                        ).when(filter -> filter.getPredicates().equals(filterPredicate))
+                                        ).when(filter -> filter.getConjuncts().equals(ImmutableSet.of(filterPredicate)))
                                 )
                         )
                 );
@@ -134,11 +135,10 @@ public class PushdownFilterThroughAggregationTest implements PatternMatchSupport
                                         logicalAggregate(
                                                 logicalFilter(
                                                         logicalOlapScan()
-                                                ).when(filter -> filter.getPredicates().child(0) instanceof GreaterThan)
-                                                        .when(filter -> filter.getPredicates()
-                                                                .child(1) instanceof LessThanEqual)
+                                                ).when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0) instanceof LessThanEqual
+                                                        && ImmutableList.copyOf(filter.getConjuncts()).get(1) instanceof GreaterThan)
                                         )
-                                ).when(filter -> filter.getPredicates() instanceof EqualTo)
+                                ).when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0) instanceof EqualTo)
                         )
                 );
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoinTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoinTest.java
index ceb9b7a7d3..789344f070 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoinTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownFilterThroughJoinTest.java
@@ -26,7 +26,6 @@ import org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
-import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.LogicalPlanBuilder;
 import org.apache.doris.nereids.util.MemoTestUtils;
 import org.apache.doris.nereids.util.PatternMatchSupported;
@@ -34,10 +33,13 @@ import org.apache.doris.nereids.util.PlanChecker;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInstance;
 
+import java.util.Set;
+
 /**
  * PushdownFilterThroughJoinTest UT.
  */
@@ -72,7 +74,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
     private void testLeft(JoinType joinType) {
         Expression whereCondition1 = new GreaterThan(rStudent.getOutput().get(1), Literal.of(18));
         Expression whereCondition2 = new GreaterThan(rStudent.getOutput().get(1), Literal.of(50));
-        Expression whereCondition = ExpressionUtils.and(whereCondition1, whereCondition2);
+        Set<Expression> whereCondition = ImmutableSet.of(whereCondition1, whereCondition2);
 
         LogicalPlan plan = new LogicalPlanBuilder(rStudent)
                 .hashJoinEmptyOn(rScore, joinType)
@@ -84,7 +86,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                 .matchesFromRoot(
                         logicalJoin(
                                 logicalFilter(logicalOlapScan())
-                                        .when(filter -> filter.getPredicates().equals(whereCondition)),
+                                        .when(filter -> filter.getConjuncts().equals(whereCondition)),
                                 logicalOlapScan()
                         )
                 );
@@ -93,7 +95,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
     private void testRight(JoinType joinType) {
         Expression whereCondition1 = new GreaterThan(rStudent.getOutput().get(1), Literal.of(18));
         Expression whereCondition2 = new GreaterThan(rStudent.getOutput().get(1), Literal.of(50));
-        Expression whereCondition = ExpressionUtils.and(whereCondition1, whereCondition2);
+        Set<Expression> whereCondition = ImmutableSet.of(whereCondition1, whereCondition2);
 
         LogicalPlan plan = new LogicalPlanBuilder(rScore)
                 .hashJoinEmptyOn(rStudent, joinType)
@@ -106,7 +108,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                         logicalJoin(
                                 logicalOlapScan(),
                                 logicalFilter(logicalOlapScan())
-                                        .when(filter -> filter.getPredicates().equals(whereCondition))
+                                        .when(filter -> filter.getConjuncts().equals(whereCondition))
                         )
                 );
     }
@@ -123,7 +125,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                 new Subtract(rScore.getOutput().get(0), Literal.of(2)));
         Expression leftSide = new GreaterThan(rStudent.getOutput().get(1), Literal.of(18));
         Expression rightSide = new GreaterThan(rScore.getOutput().get(2), Literal.of(60));
-        Expression whereCondition = ExpressionUtils.and(bothSideEqualTo, leftSide, rightSide);
+        Set<Expression> whereCondition = ImmutableSet.of(bothSideEqualTo, leftSide, rightSide);
 
         LogicalPlan plan = new LogicalPlanBuilder(rStudent)
                 .hashJoinEmptyOn(rScore, joinType)
@@ -137,9 +139,9 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                     .matchesFromRoot(
                             logicalJoin(
                                     logicalFilter(logicalOlapScan())
-                                            .when(filter -> filter.getPredicates().equals(leftSide)),
+                                            .when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(leftSide)),
                                     logicalFilter(logicalOlapScan())
-                                            .when(filter -> filter.getPredicates().equals(rightSide))
+                                            .when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(rightSide))
                             ).when(join -> join.getOtherJoinConjuncts().get(0).equals(bothSideEqualTo))
                     );
         }
@@ -151,11 +153,11 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                             logicalFilter(
                                     logicalJoin(
                                             logicalFilter(logicalOlapScan())
-                                                    .when(filter -> filter.getPredicates().equals(leftSide)),
+                                                    .when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(leftSide)),
                                             logicalFilter(logicalOlapScan())
-                                                    .when(filter -> filter.getPredicates().equals(rightSide))
+                                                    .when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(rightSide))
                                     )
-                            ).when(filter -> filter.getPredicates().equals(bothSideEqualTo))
+                            ).when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(bothSideEqualTo))
                     );
         }
     }
@@ -173,7 +175,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
     private void bothSideToLeft(JoinType joinType) {
         Expression pushSide = new GreaterThan(rStudent.getOutput().get(1), Literal.of(18));
         Expression reserveSide = new GreaterThan(rScore.getOutput().get(2), Literal.of(60));
-        Expression whereCondition = ExpressionUtils.and(pushSide, reserveSide);
+        Set<Expression> whereCondition = ImmutableSet.of(pushSide, reserveSide);
 
         LogicalPlan plan = new LogicalPlanBuilder(rStudent)
                 .hashJoinEmptyOn(rScore, joinType)
@@ -186,7 +188,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                         logicalFilter(
                                 logicalJoin(
                                         logicalFilter(logicalOlapScan())
-                                                .when(filter -> filter.getPredicates().equals(pushSide)),
+                                                .when(filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(pushSide)),
                                         logicalOlapScan()
                                 )
                         )
@@ -196,7 +198,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
     private void bothSideToRight(JoinType joinType) {
         Expression pushSide = new GreaterThan(rStudent.getOutput().get(1), Literal.of(18));
         Expression reserveSide = new GreaterThan(rScore.getOutput().get(2), Literal.of(60));
-        Expression whereCondition = ExpressionUtils.and(pushSide, reserveSide);
+        Set<Expression> whereCondition = ImmutableSet.of(pushSide, reserveSide);
 
         LogicalPlan plan = new LogicalPlanBuilder(rScore)
                 .hashJoinEmptyOn(rStudent, joinType)
@@ -210,7 +212,7 @@ public class PushdownFilterThroughJoinTest implements PatternMatchSupported {
                                 logicalJoin(
                                         logicalOlapScan(),
                                         logicalFilter(logicalOlapScan()).when(
-                                                filter -> filter.getPredicates().equals(pushSide))
+                                                filter -> ImmutableList.copyOf(filter.getConjuncts()).get(0).equals(pushSide))
                                 )
                         )
                 );
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
index 9b7d20b84a..3fdea34cd4 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushdownJoinOtherConditionTest.java
@@ -35,6 +35,7 @@ import org.apache.doris.nereids.util.PlanRewriter;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
@@ -107,7 +108,7 @@ public class PushdownJoinOtherConditionTest {
         Assertions.assertTrue(shouldScan instanceof LogicalOlapScan);
         LogicalFilter<Plan> actualFilter = (LogicalFilter<Plan>) shouldFilter;
 
-        Assertions.assertEquals(ExpressionUtils.and(condition), actualFilter.getPredicates());
+        Assertions.assertEquals(condition, ImmutableList.copyOf(actualFilter.getConjuncts()));
     }
 
     @Test
@@ -142,8 +143,8 @@ public class PushdownJoinOtherConditionTest {
         Assertions.assertTrue(rightFilter instanceof LogicalFilter);
         LogicalFilter<Plan> actualLeft = (LogicalFilter<Plan>) leftFilter;
         LogicalFilter<Plan> actualRight = (LogicalFilter<Plan>) rightFilter;
-        Assertions.assertEquals(leftSide, actualLeft.getPredicates());
-        Assertions.assertEquals(rightSide, actualRight.getPredicates());
+        Assertions.assertEquals(ImmutableSet.of(leftSide), actualLeft.getConjuncts());
+        Assertions.assertEquals(ImmutableSet.of(rightSide), actualRight.getConjuncts());
     }
 
     @Test
@@ -190,7 +191,7 @@ public class PushdownJoinOtherConditionTest {
         Assertions.assertTrue(shouldFilter instanceof LogicalFilter);
         Assertions.assertTrue(shouldScan instanceof LogicalOlapScan);
         LogicalFilter<Plan> actualFilter = (LogicalFilter<Plan>) shouldFilter;
-        Assertions.assertEquals(pushSide, actualFilter.getPredicates());
+        Assertions.assertEquals(ImmutableSet.of(pushSide), actualFilter.getConjuncts());
     }
 
     private Memo rewrite(Plan plan) {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java
index c47763b2ce..fd9dfe8fcd 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/stats/StatsCalculatorTest.java
@@ -22,7 +22,6 @@ import org.apache.doris.common.Id;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.And;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Or;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
@@ -41,6 +40,7 @@ import org.apache.doris.statistics.ColumnStatisticBuilder;
 import org.apache.doris.statistics.StatsDeriveResult;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import mockit.Mocked;
 import org.junit.jupiter.api.Assertions;
@@ -121,8 +121,8 @@ public class StatsCalculatorTest {
         EqualTo eq1 = new EqualTo(slot1, new IntegerLiteral(1));
         EqualTo eq2 = new EqualTo(slot2, new IntegerLiteral(2));
 
-        And and = new And(eq1, eq2);
-        Or or = new Or(eq1, eq2);
+        ImmutableSet and = ImmutableSet.of(eq1, eq2);
+        ImmutableSet or = ImmutableSet.of(new Or(eq1, eq2));
 
         Group childGroup = new Group();
         childGroup.setLogicalProperties(new LogicalProperties(Collections::emptyList));
@@ -174,8 +174,8 @@ public class StatsCalculatorTest {
         EqualTo eq1 = new EqualTo(slot1, new IntegerLiteral(200));
         EqualTo eq2 = new EqualTo(slot2, new IntegerLiteral(300));
 
-        And and = new And(eq1, eq2);
-        Or or = new Or(eq1, eq2);
+        ImmutableSet and = ImmutableSet.of(eq1, eq2);
+        ImmutableSet or = ImmutableSet.of(new Or(eq1, eq2));
 
         Group childGroup = new Group();
         childGroup.setLogicalProperties(new LogicalProperties(Collections::emptyList));
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
index f084777bf7..f7cf818995 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanEqualsTest.java
@@ -48,6 +48,7 @@ import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import mockit.Mocked;
 import org.junit.jupiter.api.Assertions;
@@ -92,12 +93,12 @@ public class PlanEqualsTest {
 
     @Test
     public void testLogicalFilter(@Mocked Plan child) {
-        LogicalFilter<Plan> actual = new LogicalFilter<>(new EqualTo(Literal.of(1), Literal.of(1)), child);
+        LogicalFilter<Plan> actual = new LogicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(1))), child);
 
-        LogicalFilter<Plan> expected = new LogicalFilter<>(new EqualTo(Literal.of(1), Literal.of(1)), child);
+        LogicalFilter<Plan> expected = new LogicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(1))), child);
         Assertions.assertEquals(expected, actual);
 
-        LogicalFilter<Plan> unexpected = new LogicalFilter<>(new EqualTo(Literal.of(1), Literal.of(2)), child);
+        LogicalFilter<Plan> unexpected = new LogicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(2))), child);
         Assertions.assertNotEquals(unexpected, actual);
     }
 
@@ -209,14 +210,14 @@ public class PlanEqualsTest {
 
     @Test
     public void testPhysicalFilter(@Mocked Plan child, @Mocked LogicalProperties logicalProperties) {
-        PhysicalFilter<Plan> actual = new PhysicalFilter<>(new EqualTo(Literal.of(1), Literal.of(2)),
+        PhysicalFilter<Plan> actual = new PhysicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(2))),
                 logicalProperties, child);
 
-        PhysicalFilter<Plan> expected = new PhysicalFilter<>(new EqualTo(Literal.of(1), Literal.of(2)),
+        PhysicalFilter<Plan> expected = new PhysicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(2))),
                 logicalProperties, child);
         Assertions.assertEquals(expected, actual);
 
-        PhysicalFilter<Plan> unexpected = new PhysicalFilter<>(new EqualTo(Literal.of(1), Literal.of(1)),
+        PhysicalFilter<Plan> unexpected = new PhysicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(1))),
                 logicalProperties, child);
         Assertions.assertNotEquals(unexpected, actual);
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java
index 157c449b6e..b63dad3364 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/plans/PlanToStringTest.java
@@ -34,6 +34,7 @@ import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.util.PlanConstructor;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import mockit.Mocked;
 import org.junit.jupiter.api.Assertions;
@@ -61,7 +62,7 @@ public class PlanToStringTest {
 
     @Test
     public void testLogicalFilter(@Mocked Plan child) {
-        LogicalFilter<Plan> plan = new LogicalFilter<>(new EqualTo(Literal.of(1), Literal.of(1)), child);
+        LogicalFilter<Plan> plan = new LogicalFilter<>(ImmutableSet.of(new EqualTo(Literal.of(1), Literal.of(1))), child);
 
         Assertions.assertEquals("LogicalFilter ( predicates=(1 = 1) )", plan.toString());
     }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
index 2a8aa72952..ba37c768a6 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/LogicalPlanBuilder.java
@@ -36,10 +36,12 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 public class LogicalPlanBuilder {
@@ -123,8 +125,12 @@ public class LogicalPlanBuilder {
         return limit(limit, 0);
     }
 
-    public LogicalPlanBuilder filter(Expression predicate) {
-        LogicalFilter<LogicalPlan> filter = new LogicalFilter<>(predicate, this.plan);
+    public LogicalPlanBuilder filter(Expression conjunct) {
+        return filter(ImmutableSet.copyOf(ExpressionUtils.extractConjunction(conjunct)));
+    }
+
+    public LogicalPlanBuilder filter(Set<Expression> conjuncts) {
+        LogicalFilter<LogicalPlan> filter = new LogicalFilter<>(conjuncts, this.plan);
         return from(filter);
     }
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
index 362ca94f42..da57b21eab 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanChecker.java
@@ -365,7 +365,7 @@ public class PlanChecker {
     private PlanChecker assertMatches(Memo memo, Supplier<Boolean> asserter) {
         Assertions.assertTrue(asserter.get(),
                 () -> "pattern not match, plan :\n"
-                        + memo.getRoot().getLogicalExpression().getPlan().treeString()
+                        + memo.copyOut().treeString()
                         + "\n"
         );
         return this;
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanUtilsTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanUtilsTest.java
index 5218b57e3d..08cb4b087c 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanUtilsTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/PlanUtilsTest.java
@@ -17,7 +17,6 @@
 
 package org.apache.doris.nereids.util;
 
-import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -25,6 +24,7 @@ import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
 import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -49,12 +49,10 @@ class PlanUtilsTest {
     @Test
     void filterOrSelf() {
         LogicalOlapScan scan = PlanConstructor.newLogicalOlapScan(0, "t1", 0);
-        Plan filterOrSelf = PlanUtils.filterOrSelf(Lists.newArrayList(), scan);
+        Plan filterOrSelf = PlanUtils.filterOrSelf(ImmutableSet.of(), scan);
         Assertions.assertSame(scan, filterOrSelf);
 
-        List<Expression> predicate = Lists.newArrayList();
-        predicate.add(BooleanLiteral.TRUE);
-        Plan filter = PlanUtils.filterOrSelf(predicate, scan);
+        Plan filter = PlanUtils.filterOrSelf(ImmutableSet.of(BooleanLiteral.TRUE), scan);
         Assertions.assertTrue(filter instanceof LogicalFilter);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org