You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by li...@apache.org on 2022/07/13 11:05:20 UTC

[doris] branch master updated: (Refactor)[Nereids] Combine operator and plan (#10786)

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

lingmiao 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 e78cca1009 (Refactor)[Nereids] Combine operator and plan (#10786)
e78cca1009 is described below

commit e78cca1009f3ac9253e1af7c7c25d5e057b44403
Author: 924060929 <92...@qq.com>
AuthorDate: Wed Jul 13 19:05:15 2022 +0800

    (Refactor)[Nereids] Combine operator and plan (#10786)
    
    in #9755, we split plan into plan & operator, but in subsequent development, we found the rule became complex and counter intuition:
    1. we must create an operator instance, then wrap a plan by the operator type.
    2. relational algebra(operator) not contains children
    
    e.g.
    ```java
    logicalProject().then(project -> {
        List<NamedExpression> boundSlots =
            bind(project.operator.getProjects(), project.children(), project);
        LogicalProject op = new LogicalProject(flatBoundStar(boundSlots));
        // wrap a plan
        return new LogicalUnaryPlan(op, project.child());
    })
    ```
    
    after combine operator and plan, the code become to:
    ```java
    logicalProject().then(project -> {
        List<NamedExpression> boundSlots =
            bind(project.getProjects(), project.children(), project);
        return new LogicalProject(flatBoundStar(boundSlots), project.child());
    })
    ```
    
    Originally, we thought it would be convenient for `Memo.copyIn()` after split plan & operator, because Memo don't known how to re-new the plan(assembling child plan in the children groups) by the plan type. So plan must provide the `withChildren()` abstract method to assembling children. The less plan type, the lower code cost we have(logical/physical with leaf/unary/binary plan, about 6 plans, no concrete plan e.g. LogicalAggregatePlan).
    
    But the convenient make negative effect that difficult to understand, and people must known the concept then can develop some new rules, and rule become ugly. So we combine the plan & operator, make the rule as simple as possible, the negative effect is we must overwrite some withXxx for all concrete plan, e.g. LogicalAggregate, PhysicalHashJoin.
---
 fe/fe-core/pom.xml                                 |   2 +-
 .../doris/analysis/BuiltinAggregateFunction.java   |   1 +
 .../org/apache/doris/analysis/DateLiteral.java     |   3 +-
 .../org/apache/doris/common/InternalErrorCode.java |   3 +-
 .../org/apache/doris/nereids/NereidsPlanner.java   |   3 +-
 .../doris/nereids/analyzer/UnboundAlias.java       |   4 +-
 .../doris/nereids/analyzer/UnboundFunction.java    |   4 +-
 .../doris/nereids/analyzer/UnboundRelation.java    |  40 ++++++-
 .../apache/doris/nereids/analyzer/UnboundSlot.java |   4 +-
 .../apache/doris/nereids/analyzer/UnboundStar.java |   4 +-
 .../apache/doris/nereids/cost/CostCalculator.java  |  22 ++--
 .../glue/translator/ExpressionTranslator.java      |   4 +-
 .../glue/translator/PhysicalPlanTranslator.java    | 115 ++++++++-----------
 .../java/org/apache/doris/nereids/jobs/Job.java    |   2 +-
 .../nereids/jobs/cascades/CostAndEnforcerJob.java  |   4 +-
 .../java/org/apache/doris/nereids/memo/Group.java  |  13 ++-
 .../apache/doris/nereids/memo/GroupExpression.java |  22 ++--
 .../java/org/apache/doris/nereids/memo/Memo.java   |  15 ++-
 .../doris/nereids/operators/AbstractOperator.java  |  71 ------------
 .../doris/nereids/operators/OperatorVisitor.java   |  61 ----------
 .../operators/plans/BinaryPlanOperator.java        |  24 ----
 .../nereids/operators/plans/LeafPlanOperator.java  |  24 ----
 .../nereids/operators/plans/PlanOperator.java      |  31 -----
 .../nereids/operators/plans/UnaryPlanOperator.java |  24 ----
 .../operators/plans/logical/GroupPlanOperator.java |  60 ----------
 .../operators/plans/logical/LogicalFilter.java     |  59 ----------
 .../plans/physical/PhysicalBinaryOperator.java     |  47 --------
 .../plans/physical/PhysicalDistribution.java       |  42 -------
 .../operators/plans/physical/PhysicalFilter.java   |  61 ----------
 .../operators/plans/physical/PhysicalHashJoin.java |  82 --------------
 .../operators/plans/physical/PhysicalHeapSort.java |  72 ------------
 .../operators/plans/physical/PhysicalOperator.java |  26 -----
 .../operators/plans/physical/PhysicalProject.java  |  62 ----------
 .../plans/physical/PhysicalUnaryOperator.java      |  51 ---------
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  40 +++----
 .../nereids/pattern/GroupExpressionMatching.java   |  10 +-
 .../org/apache/doris/nereids/pattern/Pattern.java  |  57 +++++-----
 .../apache/doris/nereids/pattern/PatternType.java  |   7 +-
 .../org/apache/doris/nereids/pattern/Patterns.java |  97 ++++++++--------
 .../apache/doris/nereids/pattern/TypePattern.java  |  12 +-
 .../generator/LogicalBinaryPatternGenerator.java   |   7 +-
 .../generator/LogicalLeafPatternGenerator.java     |  23 +---
 .../generator/LogicalUnaryPatternGenerator.java    |   7 +-
 .../generator/PatternDescribableProcessor.java     |   8 +-
 .../pattern/generator/PatternGenerator.java        |  18 +--
 .../generator/PatternGeneratorAnalyzer.java        |   8 +-
 .../generator/PhysicalBinaryPatternGenerator.java  |   7 +-
 .../generator/PhysicalLeafPatternGenerator.java    |   5 +-
 .../generator/PhysicalUnaryPatternGenerator.java   |   7 +-
 .../properties/ChildrenOutputPropertyDeriver.java  |  14 +--
 .../doris/nereids/properties/DistributionSpec.java |   8 +-
 .../nereids/properties/LogicalProperties.java      |   2 +-
 .../apache/doris/nereids/properties/OrderSpec.java |   5 +-
 .../properties/ParentRequiredPropertyDeriver.java  |  10 +-
 .../properties/UnboundLogicalProperties.java       |   2 +-
 .../doris/nereids/rules/PlanRuleFactory.java       |   3 +-
 .../doris/nereids/rules/analysis/BindFunction.java |  15 +--
 .../doris/nereids/rules/analysis/BindRelation.java |  14 +--
 .../nereids/rules/analysis/BindSlotReference.java  |  43 +++----
 .../rules/analysis/ProjectToGlobalAggregate.java   |   7 +-
 .../rules/exploration/join/JoinCommutative.java    |  13 ++-
 .../rules/exploration/join/JoinExchange.java       |  23 +---
 .../rules/exploration/join/JoinLAsscom.java        |  17 +--
 .../exploration/join/JoinLeftAssociative.java      |  22 ++--
 .../rules/NormalizeBinaryPredicatesRule.java       |   4 +-
 .../rewrite/rules/SimplifyNotExprRule.java         |   6 +-
 .../LogicalAggToPhysicalHashAgg.java               |  21 ++--
 .../LogicalFilterToPhysicalFilter.java             |  10 +-
 .../implementation/LogicalJoinToHashJoin.java      |  12 +-
 .../LogicalOlapScanToPhysicalOlapScan.java         |  17 ++-
 .../LogicalProjectToPhysicalProject.java           |  10 +-
 .../LogicalSortToPhysicalHeapSort.java             |  13 ++-
 .../rules/rewrite/AggregateDisassemble.java        |  40 +++----
 .../logical/AbstractPushDownProjectRule.java       |   5 +-
 .../rewrite/logical/PruneAggChildColumns.java      |   7 +-
 .../rewrite/logical/PruneFilterChildColumns.java   |  16 +--
 .../rewrite/logical/PruneJoinChildrenColumns.java  |  21 ++--
 .../rewrite/logical/PruneSortChildColumns.java     |  16 +--
 .../rewrite/logical/PushPredicateThroughJoin.java  |  27 +++--
 .../doris/nereids/trees/AbstractTreeNode.java      |  22 +---
 .../org/apache/doris/nereids/trees/TreeNode.java   |   5 -
 .../doris/nereids/trees/expressions/Alias.java     |   3 +-
 .../doris/nereids/trees/expressions/And.java       |   4 +-
 .../nereids/trees/expressions/Arithmetic.java      |  21 ++--
 .../doris/nereids/trees/expressions/Between.java   |   3 +-
 .../trees/expressions/ComparisonPredicate.java     |   3 +-
 .../trees/expressions/CompoundPredicate.java       |  11 +-
 .../doris/nereids/trees/expressions/EqualTo.java   |   3 +-
 .../nereids/trees/expressions/Expression.java      |  11 +-
 .../ExpressionType.java}                           |  20 +---
 .../nereids/trees/expressions/GreaterThan.java     |   3 +-
 .../trees/expressions/GreaterThanEqual.java        |   3 +-
 .../doris/nereids/trees/expressions/LessThan.java  |   3 +-
 .../nereids/trees/expressions/LessThanEqual.java   |   3 +-
 .../doris/nereids/trees/expressions/Like.java      |   3 +-
 .../doris/nereids/trees/expressions/Literal.java   |   5 +-
 .../nereids/trees/expressions/NamedExpression.java |   3 +-
 .../doris/nereids/trees/expressions/Not.java       |   3 +-
 .../nereids/trees/expressions/NullSafeEqual.java   |   3 +-
 .../apache/doris/nereids/trees/expressions/Or.java |   4 +-
 .../doris/nereids/trees/expressions/Regexp.java    |   3 +-
 .../doris/nereids/trees/expressions/Slot.java      |   4 +-
 .../nereids/trees/expressions/SlotReference.java   |   3 +-
 .../trees/expressions/StringRegexPredicate.java    |   3 +-
 .../trees/expressions/functions/BoundFunction.java |   4 +-
 .../doris/nereids/trees/plans/AbstractPlan.java    |  38 +++----
 .../{operators => trees}/plans/AggPhase.java       |   2 +-
 .../doris/nereids/trees/plans/BinaryPlan.java      |   4 -
 .../doris/nereids/trees/plans/GroupPlan.java       |  59 +++++++---
 .../{operators => trees}/plans/JoinType.java       |   2 +-
 .../apache/doris/nereids/trees/plans/LeafPlan.java |  14 +--
 .../org/apache/doris/nereids/trees/plans/Plan.java |  21 +++-
 .../nereids/trees/plans/PlanOperatorVisitor.java   |  93 ---------------
 .../plans/PlanType.java}                           |  18 +--
 .../apache/doris/nereids/trees/plans/Plans.java    |  71 ------------
 .../doris/nereids/trees/plans/UnaryPlan.java       |   4 -
 .../trees/plans/logical/AbstractLogicalPlan.java   |  20 ++--
 .../plans/logical/LogicalAggregate.java            |  79 ++++++++++---
 .../plans/logical/LogicalBinary.java}              |  43 +++----
 .../trees/plans/logical/LogicalBinaryPlan.java     |  76 -------------
 .../nereids/trees/plans/logical/LogicalFilter.java |  91 +++++++++++++++
 .../plans/logical/LogicalJoin.java                 |  52 +++++++--
 .../plans/logical/LogicalLeaf.java}                |  35 +++---
 .../trees/plans/logical/LogicalLeafPlan.java       |  73 ------------
 .../plans/logical/LogicalOlapScan.java             |  34 +++++-
 .../nereids/trees/plans/logical/LogicalPlan.java   |   8 --
 .../plans/logical/LogicalProject.java              |  43 ++++++-
 .../plans/logical/LogicalRelation.java             |  26 ++++-
 .../plans/logical/LogicalSort.java                 |  44 +++++++-
 .../plans/logical/LogicalUnary.java}               |  39 ++++---
 .../trees/plans/logical/LogicalUnaryPlan.java      |  73 ------------
 .../trees/plans/physical/AbstractPhysicalPlan.java |  24 ++--
 .../plans/physical/PhysicalAggregate.java          |  54 +++++++--
 .../trees/plans/physical/PhysicalBinary.java       |  47 ++++++++
 .../trees/plans/physical/PhysicalBinaryPlan.java   |  74 ------------
 .../trees/plans/physical/PhysicalDistribution.java |  77 +++++++++++++
 .../trees/plans/physical/PhysicalFilter.java       |  85 ++++++++++++++
 .../trees/plans/physical/PhysicalHashJoin.java     | 112 ++++++++++++++++++
 .../trees/plans/physical/PhysicalHeapSort.java     | 101 +++++++++++++++++
 .../plans/physical/PhysicalLeaf.java}              |  28 ++---
 .../trees/plans/physical/PhysicalLeafPlan.java     |  69 ------------
 .../plans/physical/PhysicalOlapScan.java           |  34 +++---
 .../nereids/trees/plans/physical/PhysicalPlan.java |   5 +-
 .../trees/plans/physical/PhysicalProject.java      |  86 ++++++++++++++
 .../plans/physical/PhysicalRelation.java}          |  30 ++++-
 .../plans/physical/PhysicalUnary.java}             |  27 +++--
 .../trees/plans/physical/PhysicalUnaryPlan.java    |  70 ------------
 .../plans/visitor/DefaultPlanRewriter.java}        |  31 +++--
 .../plans/visitor/DefaultPlanVisitor.java}         |  16 ++-
 .../nereids/trees/plans/visitor/PlanVisitor.java   | 125 +++++++++++++++++++++
 .../apache/doris/nereids/util/ExpressionUtils.java |  28 ++---
 .../org/apache/doris/nereids/AnalyzeSSBTest.java   |   2 +-
 .../doris/nereids/jobs/RewriteTopDownJobTest.java  |  61 +++++++---
 .../org/apache/doris/nereids/memo/MemoTest.java    |  30 ++---
 .../doris/nereids/parser/NereidsParserTest.java    |   5 +-
 .../pattern/GroupExpressionMatchingTest.java       |  97 +++++++---------
 .../apache/doris/nereids/plan/TestPlanOutput.java  |  44 ++++----
 .../LogicalProjectToPhysicalProjectTest.java       |  12 +-
 .../rewrite/logical/AggregateDisassembleTest.java  |  61 +++++-----
 .../rules/rewrite/logical/ColumnPruningTest.java   |  34 +++---
 .../rewrite/logical/PushDownPredicateTest.java     |  75 +++++++------
 161 files changed, 2029 insertions(+), 2531 deletions(-)

diff --git a/fe/fe-core/pom.xml b/fe/fe-core/pom.xml
index d6dbe82bcd..f5eda91bf7 100644
--- a/fe/fe-core/pom.xml
+++ b/fe/fe-core/pom.xml
@@ -859,7 +859,7 @@ under the License.
                         <configuration>
                             <proc>only</proc>
                             <compilerArgs>
-                                <arg>-AoperatorPath=${basedir}/src/main/java/org/apache/doris/nereids</arg>
+                                <arg>-AplanPath=${basedir}/src/main/java/org/apache/doris/nereids</arg>
                             </compilerArgs>
                             <includes>
                                 <include>org/apache/doris/nereids/pattern/generator/PatternDescribableProcessPoint.java</include>
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/BuiltinAggregateFunction.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/BuiltinAggregateFunction.java
index d14f3400d0..13a58967e6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/BuiltinAggregateFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/BuiltinAggregateFunction.java
@@ -127,6 +127,7 @@ public class BuiltinAggregateFunction extends Function {
         // The intermediate type for this function if it is constant regardless of
         // input type. Set to null if it can only be determined during analysis.
         private final org.apache.doris.catalog.Type intermediateType;
+
         Operator(String description, TAggregationOp thriftOp,
                 org.apache.doris.catalog.Type intermediateType) {
             this.description = description;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
index a20e26000a..fffa15adf4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DateLiteral.java
@@ -158,7 +158,8 @@ public class DateLiteral extends LiteralExpr {
         DATEV2(3);
 
         private final int value;
-        private DateLiteralType(int value) {
+
+        DateLiteralType(int value) {
             this.value = value;
         }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/InternalErrorCode.java b/fe/fe-core/src/main/java/org/apache/doris/common/InternalErrorCode.java
index 20a19937d7..2bbd5c58ef 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/InternalErrorCode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/InternalErrorCode.java
@@ -37,7 +37,8 @@ public enum InternalErrorCode {
     TASKS_ABORT_ERR(104);
 
     private long errCode;
-    private InternalErrorCode(long code) {
+
+    InternalErrorCode(long code) {
         this.errCode = code;
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
index 82c210d9a6..7880cb8f48 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/NereidsPlanner.java
@@ -150,8 +150,7 @@ public class NereidsPlanner extends Planner {
             planChildren.add(chooseBestPlan(groupExpression.child(i), inputPropertiesList.get(i)));
         }
 
-        Plan plan = ((PhysicalPlan) groupExpression.getOperator().toTreeNode(groupExpression)).withChildren(
-                planChildren);
+        Plan plan = groupExpression.getPlan().withChildren(planChildren);
         if (!(plan instanceof PhysicalPlan)) {
             throw new AnalysisException("generate logical plan");
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
index 2c5f372c70..93b41f5938 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundAlias.java
@@ -18,8 +18,8 @@
 package org.apache.doris.nereids.analyzer;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.UnaryExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -35,7 +35,7 @@ import java.util.List;
 public class UnboundAlias extends NamedExpression implements UnaryExpression, Unbound {
 
     public UnboundAlias(Expression child) {
-        super(NodeType.UNBOUND_ALIAS, child);
+        super(ExpressionType.UNBOUND_ALIAS, child);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java
index 0de54a6d20..f45fc639b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundFunction.java
@@ -18,8 +18,8 @@
 package org.apache.doris.nereids.analyzer;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Joiner;
@@ -37,7 +37,7 @@ public class UnboundFunction extends Expression implements Unbound {
     private final boolean isDistinct;
 
     public UnboundFunction(String name, boolean isDistinct, List<Expression> arguments) {
-        super(NodeType.UNBOUND_FUNCTION, arguments.toArray(new Expression[0]));
+        super(ExpressionType.UNBOUND_FUNCTION, arguments.toArray(new Expression[0]));
         this.name = Objects.requireNonNull(name, "name can not be null");
         this.isDistinct = isDistinct;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
index c3280286a4..4bd8413d0e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundRelation.java
@@ -19,38 +19,51 @@ package org.apache.doris.nereids.analyzer;
 
 import org.apache.doris.nereids.analyzer.identifier.TableIdentifier;
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
+import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.UnboundLogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.nereids.util.Utils;
 
 import com.google.common.collect.Lists;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
  * Represent a relation plan node that has not been bound.
  */
-public class UnboundRelation extends LogicalLeafOperator implements Unbound {
+public class UnboundRelation extends LogicalLeaf implements Unbound {
     private final List<String> nameParts;
 
     public UnboundRelation(List<String> nameParts) {
-        super(OperatorType.LOGICAL_UNBOUND_RELATION);
+        this(nameParts, Optional.empty(), Optional.empty());
+    }
+
+    public UnboundRelation(List<String> nameParts, Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties) {
+        super(PlanType.LOGICAL_UNBOUND_RELATION, groupExpression, logicalProperties);
         this.nameParts = nameParts;
     }
 
+    public UnboundRelation(TableIdentifier identifier) {
+        this(identifier, Optional.empty(), Optional.empty());
+    }
+
     /**
      * Constructor for UnboundRelation.
      *
      * @param identifier relation identifier
      */
-    public UnboundRelation(TableIdentifier identifier) {
-        super(OperatorType.LOGICAL_UNBOUND_RELATION);
+    public UnboundRelation(TableIdentifier identifier, Optional<GroupExpression> groupExpression,
+            Optional<LogicalProperties> logicalProperties) {
+        super(PlanType.LOGICAL_UNBOUND_RELATION, groupExpression, logicalProperties);
         this.nameParts = Lists.newArrayList();
         if (identifier.getDatabaseName().isPresent()) {
             nameParts.add(identifier.getDatabaseName().get());
@@ -72,6 +85,16 @@ public class UnboundRelation extends LogicalLeafOperator implements Unbound {
         return new UnboundLogicalProperties();
     }
 
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new UnboundRelation(nameParts, groupExpression, Optional.of(logicalProperties));
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new UnboundRelation(nameParts, Optional.empty(), logicalProperties);
+    }
+
     @Override
     public List<Slot> computeOutput() {
         throw new UnboundException("output");
@@ -82,6 +105,11 @@ public class UnboundRelation extends LogicalLeafOperator implements Unbound {
         return "UnresolvedRelation" + "(" + StringUtils.join(nameParts, ".") + ")";
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitUnboundRelation(this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         throw new UnsupportedOperationException(this.getClass().getSimpleName() + " don't support getExpression()");
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
index 0421e24c17..52baded855 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundSlot.java
@@ -17,7 +17,7 @@
 
 package org.apache.doris.nereids.analyzer;
 
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.util.Utils;
@@ -33,7 +33,7 @@ public class UnboundSlot extends Slot implements Unbound {
     private final List<String> nameParts;
 
     public UnboundSlot(List<String> nameParts) {
-        super(NodeType.UNBOUND_SLOT);
+        super(ExpressionType.UNBOUND_SLOT);
         this.nameParts = nameParts;
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
index 0c6d1cae5c..6546d82826 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/analyzer/UnboundStar.java
@@ -18,7 +18,7 @@
 package org.apache.doris.nereids.analyzer;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.LeafExpression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
@@ -35,7 +35,7 @@ public class UnboundStar extends NamedExpression implements LeafExpression, Unbo
     private final List<String> qualifier;
 
     public UnboundStar(List<String> qualifier) {
-        super(NodeType.UNBOUND_STAR);
+        super(ExpressionType.UNBOUND_STAR);
         this.qualifier = qualifier;
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java
index 6e7bfd895e..0950ed9643 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/cost/CostCalculator.java
@@ -20,12 +20,12 @@ package org.apache.doris.nereids.cost;
 import org.apache.doris.common.Id;
 import org.apache.doris.nereids.PlanContext;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.OperatorVisitor;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalAggregate;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOlapScan;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalProject;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.statistics.StatsDeriveResult;
 
 import com.google.common.base.Preconditions;
@@ -43,7 +43,7 @@ public class CostCalculator {
     public static double calculateCost(GroupExpression groupExpression) {
         PlanContext planContext = new PlanContext(groupExpression);
         CostEstimator costCalculator = new CostEstimator();
-        CostEstimate costEstimate = groupExpression.getOperator().accept(costCalculator, planContext);
+        CostEstimate costEstimate = groupExpression.getPlan().accept(costCalculator, planContext);
         return costFormula(costEstimate);
     }
 
@@ -55,14 +55,14 @@ public class CostCalculator {
                 + costEstimate.getNetworkCost() * networkCostWeight;
     }
 
-    private static class CostEstimator extends OperatorVisitor<CostEstimate, PlanContext> {
+    private static class CostEstimator extends PlanVisitor<CostEstimate, PlanContext> {
         @Override
-        public CostEstimate visitOperator(Operator operator, PlanContext context) {
+        public CostEstimate visit(Plan plan, PlanContext context) {
             return CostEstimate.zero();
         }
 
         @Override
-        public CostEstimate visitPhysicalAggregation(PhysicalAggregate physicalAggregate, PlanContext context) {
+        public CostEstimate visitPhysicalAggregate(PhysicalAggregate<Plan> aggregate, PlanContext context) {
             StatsDeriveResult statistics = context.getStatisticsWithCheck();
             return CostEstimate.ofCpu(statistics.computeSize());
         }
@@ -74,7 +74,7 @@ public class CostCalculator {
         }
 
         @Override
-        public CostEstimate visitPhysicalHashJoin(PhysicalHashJoin physicalHashJoin, PlanContext context) {
+        public CostEstimate visitPhysicalHashJoin(PhysicalHashJoin<Plan, Plan> physicalHashJoin, PlanContext context) {
             Preconditions.checkState(context.getGroupExpression().arity() == 2);
             Preconditions.checkState(context.getChildrenStats().size() == 2);
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
index 463eddf7ac..5fa60994c0 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java
@@ -29,13 +29,13 @@ import org.apache.doris.analysis.NullLiteral;
 import org.apache.doris.analysis.StringLiteral;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Arithmetic;
 import org.apache.doris.nereids.trees.expressions.Between;
 import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.LessThan;
@@ -170,7 +170,7 @@ public class ExpressionTranslator extends DefaultExpressionVisitor<Expr, PlanTra
 
     @Override
     public Expr visitCompoundPredicate(CompoundPredicate compoundPredicate, PlanTranslatorContext context) {
-        NodeType nodeType = compoundPredicate.getType();
+        ExpressionType nodeType = compoundPredicate.getType();
         org.apache.doris.analysis.CompoundPredicate.Operator staleOp;
         switch (nodeType) {
             case OR:
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 b790f37085..0467ef4f22 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
@@ -29,15 +29,6 @@ import org.apache.doris.analysis.TupleDescriptor;
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Table;
 import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.operators.plans.AggPhase;
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalAggregate;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalFilter;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHeapSort;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOlapScan;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalProject;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.ExprId;
@@ -47,12 +38,17 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
+import org.apache.doris.nereids.trees.plans.AggPhase;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHeapSort;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
 import org.apache.doris.nereids.util.ExpressionUtils;
 import org.apache.doris.planner.AggregationNode;
 import org.apache.doris.planner.DataPartition;
@@ -84,7 +80,7 @@ import java.util.stream.Collectors;
  *      Must always visit plan's children first when you implement a method to translate from PhysicalPlan to PlanNode.
  * </STRONG>
  */
-public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, PlanTranslatorContext> {
+public class PhysicalPlanTranslator extends DefaultPlanVisitor<PlanFragment, PlanTranslatorContext> {
     /**
      * The left and right child of origin predicates need to be swap sometimes.
      * Case A:
@@ -111,7 +107,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
      * @return Stale Planner PlanFragment tree
      */
     public PlanFragment translatePlan(PhysicalPlan physicalPlan, PlanTranslatorContext context) {
-        PlanFragment rootFragment = visit(physicalPlan, context);
+        PlanFragment rootFragment = physicalPlan.accept(this, context);
         if (rootFragment.isPartitioned() && rootFragment.getPlanRoot().getNumInstances() > 1) {
             rootFragment = exchangeToMergeFragment(rootFragment, context);
         }
@@ -127,21 +123,13 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         return rootFragment;
     }
 
-    @Override
-    public PlanFragment visit(Plan plan, PlanTranslatorContext context) {
-        PhysicalOperator operator = (PhysicalOperator) plan.getOperator();
-        return operator.accept(this, plan, context);
-    }
-
     /**
      * Translate Agg.
      * todo: support DISTINCT
      */
     @Override
-    public PlanFragment visitPhysicalAggregate(
-            PhysicalUnaryPlan<PhysicalAggregate, Plan> aggregate, PlanTranslatorContext context) {
-        PlanFragment inputPlanFragment = visit(aggregate.child(0), context);
-        PhysicalAggregate physicalAggregate = aggregate.getOperator();
+    public PlanFragment visitPhysicalAggregate(PhysicalAggregate<Plan> aggregate, PlanTranslatorContext context) {
+        PlanFragment inputPlanFragment = aggregate.child(0).accept(this, context);
 
         // TODO: stale planner generate aggregate tuple in a special way. tuple include 2 parts:
         //    1. group by expressions: removing duplicate expressions add to tuple
@@ -151,8 +139,8 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         //    1. add a project after agg, if agg function is not the top output expression.
         //    2. introduce canonicalized, semanticEquals and deterministic in Expression
         //       for removing duplicate.
-        List<Expression> groupByExpressionList = physicalAggregate.getGroupByExprList();
-        List<NamedExpression> outputExpressionList = physicalAggregate.getOutputExpressionList();
+        List<Expression> groupByExpressionList = aggregate.getGroupByExprList();
+        List<NamedExpression> outputExpressionList = aggregate.getOutputExpressionList();
 
         // 1. generate slot reference for each group expression
         List<SlotReference> groupSlotList = Lists.newArrayList();
@@ -182,7 +170,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         //  split merge agg to project(agg) and generate tuple like what first phase agg do.
         List<Slot> slotList = Lists.newArrayList();
         TupleDescriptor outputTupleDesc;
-        if (physicalAggregate.getAggPhase() == AggPhase.GLOBAL) {
+        if (aggregate.getAggPhase() == AggPhase.GLOBAL) {
             slotList.addAll(groupSlotList);
             slotList.addAll(aggFunctionOutput);
             outputTupleDesc = generateTupleDesc(slotList, null, context);
@@ -191,7 +179,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         }
 
         // process partition list
-        List<Expression> partitionExpressionList = physicalAggregate.getPartitionExprList();
+        List<Expression> partitionExpressionList = aggregate.getPartitionExprList();
         List<Expr> execPartitionExpressions = partitionExpressionList.stream()
                 .map(e -> ExpressionTranslator.translate(e, context)).collect(Collectors.toList());
         DataPartition mergePartition = DataPartition.UNPARTITIONED;
@@ -199,20 +187,20 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
             mergePartition = DataPartition.hashPartitioned(execGroupingExpressions);
         }
 
-        if (physicalAggregate.getAggPhase() == AggPhase.GLOBAL) {
+        if (aggregate.getAggPhase() == AggPhase.GLOBAL) {
             for (FunctionCallExpr execAggregateFunction : execAggregateFunctions) {
                 execAggregateFunction.setMergeForNereids(true);
             }
         }
         AggregateInfo aggInfo = AggregateInfo.create(execGroupingExpressions, execAggregateFunctions, outputTupleDesc,
-                outputTupleDesc, physicalAggregate.getAggPhase().toExec());
+                outputTupleDesc, aggregate.getAggPhase().toExec());
         AggregationNode aggregationNode = new AggregationNode(context.nextPlanNodeId(),
                 inputPlanFragment.getPlanRoot(), aggInfo);
         inputPlanFragment.setPlanRoot(aggregationNode);
-        switch (physicalAggregate.getAggPhase()) {
+        switch (aggregate.getAggPhase()) {
             case LOCAL:
                 aggregationNode.unsetNeedsFinalize();
-                aggregationNode.setUseStreamingPreagg(physicalAggregate.isUsingStream());
+                aggregationNode.setUseStreamingPreagg(aggregate.isUsingStream());
                 aggregationNode.setIntermediateTuple();
                 return createParentFragment(inputPlanFragment, mergePartition, context);
             case GLOBAL:
@@ -224,13 +212,11 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
     }
 
     @Override
-    public PlanFragment visitPhysicalOlapScan(
-            PhysicalLeafPlan<PhysicalOlapScan> olapScan, PlanTranslatorContext context) {
+    public PlanFragment visitPhysicalOlapScan(PhysicalOlapScan olapScan, PlanTranslatorContext context) {
         // Create OlapScanNode
         List<Slot> slotList = olapScan.getOutput();
-        PhysicalOlapScan physicalOlapScan = olapScan.getOperator();
-        OlapTable olapTable = physicalOlapScan.getTable();
-        List<Expr> execConjunctsList = physicalOlapScan
+        OlapTable olapTable = olapScan.getTable();
+        List<Expr> execConjunctsList = olapScan
                 .getExpressions()
                 .stream()
                 .map(e -> ExpressionTranslator.translate(e, context)).collect(Collectors.toList());
@@ -242,9 +228,9 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         TableRef ref = new TableRef(tableName, null, null);
         BaseTableRef tableRef = new BaseTableRef(ref, olapTable, tableName);
         tupleDescriptor.setRef(tableRef);
-        olapScanNode.setSelectedPartitionIds(physicalOlapScan.getSelectedPartitionId());
+        olapScanNode.setSelectedPartitionIds(olapScan.getSelectedPartitionId());
         try {
-            olapScanNode.updateScanRangeInfoByNewMVSelector(physicalOlapScan.getSelectedIndexId(), false, "");
+            olapScanNode.updateScanRangeInfoByNewMVSelector(olapScan.getSelectedIndexId(), false, "");
         } catch (Exception e) {
             throw new AnalysisException(e.getMessage());
         }
@@ -279,16 +265,15 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
      *       After a+1 can be parsed , reprocessing.
      */
     @Override
-    public PlanFragment visitPhysicalHeapSort(PhysicalUnaryPlan<PhysicalHeapSort, Plan> sort,
+    public PlanFragment visitPhysicalHeapSort(PhysicalHeapSort<Plan> sort,
             PlanTranslatorContext context) {
-        PlanFragment childFragment = visit(sort.child(0), context);
-        PhysicalHeapSort physicalHeapSort = sort.getOperator();
-        long limit = physicalHeapSort.getLimit();
+        PlanFragment childFragment = sort.child(0).accept(this, context);
+        long limit = sort.getLimit();
         // TODO: need to discuss how to process field: SortNode::resolvedTupleExprs
         List<Expr> oldOrderingExprList = Lists.newArrayList();
         List<Boolean> ascOrderList = Lists.newArrayList();
         List<Boolean> nullsFirstParamList = Lists.newArrayList();
-        List<OrderKey> orderKeyList = physicalHeapSort.getOrderKeys();
+        List<OrderKey> orderKeyList = sort.getOrderKeys();
         // 1.Get previous slotRef
         orderKeyList.forEach(k -> {
             oldOrderingExprList.add(ExpressionTranslator.translate(k.getExpr(), context));
@@ -312,7 +297,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         PlanNode childNode = childFragment.getPlanRoot();
         // TODO: notice topN
         SortNode sortNode = new SortNode(context.nextPlanNodeId(), childNode, sortInfo, true,
-                physicalHeapSort.hasLimit(), physicalHeapSort.getOffset());
+                sort.hasLimit(), sort.getOffset());
         sortNode.finalizeForNereids(tupleDesc, sortTupleOutputList, oldOrderingExprList);
         childFragment.addPlanRoot(sortNode);
         if (!childFragment.isPartitioned()) {
@@ -321,10 +306,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
         PlanFragment mergeFragment = createParentFragment(childFragment, DataPartition.UNPARTITIONED, context);
         ExchangeNode exchangeNode = (ExchangeNode) mergeFragment.getPlanRoot();
         exchangeNode.unsetLimit();
-        if (physicalHeapSort.hasLimit()) {
-            exchangeNode.setLimit(limit);
-        }
-        long offset = physicalHeapSort.getOffset();
+        long offset = sort.getOffset();
         exchangeNode.setMergeInfo(sortNode.getSortInfo(), offset);
 
         // Child nodes should not process the offset. If there is a limit,
@@ -343,22 +325,19 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
     // TODO: 1. support shuffle join / co-locate / bucket shuffle join later
     //       2. For ssb, there are only binary equal predicate, we shall support more in the future.
     @Override
-    public PlanFragment visitPhysicalHashJoin(
-            PhysicalBinaryPlan<PhysicalHashJoin, Plan, Plan> hashJoin, PlanTranslatorContext context) {
+    public PlanFragment visitPhysicalHashJoin(PhysicalHashJoin<Plan, Plan> hashJoin, PlanTranslatorContext context) {
         // NOTICE: We must visit from right to left, to ensure the last fragment is root fragment
-        PlanFragment rightFragment = visit(hashJoin.child(1), context);
-        PlanFragment leftFragment = visit(hashJoin.child(0), context);
+        PlanFragment rightFragment = hashJoin.child(1).accept(this, context);
+        PlanFragment leftFragment = hashJoin.child(0).accept(this, context);
         PlanNode leftFragmentPlanRoot = leftFragment.getPlanRoot();
         PlanNode rightFragmentPlanRoot = rightFragment.getPlanRoot();
-        PhysicalHashJoin physicalHashJoin = hashJoin.getOperator();
-        JoinType joinType = physicalHashJoin.getJoinType();
+        JoinType joinType = hashJoin.getJoinType();
 
         if (joinType.equals(JoinType.CROSS_JOIN)
-                || physicalHashJoin.getJoinType().equals(JoinType.INNER_JOIN)
-                && !physicalHashJoin.getCondition().isPresent()) {
+                || (joinType.equals(JoinType.INNER_JOIN) && !hashJoin.getCondition().isPresent())) {
             throw new RuntimeException("Physical hash join could not execute without equal join condition.");
         } else {
-            Expression eqJoinExpression = physicalHashJoin.getCondition().get();
+            Expression eqJoinExpression = hashJoin.getCondition().get();
             List<Expr> execEqConjunctList = ExpressionUtils.extractConjunct(eqJoinExpression).stream()
                     .map(EqualTo.class::cast)
                     .map(e -> swapEqualToForChildrenOrder(e, hashJoin.left().getOutput()))
@@ -367,7 +346,7 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
 
             HashJoinNode hashJoinNode = new HashJoinNode(context.nextPlanNodeId(), leftFragmentPlanRoot,
                     rightFragmentPlanRoot,
-                    JoinType.toJoinOperator(physicalHashJoin.getJoinType()), execEqConjunctList, Lists.newArrayList());
+                    JoinType.toJoinOperator(joinType), execEqConjunctList, Lists.newArrayList());
 
             hashJoinNode.setDistributionMode(DistributionMode.BROADCAST);
             hashJoinNode.setChild(0, leftFragmentPlanRoot);
@@ -379,11 +358,9 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
 
     // TODO: generate expression mapping when be project could do in ExecNode
     @Override
-    public PlanFragment visitPhysicalProject(
-            PhysicalUnaryPlan<PhysicalProject, Plan> projectPlan, PlanTranslatorContext context) {
-        PlanFragment inputFragment = visit(projectPlan.child(0), context);
-        PhysicalProject physicalProject = projectPlan.getOperator();
-        List<Expr> execExprList = physicalProject.getProjects()
+    public PlanFragment visitPhysicalProject(PhysicalProject<Plan> project, PlanTranslatorContext context) {
+        PlanFragment inputFragment = project.child(0).accept(this, context);
+        List<Expr> execExprList = project.getProjects()
                 .stream()
                 .map(e -> ExpressionTranslator.translate(e, context))
                 .collect(Collectors.toList());
@@ -402,18 +379,16 @@ public class PhysicalPlanTranslator extends PlanOperatorVisitor<PlanFragment, Pl
     }
 
     @Override
-    public PlanFragment visitPhysicalFilter(
-            PhysicalUnaryPlan<PhysicalFilter, Plan> filterPlan, PlanTranslatorContext context) {
-        PlanFragment inputFragment = visit(filterPlan.child(0), context);
+    public PlanFragment visitPhysicalFilter(PhysicalFilter<Plan> filter, PlanTranslatorContext context) {
+        PlanFragment inputFragment = filter.child(0).accept(this, context);
         PlanNode planNode = inputFragment.getPlanRoot();
-        PhysicalFilter filter = filterPlan.getOperator();
         Expression expression = filter.getPredicates();
         List<Expression> expressionList = ExpressionUtils.extractConjunct(expression);
         expressionList.stream().map(e -> ExpressionTranslator.translate(e, context)).forEach(planNode::addConjunct);
         return inputFragment;
     }
 
-    private void extractExecSlot(Expr root, Set<Integer>  slotRefList) {
+    private void extractExecSlot(Expr root, Set<Integer> slotRefList) {
         if (root instanceof SlotRef) {
             slotRefList.add(((SlotRef) root).getDesc().getId().asInt());
             return;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
index 8363ef7af7..2c2eed9e7e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/Job.java
@@ -57,7 +57,7 @@ public abstract class Job<NODE_TYPE extends TreeNode<NODE_TYPE>> {
     public List<Rule<NODE_TYPE>> getValidRules(GroupExpression groupExpression,
             List<Rule<NODE_TYPE>> candidateRules) {
         return candidateRules.stream()
-                .filter(rule -> Objects.nonNull(rule) && rule.getPattern().matchOperator(groupExpression.getOperator())
+                .filter(rule -> Objects.nonNull(rule) && rule.getPattern().matchRoot(groupExpression.getPlan())
                         && groupExpression.notApplied(rule)).collect(Collectors.toList());
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/CostAndEnforcerJob.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/CostAndEnforcerJob.java
index 5516525233..88d4bb0293 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/CostAndEnforcerJob.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/cascades/CostAndEnforcerJob.java
@@ -83,11 +83,11 @@ public class CostAndEnforcerJob extends Job<Plan> {
      * execute.
      */
     public void execute1() {
-        // Do init logic of root operator/groupExpr of `subplan`, only run once per task.
+        // Do init logic of root plan/groupExpr of `subplan`, only run once per task.
         if (curChildIndex != -1) {
             curTotalCost = 0;
 
-            // Get property from groupExpression operator (it's root of subplan).
+            // Get property from groupExpression plan (it's root of subplan).
             ParentRequiredPropertyDeriver parentRequiredPropertyDeriver = new ParentRequiredPropertyDeriver(context);
             propertiesListList = parentRequiredPropertyDeriver.getRequiredPropertyListList(groupExpression);
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
index a9343cdd97..fbb9a862c7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Group.java
@@ -19,10 +19,10 @@ package org.apache.doris.nereids.memo;
 
 import org.apache.doris.common.Pair;
 import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
 import org.apache.doris.nereids.trees.plans.physical.PhysicalPlan;
 import org.apache.doris.statistics.StatsDeriveResult;
 
@@ -60,7 +60,7 @@ public class Group {
      */
     public Group(GroupId groupId, GroupExpression groupExpression, LogicalProperties logicalProperties) {
         this.groupId = groupId;
-        if (groupExpression.getOperator() instanceof LogicalOperator) {
+        if (groupExpression.getPlan() instanceof LogicalPlan) {
             this.logicalExpressions.add(groupExpression);
         } else {
             this.physicalExpressions.add(groupExpression);
@@ -88,7 +88,7 @@ public class Group {
      * @return added {@link GroupExpression}
      */
     public GroupExpression addGroupExpression(GroupExpression groupExpression) {
-        if (groupExpression.getOperator() instanceof LogicalOperator) {
+        if (groupExpression.getPlan() instanceof LogicalPlan) {
             logicalExpressions.add(groupExpression);
         } else {
             physicalExpressions.add(groupExpression);
@@ -104,7 +104,7 @@ public class Group {
      * @return removed {@link GroupExpression}
      */
     public GroupExpression removeGroupExpression(GroupExpression groupExpression) {
-        if (groupExpression.getOperator() instanceof LogicalOperator) {
+        if (groupExpression.getPlan() instanceof LogicalPlan) {
             logicalExpressions.remove(groupExpression);
         } else {
             physicalExpressions.remove(groupExpression);
@@ -233,8 +233,9 @@ public class Group {
             planChildren.add(groupExpression.child(i).extractPlan());
         }
 
-        Plan plan = ((PhysicalPlan) groupExpression.getOperator().toTreeNode(groupExpression)).withChildren(
-                planChildren);
+        Plan plan = groupExpression.getPlan()
+                .withChildren(planChildren)
+                .withGroupExpression(Optional.of(groupExpression));
         if (!(plan instanceof PhysicalPlan)) {
             throw new AnalysisException("generate logical plan");
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
index da4373de66..3ff6d9b4dc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/GroupExpression.java
@@ -18,10 +18,10 @@
 package org.apache.doris.nereids.memo;
 
 import org.apache.doris.common.Pair;
-import org.apache.doris.nereids.operators.Operator;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
+import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
@@ -38,7 +38,7 @@ import java.util.Objects;
 public class GroupExpression {
     private Group parent;
     private List<Group> children;
-    private final Operator op;
+    private final Plan plan;
     private final BitSet ruleMasks;
     private boolean statDerived;
 
@@ -47,18 +47,18 @@ public class GroupExpression {
     // Each physical group expression maintains mapping incoming requests to the corresponding child requests.
     private final Map<PhysicalProperties, PhysicalProperties> requestPropertiesMap;
 
-    public GroupExpression(Operator op) {
-        this(op, Lists.newArrayList());
+    public GroupExpression(Plan plan) {
+        this(plan, Lists.newArrayList());
     }
 
     /**
      * Constructor for GroupExpression.
      *
-     * @param op {@link Operator} to reference
+     * @param plan {@link Plan} to reference
      * @param children children groups in memo
      */
-    public GroupExpression(Operator op, List<Group> children) {
-        this.op = Objects.requireNonNull(op);
+    public GroupExpression(Plan plan, List<Group> children) {
+        this.plan = Objects.requireNonNull(plan, "plan can not be null");
         this.children = Objects.requireNonNull(children);
         this.ruleMasks = new BitSet(RuleType.SENTINEL.ordinal());
         this.statDerived = false;
@@ -89,8 +89,8 @@ public class GroupExpression {
         this.parent = parent;
     }
 
-    public Operator getOperator() {
-        return op;
+    public Plan getPlan() {
+        return plan;
     }
 
     public Group child(int i) {
@@ -167,11 +167,11 @@ public class GroupExpression {
             return false;
         }
         GroupExpression that = (GroupExpression) o;
-        return children.equals(that.children) && op.equals(that.op);
+        return children.equals(that.children) && plan.equals(that.plan);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(children, op);
+        return Objects.hash(children, plan);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
index e07f4e46ee..78f4cb1d8f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/memo/Memo.java
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
@@ -84,7 +85,8 @@ public class Memo {
                 childrenGroups.add(copyIn(child, null, rewrite).getParent());
             }
         }
-        GroupExpression newGroupExpression = new GroupExpression(node.getOperator());
+        node = replaceChildrenToGroupPlan(node, childrenGroups);
+        GroupExpression newGroupExpression = new GroupExpression(node);
         newGroupExpression.setChildren(childrenGroups);
         return insertOrRewriteGroupExpression(newGroupExpression, target, rewrite, node.getLogicalProperties());
         // TODO: need to derive logical property if generate new group. currently we not copy logical plan into
@@ -100,7 +102,7 @@ public class Memo {
         for (Group child : logicalExpression.children()) {
             childrenNode.add(groupToTreeNode(child));
         }
-        Plan result = logicalExpression.getOperator().toTreeNode(logicalExpression);
+        Plan result = logicalExpression.getPlan();
         if (result.children().size() == 0) {
             return result;
         }
@@ -200,4 +202,13 @@ public class Memo {
     public void addEnforcerPlan(GroupExpression groupExpression, Group group) {
         groupExpression.setParent(group);
     }
+
+    private Plan replaceChildrenToGroupPlan(Plan plan, List<Group> childrenGroups) {
+        List<Plan> groupPlanChildren = childrenGroups.stream()
+                .map(group -> new GroupPlan(group))
+                .collect(ImmutableList.toImmutableList());
+        LogicalProperties logicalProperties = plan.getLogicalProperties();
+        return plan.withChildren(groupPlanChildren)
+            .withLogicalProperties(Optional.of(logicalProperties));
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/AbstractOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/AbstractOperator.java
deleted file mode 100644
index c3a4098f84..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/AbstractOperator.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators;
-
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-
-import java.util.Objects;
-
-/**
- * Abstract class for all concrete operator.
- */
-public abstract class AbstractOperator implements Operator {
-    protected final OperatorType type;
-    protected final long limit;
-
-    public AbstractOperator(OperatorType type) {
-        this.type = Objects.requireNonNull(type, "type can not be null");
-        this.limit = -1;
-    }
-
-    public AbstractOperator(OperatorType type, long limit) {
-        this.type = type;
-        this.limit = limit;
-    }
-
-    @Override
-    public OperatorType getType() {
-        return type;
-    }
-
-    public long getLimit() {
-        return limit;
-    }
-
-    /**
-     * Child operator should overwrite this method.
-     * for example:
-     * <code>
-     * visitor.visitPhysicalOlapScanPlan(
-     * (PhysicalPlan<? extends PhysicalPlan, PhysicalOlapScan>) plan, context);
-     * </code>
-     */
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return null;
-    }
-
-    public <R, C> R accept(OperatorVisitor<R, C> visitor, C context) {
-        return visitor.visitOperator(this, context);
-    }
-
-    public <R, C> R accept(OperatorVisitor<R, C> visitor, Operator operator, C context) {
-        return null;
-    }
-
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorVisitor.java
deleted file mode 100644
index 1851ee6a6a..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorVisitor.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators;
-
-import org.apache.doris.nereids.operators.plans.physical.PhysicalAggregate;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalFilter;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHeapSort;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOlapScan;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalProject;
-
-/**
- * Base class for the processing of logical and physical operator.
- *
- * @param <R> Return type of each visit method.
- * @param <C> Context type.
- */
-public abstract class OperatorVisitor<R, C> {
-    /* Operator */
-
-    public abstract R visitOperator(Operator operator, C context);
-
-    public R visitPhysicalAggregation(PhysicalAggregate physicalAggregate, C context) {
-        return null;
-    }
-
-    public R visitPhysicalOlapScan(PhysicalOlapScan physicalOlapScan, C context) {
-        return null;
-    }
-
-    public R visitPhysicalHeapSort(PhysicalHeapSort physicalHeapSort, C context) {
-        return null;
-    }
-
-    public R visitPhysicalHashJoin(PhysicalHashJoin physicalHashJoin, C context) {
-        return null;
-    }
-
-    public R visitPhysicalProject(PhysicalProject physicalProject,  C context) {
-        return null;
-    }
-
-    public R visitPhysicalFilter(PhysicalFilter physicalFilter, C context) {
-        return null;
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/BinaryPlanOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/BinaryPlanOperator.java
deleted file mode 100644
index 30fea7cbac..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/BinaryPlanOperator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans;
-
-/**
- * interface for all concrete binary plan operator.
- */
-public interface BinaryPlanOperator extends PlanOperator {
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/LeafPlanOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/LeafPlanOperator.java
deleted file mode 100644
index 9efe4c0386..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/LeafPlanOperator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans;
-
-/**
- * interface for all concrete leaf plan operator.
- */
-public interface LeafPlanOperator extends PlanOperator {
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/PlanOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/PlanOperator.java
deleted file mode 100644
index d7895ddc8a..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/PlanOperator.java
+++ /dev/null
@@ -1,31 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans;
-
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.trees.expressions.Expression;
-
-import java.util.List;
-
-/**
- * interface for all concrete plan operator.
- */
-public interface PlanOperator extends Operator {
-
-    List<Expression> getExpressions();
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/UnaryPlanOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/UnaryPlanOperator.java
deleted file mode 100644
index 321f7a6f78..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/UnaryPlanOperator.java
+++ /dev/null
@@ -1,24 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans;
-
-/**
- * interface for all concrete unary plan operator.
- */
-public interface UnaryPlanOperator extends PlanOperator {
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/GroupPlanOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/GroupPlanOperator.java
deleted file mode 100644
index 2ff3106e33..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/GroupPlanOperator.java
+++ /dev/null
@@ -1,60 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.logical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-
-
-/** GroupPlanOperator. */
-public class GroupPlanOperator extends LogicalLeafOperator {
-    public GroupPlanOperator() {
-        super(OperatorType.GROUP_PLAN);
-    }
-
-    @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitGroupPlan((GroupPlan) plan, context);
-    }
-
-    @Override
-    public List<Slot> computeOutput() {
-        throw new IllegalStateException("GroupPlanOperator can not compute output."
-            + " You should invoke GroupPlan.getOutput()");
-    }
-
-    @Override
-    public LogicalProperties computeLogicalProperties(Plan... inputs) {
-        throw new IllegalStateException("GroupPlanOperator can not compute logical properties."
-            + " You should invoke GroupPlan.getLogicalProperties()");
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return ImmutableList.of();
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java
deleted file mode 100644
index 960739f102..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalFilter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.logical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Logical filter plan operator.
- */
-public class LogicalFilter extends LogicalUnaryOperator {
-    private final Expression predicates;
-
-    public LogicalFilter(Expression predicates) {
-        super(OperatorType.LOGICAL_FILTER);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
-    }
-
-    public Expression getPredicates() {
-        return predicates;
-    }
-
-    @Override
-    public List<Slot> computeOutput(Plan input) {
-        return input.getOutput();
-    }
-
-    @Override
-    public String toString() {
-        return "LogicalFilter (" + predicates + ")";
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return ImmutableList.of(predicates);
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java
deleted file mode 100644
index a7b81cb7fb..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalBinaryOperator.java
+++ /dev/null
@@ -1,47 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.BinaryPlanOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-
-import java.util.Optional;
-
-/**
- * Abstract class for all physical operator that have two inputs.
- */
-public abstract class PhysicalBinaryOperator extends AbstractOperator
-        implements PhysicalOperator, BinaryPlanOperator {
-
-    public PhysicalBinaryOperator(OperatorType type) {
-        super(type);
-    }
-
-    @Override
-    public PhysicalBinaryPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new PhysicalBinaryPlan(this, Optional.of(groupExpression), logicalProperties,
-                new GroupPlan(groupExpression.child(0)), new GroupPlan(groupExpression.child(0))
-        );
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalDistribution.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalDistribution.java
deleted file mode 100644
index 64c23ee024..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalDistribution.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.properties.DistributionSpec;
-import org.apache.doris.nereids.trees.expressions.Expression;
-
-import java.util.List;
-
-/**
- * Enforcer operator.
- */
-public class PhysicalDistribution extends PhysicalUnaryOperator {
-
-    protected DistributionSpec distributionSpec;
-
-
-    public PhysicalDistribution(DistributionSpec spec) {
-        super(OperatorType.PHYSICAL_DISTRIBUTION);
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return null;
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalFilter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalFilter.java
deleted file mode 100644
index 8dad1e39c8..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalFilter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Physical filter plan operator.
- */
-public class PhysicalFilter extends PhysicalUnaryOperator {
-
-    private final Expression predicates;
-
-    public PhysicalFilter(Expression predicates) {
-        super(OperatorType.PHYSICAL_FILTER);
-        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
-    }
-
-    public Expression getPredicates() {
-        return predicates;
-    }
-
-    @Override
-    public String toString() {
-        return "Filter (" + predicates + ")";
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return ImmutableList.of(predicates);
-    }
-
-    @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalFilter((PhysicalUnaryPlan<PhysicalFilter, Plan>) plan, context);
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHashJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHashJoin.java
deleted file mode 100644
index bf55f8a912..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHashJoin.java
+++ /dev/null
@@ -1,82 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * Physical hash join plan operator.
- */
-public class PhysicalHashJoin extends PhysicalBinaryOperator {
-
-    private final JoinType joinType;
-
-    private final Optional<Expression> condition;
-
-    /**
-     * Constructor of PhysicalHashJoinNode.
-     *
-     * @param joinType Which join type, left semi join, inner join...
-     * @param predicate join condition.
-     */
-    public PhysicalHashJoin(JoinType joinType, Optional<Expression> predicate) {
-        super(OperatorType.PHYSICAL_HASH_JOIN);
-        this.joinType = Objects.requireNonNull(joinType, "joinType can not be null");
-        this.condition = Objects.requireNonNull(predicate, "predicate can not be null");
-    }
-
-    public JoinType getJoinType() {
-        return joinType;
-    }
-
-    public Optional<Expression> getCondition() {
-        return condition;
-    }
-
-    @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalHashJoin((PhysicalBinaryPlan<PhysicalHashJoin, Plan, Plan>) plan, context);
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return condition.<List<Expression>>map(ImmutableList::of).orElseGet(ImmutableList::of);
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        sb.append("PhysicalHashJoin ([").append(joinType).append("]");
-        if (condition.isPresent()) {
-            sb.append(", [").append(condition.get()).append("]");
-        }
-        sb.append(")");
-        return sb.toString();
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHeapSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHeapSort.java
deleted file mode 100644
index 4faba9dc59..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalHeapSort.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.properties.OrderKey;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-import com.google.common.collect.ImmutableList;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Physical sort plan operator.
- */
-public class PhysicalHeapSort extends PhysicalUnaryOperator {
-    // Default offset is 0.
-    private final int offset;
-
-    private final List<OrderKey> orderKeys;
-
-    /**
-     * Constructor of PhysicalHashJoinNode.
-     */
-    public PhysicalHeapSort(List<OrderKey> orderKeys, long limit, int offset) {
-        super(OperatorType.PHYSICAL_SORT, limit);
-        this.offset = offset;
-        this.orderKeys = orderKeys;
-    }
-
-    public int getOffset() {
-        return offset;
-    }
-
-    public List<OrderKey> getOrderKeys() {
-        return orderKeys;
-    }
-
-    public boolean hasLimit() {
-        return limit > -1;
-    }
-
-    @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalHeapSort((PhysicalUnaryPlan<PhysicalHeapSort, Plan>) plan, context);
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return ImmutableList.<Expression>builder()
-                .addAll(orderKeys.stream().map(o -> o.getExpr()).collect(Collectors.toList())).build();
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java
deleted file mode 100644
index 3f0777e6db..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOperator.java
+++ /dev/null
@@ -1,26 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.plans.PlanOperator;
-
-/**
- * interface for all concrete physical operator.
- */
-public interface PhysicalOperator extends PlanOperator {
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalProject.java
deleted file mode 100644
index ff4ab2c865..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalProject.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.trees.expressions.Expression;
-import org.apache.doris.nereids.trees.expressions.NamedExpression;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.List;
-import java.util.Objects;
-
-/**
- * Physical project plan operator.
- */
-public class PhysicalProject extends PhysicalUnaryOperator {
-
-    private final List<NamedExpression> projects;
-
-    public PhysicalProject(List<NamedExpression> projects) {
-        super(OperatorType.PHYSICAL_PROJECT);
-        this.projects = Objects.requireNonNull(projects, "projects can not be null");
-    }
-
-    public List<NamedExpression> getProjects() {
-        return projects;
-    }
-
-    @Override
-    public String toString() {
-        return "Project (" + StringUtils.join(projects, ", ") + ")";
-    }
-
-    @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalProject(((PhysicalUnaryPlan<PhysicalProject, Plan>) plan), context);
-    }
-
-    @Override
-    public List<Expression> getExpressions() {
-        return (List) projects;
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java
deleted file mode 100644
index 345de2e1ea..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalUnaryOperator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.operators.plans.physical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.UnaryPlanOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-import java.util.Optional;
-
-/**
- * Abstract class for all physical operator that have one input.
- */
-public abstract class PhysicalUnaryOperator extends AbstractOperator
-        implements PhysicalOperator, UnaryPlanOperator {
-
-    public PhysicalUnaryOperator(OperatorType type) {
-        super(type);
-    }
-
-    public PhysicalUnaryOperator(OperatorType type, long limit) {
-        super(type, limit);
-    }
-
-    @Override
-    public PhysicalUnaryPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new PhysicalUnaryPlan(this, Optional.of(groupExpression),
-            logicalProperties, new GroupPlan(groupExpression.child(0))
-        );
-    }
-}
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 18b880d94c..6c8f4a0151 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
@@ -60,12 +60,6 @@ import org.apache.doris.nereids.analyzer.UnboundFunction;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
-import org.apache.doris.nereids.operators.plans.logical.LogicalSort;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.Add;
 import org.apache.doris.nereids.trees.expressions.Alias;
@@ -88,10 +82,13 @@ import org.apache.doris.nereids.trees.expressions.NullSafeEqual;
 import org.apache.doris.nereids.trees.expressions.Or;
 import org.apache.doris.nereids.trees.expressions.Regexp;
 import org.apache.doris.nereids.trees.expressions.Subtract;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+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.LogicalPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -174,9 +171,8 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
     @Override
     public LogicalPlan visitTableName(TableNameContext ctx) {
         List<String> tableId = visitMultipartIdentifier(ctx.multipartIdentifier());
-        UnboundRelation relation = new UnboundRelation(tableId);
         // TODO: sample and time travel, alias, sub query
-        return new LogicalLeafPlan(relation);
+        return new UnboundRelation(tableId);
     }
 
     /**
@@ -441,12 +437,9 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
             // build left deep join tree
             for (RelationContext relation : ctx.relation()) {
                 LogicalPlan right = plan(relation.relationPrimary());
-                if (left == null) {
-                    left = right;
-                } else {
-                    LogicalJoin logicalJoin = new LogicalJoin(JoinType.INNER_JOIN, Optional.empty());
-                    left = new LogicalBinaryPlan(logicalJoin, left, right);
-                }
+                left = left == null
+                        ? right
+                        : new LogicalJoin(JoinType.INNER_JOIN, Optional.empty(), left, right);
                 left = withJoinRelations(left, relation);
             }
             // TODO: pivot and lateral view
@@ -525,7 +518,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
 
     private LogicalPlan withQueryOrganization(LogicalPlan children, QueryOrganizationContext ctx) {
         List<OrderKey> orderKeys = visitQueryOrganization(ctx);
-        return orderKeys.isEmpty() ? children : new LogicalUnaryPlan(new LogicalSort(orderKeys), children);
+        return orderKeys.isEmpty() ? children : new LogicalSort(orderKeys, children);
     }
 
     /**
@@ -596,10 +589,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
                 condition = getExpression(joinCriteria.booleanExpression());
             }
 
-            last = new LogicalBinaryPlan(
-                    new LogicalJoin(joinType, Optional.ofNullable(condition)),
-                    last, plan(join.relationPrimary())
-            );
+            last = new LogicalJoin(joinType, Optional.ofNullable(condition), last, plan(join.relationPrimary()));
         }
         return last;
     }
@@ -612,14 +602,14 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
                 return input;
             } else {
                 List<NamedExpression> projects = getNamedExpressions(selectCtx.namedExpressionSeq());
-                return new LogicalUnaryPlan(new LogicalProject(projects), input);
+                return new LogicalProject(projects, input);
             }
         });
     }
 
     private LogicalPlan withFilter(LogicalPlan input, Optional<WhereClauseContext> whereCtx) {
         return input.optionalMap(whereCtx, () ->
-            new LogicalUnaryPlan(new LogicalFilter(getExpression((whereCtx.get().booleanExpression()))), input)
+            new LogicalFilter(getExpression((whereCtx.get().booleanExpression())), input)
         );
     }
 
@@ -628,7 +618,7 @@ public class LogicalPlanBuilder extends DorisParserBaseVisitor<Object> {
         return input.optionalMap(aggCtx, () -> {
             List<Expression> groupByExpressions = visit(aggCtx.get().groupByItem().expression(), Expression.class);
             List<NamedExpression> namedExpressions = getNamedExpressions(selectCtx.namedExpressionSeq());
-            return new LogicalUnaryPlan(new LogicalAggregate(groupByExpressions, namedExpressions), input);
+            return new LogicalAggregate(groupByExpressions, namedExpressions, input);
         });
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
index 33857257cc..4cb10e0a69 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/GroupExpressionMatching.java
@@ -62,7 +62,7 @@ public class GroupExpressionMatching implements Iterable<Plan> {
          * @param groupExpression group expression to be matched
          */
         public GroupExpressionIterator(Pattern<Plan, Plan> pattern, GroupExpression groupExpression) {
-            if (!pattern.matchOperator(groupExpression.getOperator())) {
+            if (!pattern.matchRoot(groupExpression.getPlan())) {
                 return;
             }
 
@@ -86,8 +86,8 @@ public class GroupExpressionMatching implements Iterable<Plan> {
                 return;
             }
 
-            // toTreeNode will wrap operator to plan, and set GroupPlan as children placeholder
-            Plan root = groupExpression.getOperator().toTreeNode(groupExpression);
+            // getPlan return the plan with GroupPlan as children
+            Plan root = groupExpression.getPlan();
             // pattern.arity() == 0 equals to root.arity() == 0
             if (pattern.arity() == 0) {
                 if (pattern.matchPredicates(root)) {
@@ -152,8 +152,8 @@ public class GroupExpressionMatching implements Iterable<Plan> {
                 // withChildren will erase groupExpression, so we must
                 // withGroupExpression too.
                 Plan rootWithChildren = root.withChildren(children)
-                        .withGroupExpression(root.getGroupExpression())
-                        .withLogicalProperties(Optional.of(logicalProperties));
+                        .withLogicalProperties(Optional.of(logicalProperties))
+                        .withGroupExpression(Optional.of(groupExpression));
                 if (rootPattern.matchPredicates(rootWithChildren)) {
                     results.add(rootWithChildren);
                 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
index 73899d69e4..fd1348898e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Pattern.java
@@ -18,11 +18,10 @@
 package org.apache.doris.nereids.pattern;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.trees.AbstractTreeNode;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import com.google.common.collect.ImmutableList;
 
@@ -44,18 +43,18 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
 
     protected final List<Predicate<TYPE>> predicates;
     protected final PatternType patternType;
-    protected final OperatorType operatorType;
+    protected final PlanType planType;
 
-    public Pattern(OperatorType operatorType, Pattern... children) {
-        this(PatternType.NORMAL, operatorType, children);
+    public Pattern(PlanType planType, Pattern... children) {
+        this(PatternType.NORMAL, planType, children);
     }
 
-    public Pattern(OperatorType operatorType, List<Predicate<TYPE>> predicates, Pattern... children) {
-        this(PatternType.NORMAL, operatorType, predicates, children);
+    public Pattern(PlanType planType, List<Predicate<TYPE>> predicates, Pattern... children) {
+        this(PatternType.NORMAL, planType, predicates, children);
     }
 
     private Pattern(PatternType patternType, Pattern... children) {
-        this(patternType, OperatorType.UNKNOWN, children);
+        this(patternType, PlanType.UNKNOWN, children);
     }
 
     /**
@@ -64,10 +63,10 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
      * @param patternType pattern type to matching
      * @param children sub pattern
      */
-    private Pattern(PatternType patternType, OperatorType operatorType, Pattern... children) {
-        super(NodeType.PATTERN, children);
+    private Pattern(PatternType patternType, PlanType planType, Pattern... children) {
+        super(children);
         this.patternType = patternType;
-        this.operatorType = operatorType;
+        this.planType = planType;
         this.predicates = ImmutableList.of();
     }
 
@@ -75,15 +74,15 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
      * Constructor for Pattern.
      *
      * @param patternType pattern type to matching
-     * @param operatorType operator type to matching
+     * @param planType plan type to matching
      * @param predicates custom matching predicate
      * @param children sub pattern
      */
-    private Pattern(PatternType patternType, OperatorType operatorType,
+    private Pattern(PatternType patternType, PlanType planType,
                    List<Predicate<TYPE>> predicates, Pattern... children) {
-        super(NodeType.PATTERN, children);
+        super(children);
         this.patternType = patternType;
-        this.operatorType = operatorType;
+        this.planType = planType;
         this.predicates = ImmutableList.copyOf(predicates);
 
         for (int i = 0; i + 1 < children.length; ++i) {
@@ -96,12 +95,12 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
     }
 
     /**
-     * get current type in Operator.
+     * get current type in Plan.
      *
-     * @return operator type in pattern
+     * @return plan type in pattern
      */
-    public OperatorType getOperatorType() {
-        return operatorType;
+    public PlanType getPlanType() {
+        return planType;
     }
 
     /**
@@ -139,13 +138,13 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
     }
 
     /**
-     * Return ture if current Pattern match Operator in params.
+     * Return ture if current Pattern match Plan in params.
      *
-     * @param operator wait to match
-     * @return ture if current Pattern match Operator in params
+     * @param plan wait to match
+     * @return ture if current Pattern match Plan in params
      */
-    public boolean matchOperator(Operator operator) {
-        if (operator == null) {
+    public boolean matchRoot(Plan plan) {
+        if (plan == null) {
             return false;
         }
         switch (patternType) {
@@ -155,7 +154,7 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
             case MULTI_GROUP:
                 return true;
             default:
-                return operatorType == operator.getType();
+                return planType == plan.getType();
         }
     }
 
@@ -175,7 +174,7 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
     }
 
     public Pattern<TYPE, NODE_TYPE> withPredicates(List<Predicate<TYPE>> predicates) {
-        return new Pattern(patternType, operatorType, predicates, children.toArray(new Pattern[0]));
+        return new Pattern(patternType, planType, predicates, children.toArray(new Pattern[0]));
     }
 
     @Override
@@ -202,11 +201,11 @@ public class Pattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE_TYP
         Pattern<?, ?> pattern = (Pattern<?, ?>) o;
         return predicates.equals(pattern.predicates)
                 && patternType == pattern.patternType
-                && operatorType == pattern.operatorType;
+                && planType == pattern.planType;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(predicates, patternType, operatorType);
+        return Objects.hash(predicates, patternType, planType);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/PatternType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/PatternType.java
index b2f92baea0..a16617e199 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/PatternType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/PatternType.java
@@ -20,10 +20,9 @@ package org.apache.doris.nereids.pattern;
 /**
  * Types for all Pattern type.
  * <p>
- * There are four types of Pattern for pattern matching:
- * 1. NORMAL:      normal pattern matching, e.g. match by operatorId or class type.
- * 2. ANY:         match any operator, return a plan when matched.
- * 3. MULTI:       match multiple children operators, that we don't know how many children exist.
+ * 1. NORMAL:      normal pattern matching, e.g. match by planId or class type.
+ * 2. ANY:         match any plan, return a plan when matched.
+ * 3. MULTI:       match multiple children plans, that we don't know how many children exist.
  *                 only use as the last child pattern, and can not use as the top pattern.
  *                 return some children plan with real plan type when matched.
  * 4. GROUP:       match a group plan, only use in a pattern's children, and can not use as the top pattern.
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Patterns.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Patterns.java
index f5c659c814..1c0fab37e8 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Patterns.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/Patterns.java
@@ -17,16 +17,6 @@
 
 package org.apache.doris.nereids.pattern;
 
-import org.apache.doris.nereids.operators.plans.BinaryPlanOperator;
-import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
-import org.apache.doris.nereids.operators.plans.UnaryPlanOperator;
-import org.apache.doris.nereids.operators.plans.logical.LogicalBinaryOperator;
-import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
-import org.apache.doris.nereids.operators.plans.logical.LogicalUnaryOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalBinaryOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalLeafOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalScan;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalUnaryOperator;
 import org.apache.doris.nereids.rules.RulePromise;
 import org.apache.doris.nereids.trees.TreeNode;
 import org.apache.doris.nereids.trees.plans.BinaryPlan;
@@ -34,12 +24,14 @@ import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.UnaryPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalBinary;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnary;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalBinary;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalLeaf;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalUnary;
 
 /**
  * An interface provided some PatternDescriptor.
@@ -68,20 +60,20 @@ public interface Patterns {
         return new PatternDescriptor<>(Pattern.MULTI_GROUP, defaultPromise());
     }
 
-    /* abstract plan operator patterns */
+    /* abstract plan patterns */
 
     /**
      * create a leafPlan pattern.
      */
     default PatternDescriptor<LeafPlan, Plan> leafPlan() {
-        return new PatternDescriptor(new TypePattern(LeafPlanOperator.class), defaultPromise());
+        return new PatternDescriptor(new TypePattern(LeafPlan.class), defaultPromise());
     }
 
     /**
      * create a unaryPlan pattern.
      */
-    default PatternDescriptor<UnaryPlan<Plan>, Plan> unaryPlan() {
-        return new PatternDescriptor(new TypePattern(UnaryPlanOperator.class, Pattern.GROUP), defaultPromise());
+    default PatternDescriptor<UnaryPlan<GroupPlan>, Plan> unaryPlan() {
+        return new PatternDescriptor(new TypePattern(UnaryPlan.class, Pattern.GROUP), defaultPromise());
     }
 
     /**
@@ -89,15 +81,15 @@ public interface Patterns {
      */
     default <C extends Plan> PatternDescriptor<UnaryPlan<C>, Plan>
             unaryPlan(PatternDescriptor<C, Plan> child) {
-        return new PatternDescriptor(new TypePattern(UnaryPlanOperator.class, child.pattern), defaultPromise());
+        return new PatternDescriptor(new TypePattern(UnaryPlan.class, child.pattern), defaultPromise());
     }
 
     /**
      * create a binaryPlan pattern.
      */
-    default PatternDescriptor<BinaryPlan<Plan, Plan>, Plan> binaryPlan() {
+    default PatternDescriptor<BinaryPlan<GroupPlan, GroupPlan>, Plan> binaryPlan() {
         return new PatternDescriptor(
-                new TypePattern(BinaryPlanOperator.class, Pattern.GROUP, Pattern.GROUP),
+                new TypePattern(BinaryPlan.class, Pattern.GROUP, Pattern.GROUP),
                 defaultPromise()
         );
     }
@@ -110,41 +102,41 @@ public interface Patterns {
                 PatternDescriptor<LEFT_CHILD_TYPE, Plan> leftChild,
                 PatternDescriptor<RIGHT_CHILD_TYPE, Plan> rightChild) {
         return new PatternDescriptor(
-                new TypePattern(BinaryPlanOperator.class, leftChild.pattern, rightChild.pattern),
+                new TypePattern(BinaryPlan.class, leftChild.pattern, rightChild.pattern),
                 defaultPromise()
         );
     }
 
-    /* abstract logical operator patterns */
+    /* abstract logical plan patterns */
 
     /**
      * create a logicalLeaf pattern.
      */
-    default PatternDescriptor<LogicalLeafPlan<LogicalLeafOperator>, Plan> logicalLeaf() {
-        return new PatternDescriptor(new TypePattern(LogicalLeafPlan.class), defaultPromise());
+    default PatternDescriptor<LogicalLeaf, Plan> logicalLeaf() {
+        return new PatternDescriptor(new TypePattern(LogicalLeaf.class), defaultPromise());
     }
 
     /**
      * create a logicalUnary pattern.
      */
-    default PatternDescriptor<LogicalUnaryPlan<LogicalUnaryOperator, Plan>, Plan> logicalUnary() {
-        return new PatternDescriptor(new TypePattern(LogicalUnaryPlan.class, Pattern.GROUP), defaultPromise());
+    default PatternDescriptor<LogicalUnary<GroupPlan>, Plan> logicalUnary() {
+        return new PatternDescriptor(new TypePattern(LogicalUnary.class, Pattern.GROUP), defaultPromise());
     }
 
     /**
      * create a logicalUnary pattern.
      */
-    default <C extends Plan> PatternDescriptor<LogicalUnaryPlan<LogicalUnaryOperator, C>, Plan>
+    default <C extends Plan> PatternDescriptor<LogicalUnary<C>, Plan>
             logicalUnary(PatternDescriptor<C, Plan> child) {
-        return new PatternDescriptor(new TypePattern(LogicalUnaryPlan.class, child.pattern), defaultPromise());
+        return new PatternDescriptor(new TypePattern(LogicalUnary.class, child.pattern), defaultPromise());
     }
 
     /**
      * create a logicalBinary pattern.
      */
-    default PatternDescriptor<LogicalBinaryPlan<LogicalBinaryOperator, Plan, Plan>, Plan> logicalBinary() {
+    default PatternDescriptor<LogicalBinary<GroupPlan, GroupPlan>, Plan> logicalBinary() {
         return new PatternDescriptor(
-                new TypePattern(LogicalBinaryPlan.class, Pattern.GROUP, Pattern.GROUP),
+                new TypePattern(LogicalBinary.class, Pattern.GROUP, Pattern.GROUP),
                 defaultPromise()
         );
     }
@@ -153,46 +145,53 @@ public interface Patterns {
      * create a logicalBinary pattern.
      */
     default <LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
-            PatternDescriptor<LogicalBinaryPlan<LogicalBinaryOperator, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>, Plan>
+            PatternDescriptor<LogicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>, Plan>
             logicalBinary(
                 PatternDescriptor<LEFT_CHILD_TYPE, Plan> leftChild,
                 PatternDescriptor<RIGHT_CHILD_TYPE, Plan> rightChild) {
         return new PatternDescriptor(
-                new TypePattern(LogicalBinaryPlan.class, leftChild.pattern, rightChild.pattern),
+                new TypePattern(LogicalBinary.class, leftChild.pattern, rightChild.pattern),
                 defaultPromise()
         );
     }
 
-    /* abstract physical operator patterns */
+    /**
+     * create a logicalRelation pattern.
+     */
+    default PatternDescriptor<LogicalRelation, Plan> logicalRelation() {
+        return new PatternDescriptor(new TypePattern(LogicalRelation.class), defaultPromise());
+    }
+
+    /* abstract physical plan patterns */
 
     /**
      * create a physicalLeaf pattern.
      */
-    default PatternDescriptor<PhysicalLeafPlan<PhysicalLeafOperator>, Plan> physicalLeaf() {
-        return new PatternDescriptor(new TypePattern(PhysicalLeafPlan.class), defaultPromise());
+    default PatternDescriptor<PhysicalLeaf, Plan> physicalLeaf() {
+        return new PatternDescriptor(new TypePattern(PhysicalLeaf.class), defaultPromise());
     }
 
     /**
      * create a physicalUnary pattern.
      */
-    default PatternDescriptor<PhysicalUnaryPlan<PhysicalUnaryOperator, Plan>, Plan> physicalUnary() {
-        return new PatternDescriptor(new TypePattern(PhysicalUnaryPlan.class, Pattern.GROUP), defaultPromise());
+    default PatternDescriptor<PhysicalUnary<GroupPlan>, Plan> physicalUnary() {
+        return new PatternDescriptor(new TypePattern(PhysicalUnary.class, Pattern.GROUP), defaultPromise());
     }
 
     /**
      * create a physicalUnary pattern.
      */
-    default <C extends Plan> PatternDescriptor<PhysicalUnaryPlan<PhysicalUnaryOperator, C>, Plan>
+    default <C extends Plan> PatternDescriptor<PhysicalUnary<C>, Plan>
             physicalUnary(PatternDescriptor<C, Plan> child) {
-        return new PatternDescriptor(new TypePattern(PhysicalUnaryPlan.class, child.pattern), defaultPromise());
+        return new PatternDescriptor(new TypePattern(PhysicalUnary.class, child.pattern), defaultPromise());
     }
 
     /**
      * create a physicalBinary pattern.
      */
-    default PatternDescriptor<PhysicalBinaryPlan<PhysicalBinaryOperator, Plan, Plan>, Plan> physicalBinary() {
+    default PatternDescriptor<PhysicalBinary<GroupPlan, GroupPlan>, Plan> physicalBinary() {
         return new PatternDescriptor(
-                new TypePattern(PhysicalBinaryPlan.class, Pattern.GROUP, Pattern.GROUP),
+                new TypePattern(PhysicalBinary.class, Pattern.GROUP, Pattern.GROUP),
                 defaultPromise()
         );
     }
@@ -201,20 +200,20 @@ public interface Patterns {
      * create a physicalBinary pattern.
      */
     default <LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
-            PatternDescriptor<PhysicalBinaryPlan<PhysicalBinaryOperator, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>, Plan>
+            PatternDescriptor<PhysicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>, Plan>
             physicalBinary(
                 PatternDescriptor<LEFT_CHILD_TYPE, Plan> leftChild,
                 PatternDescriptor<RIGHT_CHILD_TYPE, Plan> rightChild) {
         return new PatternDescriptor(
-                new TypePattern(PhysicalBinaryPlan.class, leftChild.pattern, rightChild.pattern),
+                new TypePattern(PhysicalBinary.class, leftChild.pattern, rightChild.pattern),
                 defaultPromise()
         );
     }
 
     /**
-     * create a physicalScan pattern.
+     * create a physicalRelation pattern.
      */
-    default PatternDescriptor<PhysicalLeafPlan<PhysicalScan>, Plan> physicalScan() {
-        return new PatternDescriptor(new TypePattern(PhysicalScan.class), defaultPromise());
+    default PatternDescriptor<PhysicalRelation, Plan> physicalRelation() {
+        return new PatternDescriptor(new TypePattern(PhysicalRelation.class), defaultPromise());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/TypePattern.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/TypePattern.java
index 81202386b2..d2cd783836 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/TypePattern.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/TypePattern.java
@@ -17,9 +17,9 @@
 
 package org.apache.doris.nereids.pattern;
 
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.OperatorType;
 import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.List;
 import java.util.Objects;
@@ -31,12 +31,12 @@ public class TypePattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE
     protected final Class<TYPE> type;
 
     public TypePattern(Class clazz, Pattern... children) {
-        super(OperatorType.UNKNOWN, children);
+        super(PlanType.UNKNOWN, children);
         this.type = Objects.requireNonNull(clazz, "class can not be null");
     }
 
     public TypePattern(Class<TYPE> clazz, List<Predicate<TYPE>> predicates, Pattern... children) {
-        super(OperatorType.UNKNOWN, predicates, children);
+        super(PlanType.UNKNOWN, predicates, children);
         this.type = Objects.requireNonNull(clazz, "class can not be null");
     }
 
@@ -46,7 +46,7 @@ public class TypePattern<TYPE extends NODE_TYPE, NODE_TYPE extends TreeNode<NODE
     }
 
     @Override
-    public boolean matchOperator(Operator operator) {
-        return type.isInstance(operator);
+    public boolean matchRoot(Plan plan) {
+        return type.isInstance(plan);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalBinaryPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalBinaryPatternGenerator.java
index 98c62f0abc..7fd1fac247 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalBinaryPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalBinaryPatternGenerator.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
 import java.util.Set;
 import java.util.TreeSet;
 
-/** used to generate pattern for LogicalBinaryOperator. */
+/** used to generate pattern for LogicalBinary. */
 public class LogicalBinaryPatternGenerator extends PatternGenerator {
 
     public LogicalBinaryPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -32,12 +32,12 @@ public class LogicalBinaryPatternGenerator extends PatternGenerator {
 
     @Override
     public String genericType() {
-        return "<LogicalBinaryPlan<" + opType.name + ", GroupPlan, GroupPlan>, Plan>";
+        return "<" + opType.name + "<GroupPlan, GroupPlan>, Plan>";
     }
 
     @Override
     public String genericTypeWithChildren() {
-        return "<LogicalBinaryPlan<" + opType.name + ", C1, C2>, Plan>";
+        return "<" + opType.name + "<C1, C2>, Plan>";
     }
 
     @Override
@@ -46,7 +46,6 @@ public class LogicalBinaryPatternGenerator extends PatternGenerator {
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.GroupPlan");
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalLeafPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalLeafPatternGenerator.java
index f84acdb403..3717c99be9 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalLeafPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalLeafPatternGenerator.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
 import java.util.Set;
 import java.util.TreeSet;
 
-/** used to generate pattern for LogicalLeafOperator. */
+/** used to generate pattern for LogicalLeaf. */
 public class LogicalLeafPatternGenerator extends PatternGenerator {
 
     public LogicalLeafPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -30,27 +30,9 @@ public class LogicalLeafPatternGenerator extends PatternGenerator {
         super(analyzer, opType, parentClass);
     }
 
-    @Override
-    public String generate() {
-        String opClassName = opType.name;
-        String methodName = getPatternMethodName();
-
-        String patternParam = "<LogicalLeafPlan<" + opClassName + ">, Plan>";
-
-        generateTypePattern(methodName, opClassName, patternParam, "", false);
-
-        for (EnumFieldPatternInfo info : enumFieldPatternInfos) {
-            String predicate = ".when(p -> p.operator." + info.enumInstanceGetter + "() == "
-                    + info.enumType + "." + info.enumInstance + ")";
-            generateTypePattern(info.patternName, opClassName, patternParam, predicate, false);
-        }
-
-        return generatePatterns();
-    }
-
     @Override
     public String genericType() {
-        return  "<LogicalLeafPlan<" + opType.name + ">, Plan>";
+        return  "<" + opType.name + ", Plan>";
     }
 
     @Override
@@ -63,7 +45,6 @@ public class LogicalLeafPatternGenerator extends PatternGenerator {
         Set<String> imports = new TreeSet<>();
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalUnaryPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalUnaryPatternGenerator.java
index bf6901d8bf..e656f3958f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalUnaryPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/LogicalUnaryPatternGenerator.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
 import java.util.Set;
 import java.util.TreeSet;
 
-/** used to generate pattern for LogicalUnaryOperator. */
+/** used to generate pattern for LogicalUnary. */
 public class LogicalUnaryPatternGenerator extends PatternGenerator {
 
     public LogicalUnaryPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -32,12 +32,12 @@ public class LogicalUnaryPatternGenerator extends PatternGenerator {
 
     @Override
     public String genericType() {
-        return "<LogicalUnaryPlan<" + opType.name + ", GroupPlan>, Plan>";
+        return "<" + opType.name + "<GroupPlan>, Plan>";
     }
 
     @Override
     public String genericTypeWithChildren() {
-        return "<LogicalUnaryPlan<" + opType.name + ", C1>, Plan>";
+        return "<" + opType.name + "<C1>, Plan>";
     }
 
     @Override
@@ -46,7 +46,6 @@ public class LogicalUnaryPatternGenerator extends PatternGenerator {
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.GroupPlan");
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternDescribableProcessor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternDescribableProcessor.java
index 295d3da2af..504f3ed1ae 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternDescribableProcessor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternDescribableProcessor.java
@@ -60,12 +60,12 @@ import javax.tools.StandardLocation;
 @SupportedSourceVersion(SourceVersion.RELEASE_8)
 @SupportedAnnotationTypes("org.apache.doris.nereids.pattern.generator.PatternDescribable")
 public class PatternDescribableProcessor extends AbstractProcessor {
-    private List<File> operatorPaths;
+    private List<File> planPaths;
 
     @Override
     public synchronized void init(ProcessingEnvironment processingEnv) {
         super.init(processingEnv);
-        this.operatorPaths = Arrays.stream(processingEnv.getOptions().get("operatorPath").split(","))
+        this.planPaths = Arrays.stream(processingEnv.getOptions().get("planPath").split(","))
                 .map(path -> path.trim())
                 .filter(path -> !path.isEmpty())
                 .collect(Collectors.toSet())
@@ -80,9 +80,9 @@ public class PatternDescribableProcessor extends AbstractProcessor {
             return false;
         }
         try {
-            List<File> operatorFiles = findJavaFiles(operatorPaths);
+            List<File> planFiles = findJavaFiles(planPaths);
             PatternGeneratorAnalyzer patternGeneratorAnalyzer = new PatternGeneratorAnalyzer();
-            for (File file : operatorFiles) {
+            for (File file : planFiles) {
                 List<TypeDeclaration> asts = parseJavaFile(file);
                 patternGeneratorAnalyzer.addAsts(asts);
             }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGenerator.java
index 41303f5b8b..bd5c4566b4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGenerator.java
@@ -43,7 +43,7 @@ import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
-/** used to generate pattern by operator. */
+/** used to generate pattern by plan. */
 public abstract class PatternGenerator {
     protected final PatternGeneratorAnalyzer analyzer;
     protected final ClassDeclaration opType;
@@ -199,20 +199,20 @@ public abstract class PatternGenerator {
         return parts;
     }
 
-    /** create generator by operator's type. */
+    /** create generator by plan's type. */
     public static Optional<PatternGenerator> create(PatternGeneratorAnalyzer analyzer,
             ClassDeclaration opType, Set<String> parentClass) {
-        if (parentClass.contains("org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator")) {
+        if (parentClass.contains("org.apache.doris.nereids.trees.plans.logical.LogicalLeaf")) {
             return Optional.of(new LogicalLeafPatternGenerator(analyzer, opType, parentClass));
-        } else if (parentClass.contains("org.apache.doris.nereids.operators.plans.logical.LogicalUnaryOperator")) {
+        } else if (parentClass.contains("org.apache.doris.nereids.trees.plans.logical.LogicalUnary")) {
             return Optional.of(new LogicalUnaryPatternGenerator(analyzer, opType, parentClass));
-        } else if (parentClass.contains("org.apache.doris.nereids.operators.plans.logical.LogicalBinaryOperator")) {
+        } else if (parentClass.contains("org.apache.doris.nereids.trees.plans.logical.LogicalBinary")) {
             return Optional.of(new LogicalBinaryPatternGenerator(analyzer, opType, parentClass));
-        } else if (parentClass.contains("org.apache.doris.nereids.operators.plans.physical.PhysicalLeafOperator")) {
+        } else if (parentClass.contains("org.apache.doris.nereids.trees.plans.physical.PhysicalLeaf")) {
             return Optional.of(new PhysicalLeafPatternGenerator(analyzer, opType, parentClass));
-        } else if (parentClass.contains("org.apache.doris.nereids.operators.plans.physical.PhysicalUnaryOperator")) {
+        } else if (parentClass.contains("org.apache.doris.nereids.trees.plans.physical.PhysicalUnary")) {
             return Optional.of(new PhysicalUnaryPatternGenerator(analyzer, opType, parentClass));
-        } else if (parentClass.contains("org.apache.doris.nereids.operators.plans.physical.PhysicalBinaryOperator")) {
+        } else if (parentClass.contains("org.apache.doris.nereids.trees.plans.physical.PhysicalBinary")) {
             return Optional.of(new PhysicalBinaryPatternGenerator(analyzer, opType, parentClass));
         } else {
             return Optional.empty();
@@ -243,7 +243,7 @@ public abstract class PatternGenerator {
         }
 
         for (EnumFieldPatternInfo info : enumFieldPatternInfos) {
-            String predicate = ".when(p -> p.operator." + info.enumInstanceGetter + "() == "
+            String predicate = ".when(p -> p." + info.enumInstanceGetter + "() == "
                     + info.enumType + "." + info.enumInstance + ")";
             generateTypePattern(info.patternName, opClassName, genericType(), predicate, false);
             if (childrenNum() > 0) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGeneratorAnalyzer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGeneratorAnalyzer.java
index ebc8b7a572..5b544c0e7a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGeneratorAnalyzer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PatternGeneratorAnalyzer.java
@@ -41,7 +41,7 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
- * used to analyze operator class extends hierarchy and then generated pattern builder methods.
+ * used to analyze plan class extends hierarchy and then generated pattern builder methods.
  */
 public class PatternGeneratorAnalyzer {
     private final Map<String, TypeDeclaration> name2Ast = new LinkedHashMap<>();
@@ -74,13 +74,13 @@ public class PatternGeneratorAnalyzer {
     }
 
     private String doGenerate() {
-        Map<ClassDeclaration, Set<String>> planOpClassMap = parentClassMap.entrySet().stream()
-                .filter(kv -> kv.getValue().contains("org.apache.doris.nereids.operators.plans.PlanOperator"))
+        Map<ClassDeclaration, Set<String>> planClassMap = parentClassMap.entrySet().stream()
+                .filter(kv -> kv.getValue().contains("org.apache.doris.nereids.trees.plans.Plan"))
                 .filter(kv -> !Modifier.isAbstract(kv.getKey().modifiers.mod)
                         && kv.getKey() instanceof ClassDeclaration)
                 .collect(Collectors.toMap(kv -> (ClassDeclaration) kv.getKey(), kv -> kv.getValue()));
 
-        List<PatternGenerator> generators = planOpClassMap.entrySet()
+        List<PatternGenerator> generators = planClassMap.entrySet()
                 .stream()
                 .map(kv -> PatternGenerator.create(this, kv.getKey(), kv.getValue()))
                 .filter(generator -> generator.isPresent())
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalBinaryPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalBinaryPatternGenerator.java
index 4c96ff4deb..0ebd799630 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalBinaryPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalBinaryPatternGenerator.java
@@ -23,7 +23,7 @@ import java.util.Set;
 import java.util.TreeSet;
 
 
-/** used to generate pattern for PhysicalBinaryOperator. */
+/** used to generate pattern for PhysicalBinary. */
 public class PhysicalBinaryPatternGenerator extends PatternGenerator {
 
     public PhysicalBinaryPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -33,12 +33,12 @@ public class PhysicalBinaryPatternGenerator extends PatternGenerator {
 
     @Override
     public String genericType() {
-        return "<PhysicalBinaryPlan<" + opType.name + ", GroupPlan, GroupPlan>, Plan>";
+        return "<" + opType.name + "<GroupPlan, GroupPlan>, Plan>";
     }
 
     @Override
     public String genericTypeWithChildren() {
-        return "<PhysicalBinaryPlan<" + opType.name + ", C1, C2>, Plan>";
+        return "<" + opType.name + "<C1, C2>, Plan>";
     }
 
     @Override
@@ -47,7 +47,6 @@ public class PhysicalBinaryPatternGenerator extends PatternGenerator {
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.GroupPlan");
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalLeafPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalLeafPatternGenerator.java
index 9ba34c3cd4..851663137f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalLeafPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalLeafPatternGenerator.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
 import java.util.Set;
 import java.util.TreeSet;
 
-/** used to generate pattern for PhysicalLeafOperator. */
+/** used to generate pattern for PhysicalLeaf. */
 public class PhysicalLeafPatternGenerator extends PatternGenerator {
 
     public PhysicalLeafPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -32,7 +32,7 @@ public class PhysicalLeafPatternGenerator extends PatternGenerator {
 
     @Override
     public String genericType() {
-        return "<PhysicalLeafPlan<" + opType.name + ">, Plan>";
+        return "<" + opType.name + ", Plan>";
     }
 
     @Override
@@ -45,7 +45,6 @@ public class PhysicalLeafPatternGenerator extends PatternGenerator {
         Set<String> imports = new TreeSet<>();
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalUnaryPatternGenerator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalUnaryPatternGenerator.java
index 46a8e95ddb..a0ba83cd81 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalUnaryPatternGenerator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/pattern/generator/PhysicalUnaryPatternGenerator.java
@@ -22,7 +22,7 @@ import org.apache.doris.nereids.pattern.generator.javaast.ClassDeclaration;
 import java.util.Set;
 import java.util.TreeSet;
 
-/** used to generate pattern for PhysicalUnaryOperator. */
+/** used to generate pattern for PhysicalUnary. */
 public class PhysicalUnaryPatternGenerator extends PatternGenerator {
 
     public PhysicalUnaryPatternGenerator(PatternGeneratorAnalyzer analyzer,
@@ -32,12 +32,12 @@ public class PhysicalUnaryPatternGenerator extends PatternGenerator {
 
     @Override
     public String genericType() {
-        return "<PhysicalUnaryPlan<" + opType.name + ", GroupPlan>, Plan>";
+        return "<" + opType.name + "<GroupPlan>, Plan>";
     }
 
     @Override
     public String genericTypeWithChildren() {
-        return "<PhysicalUnaryPlan<" + opType.name + ", C1>, Plan>";
+        return "<" + opType.name + "<C1>, Plan>";
     }
 
     @Override
@@ -46,7 +46,6 @@ public class PhysicalUnaryPatternGenerator extends PatternGenerator {
         imports.add(opType.getFullQualifiedName());
         imports.add("org.apache.doris.nereids.trees.plans.GroupPlan");
         imports.add("org.apache.doris.nereids.trees.plans.Plan");
-        imports.add("org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan");
         enumFieldPatternInfos.stream()
                 .map(info -> info.enumFullName)
                 .forEach(imports::add);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildrenOutputPropertyDeriver.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildrenOutputPropertyDeriver.java
index 6df2cfe15c..8b991fea1f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildrenOutputPropertyDeriver.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ChildrenOutputPropertyDeriver.java
@@ -19,15 +19,15 @@ package org.apache.doris.nereids.properties;
 
 import org.apache.doris.nereids.PlanContext;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.OperatorVisitor;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
 import java.util.List;
 
 /**
  * Used for property drive.
  */
-public class ChildrenOutputPropertyDeriver extends OperatorVisitor<PhysicalProperties, PlanContext> {
+public class ChildrenOutputPropertyDeriver extends PlanVisitor<PhysicalProperties, PlanContext> {
     PhysicalProperties requirements;
     List<PhysicalProperties> childrenOutputProperties;
 
@@ -45,7 +45,7 @@ public class ChildrenOutputPropertyDeriver extends OperatorVisitor<PhysicalPrope
         ChildrenOutputPropertyDeriver childrenOutputPropertyDeriver = new ChildrenOutputPropertyDeriver(requirements,
                 childrenOutputProperties);
 
-        return groupExpression.getOperator().accept(childrenOutputPropertyDeriver, new PlanContext(groupExpression));
+        return groupExpression.getPlan().accept(childrenOutputPropertyDeriver, new PlanContext(groupExpression));
     }
 
     public PhysicalProperties getRequirements() {
@@ -54,12 +54,12 @@ public class ChildrenOutputPropertyDeriver extends OperatorVisitor<PhysicalPrope
 
     //    public List<List<PhysicalProperties>> getProperties(GroupExpression groupExpression) {
     //        properties = Lists.newArrayList();
-    //        groupExpression.getOperator().accept(this, new PlanContext(groupExpression));
+    //        groupExpression.getPlan().accept(this, new PlanContext(groupExpression));
     //        return properties;
     //    }
 
     //    @Override
-    //    public Void visitOperator(Operator operator, PlanContext context) {
+    //    public Void visit(Plan plan, PlanContext context) {
     //        List<PhysicalProperties> props = Lists.newArrayList();
     //        for (int childIndex = 0; childIndex < context.getGroupExpression().arity(); ++childIndex) {
     //            props.add(new PhysicalProperties());
@@ -68,7 +68,7 @@ public class ChildrenOutputPropertyDeriver extends OperatorVisitor<PhysicalPrope
     //        return null;
     //    }
     @Override
-    public PhysicalProperties visitOperator(Operator node, PlanContext context) {
+    public PhysicalProperties visit(Plan plan, PlanContext context) {
         return new PhysicalProperties();
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/DistributionSpec.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/DistributionSpec.java
index cd0ea5a438..e849a1396f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/DistributionSpec.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/DistributionSpec.java
@@ -19,7 +19,8 @@ package org.apache.doris.nereids.properties;
 
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalDistribution;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribution;
 import org.apache.doris.planner.DataPartition;
 
 import com.google.common.collect.Lists;
@@ -58,8 +59,9 @@ public class DistributionSpec {
     }
 
     public GroupExpression addEnforcer(Group child) {
-        return new GroupExpression(new PhysicalDistribution(new DistributionSpec(dataPartition)),
-                Lists.newArrayList(child));
+        PhysicalDistribution distribution = new PhysicalDistribution(
+                new DistributionSpec(dataPartition), child.getLogicalProperties(), new GroupPlan(child));
+        return new GroupExpression(distribution, Lists.newArrayList(child));
     }
 
     // TODO
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
index 3623767554..c7fd2b66b3 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/LogicalProperties.java
@@ -47,7 +47,7 @@ public class LogicalProperties {
         return outputSupplier.get();
     }
 
-    public static LogicalProperties withOutput(List<Slot> output) {
+    public LogicalProperties withOutput(List<Slot> output) {
         return new LogicalProperties(Suppliers.ofInstance(output));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderSpec.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderSpec.java
index 1d1c3777ae..196bbf0cc6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderSpec.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/OrderSpec.java
@@ -19,7 +19,8 @@ package org.apache.doris.nereids.properties;
 
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHeapSort;
+import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHeapSort;
 
 import com.google.common.collect.Lists;
 
@@ -54,7 +55,7 @@ public class OrderSpec {
 
     public GroupExpression addEnforcer(Group child) {
         return new GroupExpression(
-                new PhysicalHeapSort(orderKeys, -1, 0),
+                new PhysicalHeapSort(orderKeys, -1, 0, child.getLogicalProperties(), new GroupPlan(child)),
                 Lists.newArrayList(child)
         );
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ParentRequiredPropertyDeriver.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ParentRequiredPropertyDeriver.java
index 681500ed69..f78fd2c601 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ParentRequiredPropertyDeriver.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/ParentRequiredPropertyDeriver.java
@@ -20,8 +20,8 @@ package org.apache.doris.nereids.properties;
 import org.apache.doris.nereids.PlanContext;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.OperatorVisitor;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
 import com.google.common.collect.Lists;
 
@@ -30,7 +30,7 @@ import java.util.List;
 /**
  * Used for parent property drive.
  */
-public class ParentRequiredPropertyDeriver extends OperatorVisitor<Void, PlanContext> {
+public class ParentRequiredPropertyDeriver extends PlanVisitor<Void, PlanContext> {
 
     PhysicalProperties requestPropertyFromParent;
     List<List<PhysicalProperties>> requiredPropertyListList;
@@ -41,12 +41,12 @@ public class ParentRequiredPropertyDeriver extends OperatorVisitor<Void, PlanCon
 
     public List<List<PhysicalProperties>> getRequiredPropertyListList(GroupExpression groupExpression) {
         requiredPropertyListList = Lists.newArrayList();
-        groupExpression.getOperator().accept(this, new PlanContext(groupExpression));
+        groupExpression.getPlan().accept(this, new PlanContext(groupExpression));
         return requiredPropertyListList;
     }
 
     @Override
-    public Void visitOperator(Operator operator, PlanContext context) {
+    public Void visit(Plan plan, PlanContext context) {
         List<PhysicalProperties> requiredPropertyList = Lists.newArrayList();
         for (int i = 0; i < context.getGroupExpression().arity(); i++) {
             requiredPropertyList.add(new PhysicalProperties());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
index ca443a402e..23153b232f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/UnboundLogicalProperties.java
@@ -25,7 +25,7 @@ import com.google.common.collect.ImmutableList;
 import java.util.List;
 
 /**
- * LogicalPlanOperator must compute and return non-null LogicalProperties without exception,
+ * LogicalPlan must compute and return non-null LogicalProperties without exception,
  * so UnboundRelation.computeLogicalProperties() return a UnboundLogicalProperties temporary.
  */
 public class UnboundLogicalProperties extends LogicalProperties {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/PlanRuleFactory.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/PlanRuleFactory.java
index eec473b5c9..5c97a3d794 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/PlanRuleFactory.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/PlanRuleFactory.java
@@ -18,10 +18,9 @@
 package org.apache.doris.nereids.rules;
 
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
 
 /**
  * interface for all plan rule factories.
  */
-public interface PlanRuleFactory extends RuleFactory<Plan>, Plans {
+public interface PlanRuleFactory extends RuleFactory<Plan> {
 }
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 753436e7e0..3fb2181da4 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
@@ -18,8 +18,6 @@
 package org.apache.doris.nereids.rules.analysis;
 
 import org.apache.doris.nereids.analyzer.UnboundFunction;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Expression;
@@ -27,6 +25,7 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.functions.Sum;
 import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
 
@@ -42,17 +41,15 @@ public class BindFunction implements AnalysisRuleFactory {
         return ImmutableList.of(
             RuleType.BINDING_PROJECT_FUNCTION.build(
                 logicalProject().then(project -> {
-                    List<NamedExpression> boundExpr = bind(project.operator.getProjects());
-                    LogicalProject op = new LogicalProject(boundExpr);
-                    return plan(op, project.child());
+                    List<NamedExpression> boundExpr = bind(project.getProjects());
+                    return new LogicalProject(boundExpr, project.child());
                 })
             ),
             RuleType.BINDING_AGGREGATE_FUNCTION.build(
                 logicalAggregate().then(agg -> {
-                    List<Expression> groupBy = bind(agg.operator.getGroupByExpressionList());
-                    List<NamedExpression> output = bind(agg.operator.getOutputExpressionList());
-                    LogicalAggregate op = agg.operator.withGroupByAndOutput(groupBy, output);
-                    return plan(op, agg.child());
+                    List<Expression> groupBy = bind(agg.getGroupByExpressionList());
+                    List<NamedExpression> output = bind(agg.getOutputExpressionList());
+                    return agg.withGroupByAndOutput(groupBy, output);
                 })
             )
         );
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
index d51791ca86..f38c404570 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java
@@ -20,11 +20,10 @@ package org.apache.doris.nereids.rules.analysis;
 import org.apache.doris.catalog.Catalog;
 import org.apache.doris.catalog.Database;
 import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.Lists;
@@ -40,23 +39,20 @@ public class BindRelation extends OneAnalysisRuleFactory {
         // fixme, just for example now
         return unboundRelation().thenApply(ctx -> {
             ConnectContext connectContext = ctx.plannerContext.getConnectContext();
-            List<String> nameParts = ctx.root.operator.getNameParts();
+            List<String> nameParts = ctx.root.getNameParts();
             switch (nameParts.size()) {
                 case 1: {
                     List<String> qualifier = Lists.newArrayList(connectContext.getDatabase(), nameParts.get(0));
                     Table table = getTable(qualifier, connectContext.getCatalog());
                     // TODO: should generate different Scan sub class according to table's type
-                    LogicalOlapScan olapScan = new LogicalOlapScan(table, qualifier);
-                    return new LogicalLeafPlan<>(olapScan);
+                    return new LogicalOlapScan(table, qualifier);
                 }
                 case 2: {
                     Table table = getTable(nameParts, connectContext.getCatalog());
-                    LogicalOlapScan olapScan = new LogicalOlapScan(table, nameParts);
-                    return new LogicalLeafPlan<>(olapScan);
+                    return new LogicalOlapScan(table, nameParts);
                 }
                 default:
-                    throw new IllegalStateException("Table name ["
-                            + ctx.root.operator.getTableName() + "] is invalid.");
+                    throw new IllegalStateException("Table name [" + ctx.root.getTableName() + "] is invalid.");
             }
         }).toRule(RuleType.BINDING_RELATION);
     }
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 18100e5cbc..e69ab79710 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
@@ -21,21 +21,20 @@ import org.apache.doris.nereids.analyzer.UnboundAlias;
 import org.apache.doris.nereids.analyzer.UnboundSlot;
 import org.apache.doris.nereids.analyzer.UnboundStar;
 import org.apache.doris.nereids.exceptions.AnalysisException;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
-import org.apache.doris.nereids.operators.plans.logical.LogicalSort;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Alias;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
 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.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -56,46 +55,40 @@ public class BindSlotReference implements AnalysisRuleFactory {
             RuleType.BINDING_PROJECT_SLOT.build(
                 logicalProject().then(project -> {
                     List<NamedExpression> boundSlots =
-                            bind(project.operator.getProjects(), project.children(), project);
-                    return plan(new LogicalProject(flatBoundStar(boundSlots)), project.child());
+                            bind(project.getProjects(), project.children(), project);
+                    return new LogicalProject(flatBoundStar(boundSlots), project.child());
                 })
             ),
             RuleType.BINDING_FILTER_SLOT.build(
                 logicalFilter().then(filter -> {
-                    Expression boundPredicates = bind(
-                            filter.operator.getPredicates(), filter.children(), filter);
-                    return plan(new LogicalFilter(boundPredicates), filter.child());
+                    Expression boundPredicates = bind(filter.getPredicates(), filter.children(), filter);
+                    return new LogicalFilter(boundPredicates, filter.child());
                 })
             ),
             RuleType.BINDING_JOIN_SLOT.build(
                 logicalJoin().then(join -> {
-                    Optional<Expression> cond = join.operator.getCondition()
+                    Optional<Expression> cond = join.getCondition()
                             .map(expr -> bind(expr, join.children(), join));
-                    LogicalJoin op = new LogicalJoin(join.operator.getJoinType(), cond);
-                    return plan(op, join.left(), join.right());
+                    return new LogicalJoin(join.getJoinType(), cond, join.left(), join.right());
                 })
             ),
             RuleType.BINDING_AGGREGATE_SLOT.build(
                 logicalAggregate().then(agg -> {
-                    List<Expression> groupBy = bind(
-                            agg.operator.getGroupByExpressionList(), agg.children(), agg);
-                    List<NamedExpression> output = bind(
-                            agg.operator.getOutputExpressionList(), agg.children(), agg);
-                    LogicalAggregate op = agg.operator.withGroupByAndOutput(groupBy, output);
-                    return plan(op, agg.child());
+                    List<Expression> groupBy = bind(agg.getGroupByExpressionList(), agg.children(), agg);
+                    List<NamedExpression> output = bind(agg.getOutputExpressionList(), agg.children(), agg);
+                    return agg.withGroupByAndOutput(groupBy, output);
                 })
             ),
             RuleType.BINDING_SORT_SLOT.build(
                 logicalSort().then(sort -> {
-                    List<OrderKey> sortItemList = sort.operator.getOrderKeys()
+                    List<OrderKey> sortItemList = sort.getOrderKeys()
                             .stream()
                             .map(orderKey -> {
                                 Expression item = bind(orderKey.getExpr(), sort.children(), sort);
                                 return new OrderKey(item, orderKey.isAsc(), orderKey.isNullFirst());
                             }).collect(Collectors.toList());
 
-                    LogicalSort op = new LogicalSort(sortItemList);
-                    return plan(op, sort.child());
+                    return new LogicalSort(sortItemList, sort.child());
                 })
             )
         );
@@ -168,7 +161,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
 
         @Override
         public Expression visitUnboundStar(UnboundStar unboundStar, Void context) {
-            if (!(plan.getOperator() instanceof LogicalProject)) {
+            if (!(plan instanceof LogicalProject)) {
                 throw new AnalysisException("UnboundStar must exists in Projection");
             }
             List<String> qualifier = unboundStar.getQualifier();
@@ -260,7 +253,7 @@ public class BindSlotReference implements AnalysisRuleFactory {
     /** BoundStar is used to wrap list of slots for temporary. */
     private class BoundStar extends NamedExpression {
         public BoundStar(List<Slot> children) {
-            super(NodeType.BOUND_STAR, children.toArray(new Slot[0]));
+            super(ExpressionType.BOUND_STAR, children.toArray(new Slot[0]));
             Preconditions.checkArgument(children.stream().allMatch(slot -> !(slot instanceof UnboundSlot)),
                     "BoundStar can not wrap UnboundSlot"
             );
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java
index 6d02101588..124021b9cf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/ProjectToGlobalAggregate.java
@@ -17,12 +17,12 @@
 
 package org.apache.doris.nereids.rules.analysis;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 
 import com.google.common.collect.ImmutableList;
 
@@ -32,13 +32,12 @@ public class ProjectToGlobalAggregate extends OneAnalysisRuleFactory {
     public Rule<Plan> build() {
         return RuleType.PROJECT_TO_GLOBAL_AGGREGATE.build(
            logicalProject().then(project -> {
-               boolean needGlobalAggregate = project.operator.getProjects()
+               boolean needGlobalAggregate = project.getProjects()
                        .stream()
                        .anyMatch(this::hasNonWindowedAggregateFunction);
 
                if (needGlobalAggregate) {
-                   LogicalAggregate op = new LogicalAggregate(ImmutableList.of(), project.operator.getProjects());
-                   return plan(op, project.child());
+                   return new LogicalAggregate(ImmutableList.of(), project.getProjects(), project.child());
                } else {
                    return project;
                }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommutative.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommutative.java
index a87003f9f2..59e3bab963 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommutative.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinCommutative.java
@@ -17,11 +17,11 @@
 
 package org.apache.doris.nereids.rules.exploration.join;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
 /**
  * rule factory for exchange inner join's children.
@@ -50,10 +50,11 @@ public class JoinCommutative extends OneExplorationRuleFactory {
 
     @Override
     public Rule<Plan> build() {
-        return innerLogicalJoin().then(join -> plan(
-            new LogicalJoin(join.operator.getJoinType().swap(), join.operator.getCondition()),
-            join.right(),
-            join.left()
-        )).toRule(RuleType.LOGICAL_JOIN_COMMUTATIVE);
+        return innerLogicalJoin().then(join -> new LogicalJoin(
+                join.getJoinType().swap(),
+                join.getCondition(),
+                join.right(),
+                join.left())
+        ).toRule(RuleType.LOGICAL_JOIN_COMMUTATIVE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
index a86a6f39d4..d269e2fb2b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinExchange.java
@@ -17,13 +17,12 @@
 
 package org.apache.doris.nereids.rules.exploration.join;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
 
 /**
@@ -40,27 +39,17 @@ public class JoinExchange extends OneExplorationRuleFactory {
     @Override
     public Rule<Plan> build() {
         return innerLogicalJoin(innerLogicalJoin(), innerLogicalJoin()).then(topJoin -> {
-            LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> leftJoin = topJoin.left();
-            LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> rightJoin = topJoin.right();
+            LogicalJoin<GroupPlan, GroupPlan> leftJoin = topJoin.left();
+            LogicalJoin<GroupPlan, GroupPlan> rightJoin = topJoin.right();
 
             GroupPlan a = leftJoin.left();
             GroupPlan b = leftJoin.right();
             GroupPlan c = rightJoin.left();
             GroupPlan d = rightJoin.right();
 
-            Plan newLeftJoin = plan(
-                    new LogicalJoin(leftJoin.operator.getJoinType(), leftJoin.operator.getCondition()),
-                    a, c
-            );
-            Plan newRightJoin = plan(
-                    new LogicalJoin(rightJoin.operator.getJoinType(), rightJoin.operator.getCondition()),
-                    b, d
-            );
-            Plan newTopJoin = plan(
-                    new LogicalJoin(topJoin.operator.getJoinType(), topJoin.operator.getCondition()),
-                    newLeftJoin, newRightJoin
-            );
-            return newTopJoin;
+            Plan newLeftJoin = new LogicalJoin(leftJoin.getJoinType(), leftJoin.getCondition(), a, c);
+            Plan newRightJoin = new LogicalJoin(rightJoin.getJoinType(), rightJoin.getCondition(), b, d);
+            return new LogicalJoin(topJoin.getJoinType(), topJoin.getCondition(), newLeftJoin, newRightJoin);
         }).toRule(RuleType.LOGICAL_JOIN_EXCHANGE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
index 1ac2794acf..2822ad84aa 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLAsscom.java
@@ -17,13 +17,12 @@
 
 package org.apache.doris.nereids.rules.exploration.join;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
 /**
  * Rule for change inner join left associative to right.
@@ -39,22 +38,14 @@ public class JoinLAsscom extends OneExplorationRuleFactory {
     @Override
     public Rule<Plan> build() {
         return innerLogicalJoin(innerLogicalJoin(), any()).then(topJoin -> {
-            LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> bottomJoin = topJoin.left();
+            LogicalJoin<GroupPlan, GroupPlan> bottomJoin = topJoin.left();
 
             GroupPlan a = bottomJoin.left();
             GroupPlan b = bottomJoin.right();
             Plan c = topJoin.right();
 
-            Plan newBottomJoin = plan(
-                    new LogicalJoin(bottomJoin.operator.getJoinType(), bottomJoin.operator.getCondition()),
-                    a, c
-            );
-
-            Plan newTopJoin = plan(
-                    new LogicalJoin(bottomJoin.operator.getJoinType(), topJoin.operator.getCondition()),
-                    newBottomJoin, b
-            );
-            return newTopJoin;
+            Plan newBottomJoin = new LogicalJoin(bottomJoin.getJoinType(), bottomJoin.getCondition(), a, c);
+            return new LogicalJoin(bottomJoin.getJoinType(), topJoin.getCondition(), newBottomJoin, b);
         }).toRule(RuleType.LOGICAL_JOIN_L_ASSCOM);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLeftAssociative.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLeftAssociative.java
index ee20901f72..e47809003e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLeftAssociative.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/join/JoinLeftAssociative.java
@@ -17,12 +17,12 @@
 
 package org.apache.doris.nereids.rules.exploration.join;
 
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.exploration.OneExplorationRuleFactory;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 
 /**
  * Rule factory for change inner join left associative to right.
@@ -39,14 +39,16 @@ public class JoinLeftAssociative extends OneExplorationRuleFactory {
     public Rule<Plan> build() {
         return innerLogicalJoin(innerLogicalJoin(), any()).then(root -> {
             // fixme, just for example now
-            return plan(
-                new LogicalJoin(JoinType.INNER_JOIN, root.operator.getCondition()),
-                root.left().left(),
-                plan(
-                    new LogicalJoin(JoinType.INNER_JOIN, root.operator.getCondition()),
-                    root.left().right(),
-                    root.right()
-                )
+            return new LogicalJoin(
+                    JoinType.INNER_JOIN,
+                    root.getCondition(),
+                    root.left().left(),
+                    new LogicalJoin(
+                            JoinType.INNER_JOIN,
+                            root.getCondition(),
+                            root.left().right(),
+                            root.right()
+                    )
             );
         }).toRule(RuleType.LOGICAL_LEFT_JOIN_ASSOCIATIVE);
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeBinaryPredicatesRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeBinaryPredicatesRule.java
index 7021a7d19b..3c9c92e500 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeBinaryPredicatesRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/NormalizeBinaryPredicatesRule.java
@@ -20,10 +20,10 @@ package org.apache.doris.nereids.rules.expression.rewrite.rules;
 import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule;
 import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext;
 import org.apache.doris.nereids.rules.expression.rewrite.RewriteHelper;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
 import org.apache.doris.nereids.trees.expressions.EqualTo;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.LessThan;
@@ -42,7 +42,7 @@ public class NormalizeBinaryPredicatesRule extends AbstractExpressionRewriteRule
     public Expression visitComparisonPredicate(ComparisonPredicate expr, ExpressionRewriteContext context) {
 
         if (RewriteHelper.isConstant(expr.left()) && !RewriteHelper.isConstant(expr.right())) {
-            NodeType exprType = expr.getType();
+            ExpressionType exprType = expr.getType();
             switch (exprType) {
                 case EQUAL_TO:
                     return new EqualTo(expr.right(), expr.left());
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
index eb26bdab82..c21fc3717f 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rewrite/rules/SimplifyNotExprRule.java
@@ -19,11 +19,11 @@ package org.apache.doris.nereids.rules.expression.rewrite.rules;
 
 import org.apache.doris.nereids.rules.expression.rewrite.AbstractExpressionRewriteRule;
 import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRewriteContext;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.And;
 import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
 import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.GreaterThan;
 import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.LessThan;
@@ -58,7 +58,7 @@ public class SimplifyNotExprRule extends AbstractExpressionRewriteRule {
             ComparisonPredicate cp = (ComparisonPredicate) expr.child();
             Expression left =  rewrite(cp.left(), context);
             Expression right = rewrite(cp.right(), context);
-            NodeType type = cp.getType();
+            ExpressionType type = cp.getType();
             switch (type) {
                 case GREATER_THAN:
                     return new LessThanEqual(left, right);
@@ -75,7 +75,7 @@ public class SimplifyNotExprRule extends AbstractExpressionRewriteRule {
             CompoundPredicate cp = (CompoundPredicate) expr.child();
             Expression left =  rewrite(new Not(cp.left()), context);
             Expression right = rewrite(new Not(cp.right()), context);
-            NodeType type = cp.getType();
+            ExpressionType type = cp.getType();
             switch (type) {
                 case AND:
                     return new Or(left, right);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
index 3d05c23aaf..af5ac92f04 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalAggToPhysicalHashAgg.java
@@ -17,10 +17,10 @@
 
 package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalAggregate;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
 
 /**
  * Implementation rule that convert logical aggregation to physical hash aggregation.
@@ -28,16 +28,15 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalAggToPhysicalHashAgg extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalAggregate().then(agg -> plan(
-            new PhysicalAggregate(
+        return logicalAggregate().then(agg -> new PhysicalAggregate(
                 // TODO: for use a function to judge whether use stream
-                agg.getOperator().getGroupByExpressionList(),
-                agg.getOperator().getOutputExpressionList(),
-                agg.getOperator().getPartitionExprList(),
-                agg.getOperator().getAggPhase(),
-                false),
-            agg.getLogicalProperties(),
-            agg.child()
-        )).toRule(RuleType.LOGICAL_AGG_TO_PHYSICAL_HASH_AGG_RULE);
+                agg.getGroupByExpressionList(),
+                agg.getOutputExpressionList(),
+                agg.getPartitionExprList(),
+                agg.getAggPhase(),
+                false,
+                agg.getLogicalProperties(),
+                agg.child())
+        ).toRule(RuleType.LOGICAL_AGG_TO_PHYSICAL_HASH_AGG_RULE);
     }
 }
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 1d6afa2f08..cd2fd018cc 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
@@ -17,10 +17,10 @@
 
 package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalFilter;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
 
 /**
  * Implementation rule that convert logical filter to physical filter.
@@ -28,10 +28,10 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalFilterToPhysicalFilter extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalFilter().then(filter -> plan(
-            new PhysicalFilter(filter.getOperator().getPredicates()),
+        return logicalFilter().then(filter -> new PhysicalFilter(
+            filter.getPredicates(),
             filter.getLogicalProperties(),
-            filter.child()
-        )).toRule(RuleType.LOGICAL_FILTER_TO_PHYSICAL_FILTER_RULE);
+            filter.child())
+        ).toRule(RuleType.LOGICAL_FILTER_TO_PHYSICAL_FILTER_RULE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
index 5204b37467..373f07e953 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
@@ -17,10 +17,10 @@
 
 package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
 
 /**
  * Implementation rule that convert logical join to physical hash join.
@@ -28,10 +28,12 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalJoinToHashJoin extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalJoin().then(join -> plan(
-            new PhysicalHashJoin(join.operator.getJoinType(), join.operator.getCondition()),
+        return logicalJoin().then(join -> new PhysicalHashJoin(
+            join.getJoinType(),
+            join.getCondition(),
             join.getLogicalProperties(),
-            join.left(), join.right()
-        )).toRule(RuleType.LOGICAL_JOIN_TO_HASH_JOIN_RULE);
+            join.left(),
+            join.right())
+        ).toRule(RuleType.LOGICAL_JOIN_TO_HASH_JOIN_RULE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOlapScanToPhysicalOlapScan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOlapScanToPhysicalOlapScan.java
index dc9e8fff7a..01f3b0320b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOlapScanToPhysicalOlapScan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalOlapScanToPhysicalOlapScan.java
@@ -18,10 +18,12 @@
 package org.apache.doris.nereids.rules.implementation;
 
 import org.apache.doris.catalog.OlapTable;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOlapScan;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+
+import java.util.Optional;
 
 /**
  * Implementation rule that convert logical OlapScan to physical OlapScan.
@@ -29,10 +31,13 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalOlapScanToPhysicalOlapScan extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalOlapScan().then(olapScan -> plan(
-                        // TODO: olapScan should get (OlapTable);
-                        new PhysicalOlapScan((OlapTable) olapScan.getOperator().getTable(),
-                                olapScan.getOperator().getQualifier()), olapScan.getLogicalProperties()))
-                .toRule(RuleType.LOGICAL_OLAP_SCAN_TO_PHYSICAL_OLAP_SCAN_RULE);
+        return logicalOlapScan().then(olapScan ->
+                // TODO: olapScan should get (OlapTable);
+                new PhysicalOlapScan(
+                    (OlapTable) olapScan.getTable(),
+                    olapScan.getQualifier(),
+                    Optional.empty(),
+                    olapScan.getLogicalProperties())
+        ).toRule(RuleType.LOGICAL_OLAP_SCAN_TO_PHYSICAL_OLAP_SCAN_RULE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProject.java
index 67b665195e..d122e94b44 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProject.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProject.java
@@ -17,10 +17,10 @@
 
 package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalProject;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
 
 /**
  * Implementation rule that convert logical project to physical project.
@@ -28,10 +28,10 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalProjectToPhysicalProject extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalProject().then(project -> plan(
-                new PhysicalProject(project.getOperator().getProjects()),
+        return logicalProject().then(project -> new PhysicalProject(
+                project.getProjects(),
                 project.getLogicalProperties(),
-                project.child()
-        )).toRule(RuleType.LOGICAL_PROJECT_TO_PHYSICAL_PROJECT_RULE);
+                project.child())
+        ).toRule(RuleType.LOGICAL_PROJECT_TO_PHYSICAL_PROJECT_RULE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalSortToPhysicalHeapSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalSortToPhysicalHeapSort.java
index e15ab360f4..a786c76ff7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalSortToPhysicalHeapSort.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalSortToPhysicalHeapSort.java
@@ -17,10 +17,10 @@
 
 package org.apache.doris.nereids.rules.implementation;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHeapSort;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHeapSort;
 
 /**
  * Implementation rule that convert logical sort to physical sort.
@@ -28,9 +28,12 @@ import org.apache.doris.nereids.trees.plans.Plan;
 public class LogicalSortToPhysicalHeapSort extends OneImplementationRuleFactory {
     @Override
     public Rule<Plan> build() {
-        return logicalSort().then(sort -> plan(
-                        new PhysicalHeapSort(sort.getOperator().getOrderKeys(), sort.getOperator().getLimit(),
-                                sort.getOperator().getOffset()), sort.getLogicalProperties(), sort.child()))
-                .toRule(RuleType.LOGICAL_SORT_TO_PHYSICAL_HEAP_SORT_RULE);
+        return logicalSort().then(sort -> new PhysicalHeapSort(
+                sort.getOrderKeys(),
+                sort.getLimit(),
+                sort.getOffset(),
+                sort.getLogicalProperties(),
+                sort.child())
+            ).toRule(RuleType.LOGICAL_SORT_TO_PHYSICAL_HEAP_SORT_RULE);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AggregateDisassemble.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AggregateDisassemble.java
index 9d26605e7e..6478b83868 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AggregateDisassemble.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AggregateDisassemble.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.nereids.rules.rewrite;
 
-import org.apache.doris.nereids.operators.plans.AggPhase;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Alias;
@@ -26,15 +24,15 @@ import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.AggregateFunction;
-import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
+import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -53,19 +51,15 @@ import java.util.stream.Collectors;
  *
  * TODO:
  *     1. use different class represent different phase aggregate
- *     2. if instance count is 1, shouldn't disassemble the agg operator
+ *     2. if instance count is 1, shouldn't disassemble the agg plan
  *     3. we need another rule to removing duplicated expressions in group by expression list
  */
 public class AggregateDisassemble extends OneRewriteRuleFactory {
 
     @Override
     public Rule<Plan> build() {
-        return logicalAggregate().when(p -> {
-            LogicalAggregate logicalAggregate = p.getOperator();
-            return !logicalAggregate.isDisassembled();
-        }).thenApply(ctx -> {
-            LogicalUnaryPlan<LogicalAggregate, GroupPlan> plan = ctx.root;
-            LogicalAggregate aggregate = plan.getOperator();
+        return logicalAggregate().when(agg -> !agg.isDisassembled()).thenApply(ctx -> {
+            LogicalAggregate<GroupPlan> aggregate = ctx.root;
             List<NamedExpression> originOutputExprs = aggregate.getOutputExpressionList();
             List<Expression> originGroupByExprs = aggregate.getGroupByExpressionList();
 
@@ -130,21 +124,22 @@ public class AggregateDisassemble extends OneRewriteRuleFactory {
                     localGroupByExprs,
                     localOutputExprs,
                     true,
-                    AggPhase.LOCAL
+                    AggPhase.LOCAL,
+                    aggregate.child()
             );
-            LogicalAggregate globalAggregate = new LogicalAggregate(
+            return new LogicalAggregate(
                     globalGroupByExprs,
                     globalOutputExprs,
                     true,
-                    AggPhase.GLOBAL
+                    AggPhase.GLOBAL,
+                    localAggregate
             );
-            return plan(globalAggregate, plan(localAggregate, plan.child(0)));
         }).toRule(RuleType.AGGREGATE_DISASSEMBLE);
     }
 
     @SuppressWarnings("InnerClassMayBeStatic")
     private static class ExpressionReplacer
-            extends ExpressionVisitor<Expression, Map<Expression, Expression>> {
+            extends DefaultExpressionRewriter<Map<Expression, Expression>> {
         private static final ExpressionReplacer INSTANCE = new ExpressionReplacer();
 
         @Override
@@ -156,16 +151,7 @@ public class AggregateDisassemble extends OneRewriteRuleFactory {
             if (substitutionMap.containsKey(expr)) {
                 return substitutionMap.get(expr);
             } else {
-                List<Expression> newChildren = new ArrayList<>();
-                boolean hasNewChildren = false;
-                for (Expression child : expr.children()) {
-                    Expression newChild = visit(child, substitutionMap);
-                    if (newChild != child) {
-                        hasNewChildren = true;
-                    }
-                    newChildren.add(newChild);
-                }
-                return hasNewChildren ? expr.withChildren(newChildren) : expr;
+                return super.visit(expr, substitutionMap);
             }
         }
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AbstractPushDownProjectRule.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AbstractPushDownProjectRule.java
index 2b638050f9..cac8298da1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AbstractPushDownProjectRule.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/AbstractPushDownProjectRule.java
@@ -26,6 +26,7 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.Plan;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.util.List;
@@ -43,9 +44,9 @@ public abstract class AbstractPushDownProjectRule<C extends Plan> extends OneRew
     public Rule<Plan> build() {
         return logicalProject(target).then(project -> {
             List<Expression> projects = Lists.newArrayList();
-            projects.addAll(project.operator.getProjects());
+            projects.addAll(project.getProjects());
             Set<Slot> projectSlots = SlotExtractor.extractSlot(projects);
-            return plan(project.operator, pushDownProject(project.child(), projectSlots));
+            return project.withChildren(ImmutableList.of(pushDownProject(project.child(), projectSlots)));
         }).toRule(ruleType);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneAggChildColumns.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneAggChildColumns.java
index 46d5ea6ed5..9c2e43a0d2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneAggChildColumns.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneAggChildColumns.java
@@ -17,7 +17,6 @@
 
 package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
@@ -26,7 +25,9 @@ import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.util.List;
@@ -55,14 +56,14 @@ public class PruneAggChildColumns extends OneRewriteRuleFactory {
     public Rule<Plan> build() {
         return RuleType.COLUMN_PRUNE_AGGREGATION_CHILD.build(logicalAggregate().then(agg -> {
             List<Expression> slots = Lists.newArrayList();
-            slots.addAll(agg.operator.getExpressions());
+            slots.addAll(agg.getExpressions());
             Set<Slot> outputs = SlotExtractor.extractSlot(slots);
             List<NamedExpression> prunedOutputs = agg.child().getOutput().stream().filter(outputs::contains)
                     .collect(Collectors.toList());
             if (prunedOutputs.size() == agg.child().getOutput().size()) {
                 return agg;
             }
-            return plan(agg.operator, plan(new LogicalProject(prunedOutputs), agg.child()));
+            return agg.withChildren(ImmutableList.of(new LogicalProject(prunedOutputs, agg.child())));
         }));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneFilterChildColumns.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneFilterChildColumns.java
index 9daaabd296..3b1bddc648 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneFilterChildColumns.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneFilterChildColumns.java
@@ -17,15 +17,15 @@
 
 package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.util.Set;
@@ -51,7 +51,7 @@ import java.util.stream.Stream;
  *   |
  * scan(k1,k2,k3,v1)
  */
-public class PruneFilterChildColumns extends AbstractPushDownProjectRule<LogicalUnaryPlan<LogicalFilter, GroupPlan>> {
+public class PruneFilterChildColumns extends AbstractPushDownProjectRule<LogicalFilter<GroupPlan>> {
 
     public PruneFilterChildColumns() {
         setRuleType(RuleType.COLUMN_PRUNE_FILTER_CHILD);
@@ -59,12 +59,14 @@ public class PruneFilterChildColumns extends AbstractPushDownProjectRule<Logical
     }
 
     @Override
-    protected Plan pushDownProject(LogicalUnaryPlan<LogicalFilter, GroupPlan> filterPlan, Set<Slot> references) {
-        Set<Slot> filterSlots = SlotExtractor.extractSlot(filterPlan.operator.getPredicates());
+    protected Plan pushDownProject(LogicalFilter<GroupPlan> filterPlan, Set<Slot> references) {
+        Set<Slot> filterSlots = SlotExtractor.extractSlot(filterPlan.getPredicates());
         Set<Slot> required = Stream.concat(references.stream(), filterSlots.stream()).collect(Collectors.toSet());
         if (required.containsAll(filterPlan.child().getOutput())) {
             return filterPlan;
         }
-        return plan(filterPlan.operator, plan(new LogicalProject(Lists.newArrayList(required)), filterPlan.child()));
+        return filterPlan.withChildren(
+            ImmutableList.of(new LogicalProject(Lists.newArrayList(required), filterPlan.child()))
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneJoinChildrenColumns.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneJoinChildrenColumns.java
index b9bc738768..738372b00a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneJoinChildrenColumns.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneJoinChildrenColumns.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.ExprId;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
@@ -26,7 +24,10 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 import java.util.Set;
@@ -54,7 +55,7 @@ import java.util.stream.Collectors;
  * scan                scan
  */
 public class PruneJoinChildrenColumns
-        extends AbstractPushDownProjectRule<LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan>> {
+        extends AbstractPushDownProjectRule<LogicalJoin<GroupPlan, GroupPlan>> {
 
     public PruneJoinChildrenColumns() {
         setRuleType(RuleType.COLUMN_PRUNE_JOIN_CHILD);
@@ -62,10 +63,10 @@ public class PruneJoinChildrenColumns
     }
 
     @Override
-    protected Plan pushDownProject(LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> joinPlan,
+    protected Plan pushDownProject(LogicalJoin<GroupPlan, GroupPlan> joinPlan,
             Set<Slot> references) {
-        if (joinPlan.operator.getCondition().isPresent()) {
-            references.addAll(SlotExtractor.extractSlot(joinPlan.operator.getCondition().get()));
+        if (joinPlan.getCondition().isPresent()) {
+            references.addAll(SlotExtractor.extractSlot(joinPlan.getCondition().get()));
         }
         Set<ExprId> exprIds = references.stream().map(NamedExpression::getExprId).collect(Collectors.toSet());
 
@@ -78,12 +79,12 @@ public class PruneJoinChildrenColumns
         Plan rightPlan = joinPlan.right();
 
         if (leftInputs.size() != leftPlan.getOutput().size()) {
-            leftPlan = plan(new LogicalProject(leftInputs), leftPlan);
+            leftPlan = new LogicalProject(leftInputs, leftPlan);
         }
 
         if (rightInputs.size() != rightPlan.getOutput().size()) {
-            rightPlan = plan(new LogicalProject(rightInputs), rightPlan);
+            rightPlan = new LogicalProject(rightInputs, rightPlan);
         }
-        return plan(joinPlan.operator, leftPlan, rightPlan);
+        return joinPlan.withChildren(ImmutableList.of(leftPlan, rightPlan));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneSortChildColumns.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneSortChildColumns.java
index 333d4a7749..309a4d7cab 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneSortChildColumns.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PruneSortChildColumns.java
@@ -17,15 +17,15 @@
 
 package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
-import org.apache.doris.nereids.operators.plans.logical.LogicalSort;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
 
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 
 import java.util.Set;
@@ -36,7 +36,7 @@ import java.util.stream.Stream;
  * prune join children output.
  * pattern: project(sort())
  */
-public class PruneSortChildColumns extends AbstractPushDownProjectRule<LogicalUnaryPlan<LogicalSort, GroupPlan>> {
+public class PruneSortChildColumns extends AbstractPushDownProjectRule<LogicalSort<GroupPlan>> {
 
     public PruneSortChildColumns() {
         setRuleType(RuleType.COLUMN_PRUNE_SORT_CHILD);
@@ -44,12 +44,14 @@ public class PruneSortChildColumns extends AbstractPushDownProjectRule<LogicalUn
     }
 
     @Override
-    protected Plan pushDownProject(LogicalUnaryPlan<LogicalSort, GroupPlan> sortPlan, Set<Slot> references) {
-        Set<Slot> sortSlots = SlotExtractor.extractSlot(sortPlan.operator.getExpressions());
+    protected Plan pushDownProject(LogicalSort<GroupPlan> sortPlan, Set<Slot> references) {
+        Set<Slot> sortSlots = SlotExtractor.extractSlot(sortPlan.getExpressions());
         Set<Slot> required = Stream.concat(references.stream(), sortSlots.stream()).collect(Collectors.toSet());
         if (required.containsAll(sortPlan.child().getOutput())) {
             return sortPlan;
         }
-        return plan(sortPlan.operator, plan(new LogicalProject(Lists.newArrayList(required)), sortPlan.child()));
+        return sortPlan.withChildren(
+            ImmutableList.of(new LogicalProject(Lists.newArrayList(required), sortPlan.child()))
+        );
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java
index d712d5a149..2ea6890894 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/logical/PushPredicateThroughJoin.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.nereids.rules.rewrite.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.rules.expression.rewrite.ExpressionRuleExecutor;
@@ -30,7 +28,8 @@ import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.visitor.SlotExtractor;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.Lists;
@@ -69,20 +68,20 @@ public class PushPredicateThroughJoin extends OneRewriteRuleFactory {
     public Rule<Plan> build() {
         return logicalFilter(innerLogicalJoin()).then(filter -> {
 
-            LogicalJoin joinOp = filter.child().operator;
+            LogicalJoin<GroupPlan, GroupPlan> join = filter.child();
 
-            Expression wherePredicates = filter.operator.getPredicates();
+            Expression wherePredicates = filter.getPredicates();
             Expression onPredicates = Literal.TRUE_LITERAL;
 
             List<Expression> otherConditions = Lists.newArrayList();
             List<Expression> eqConditions = Lists.newArrayList();
 
-            if (joinOp.getCondition().isPresent()) {
-                onPredicates = joinOp.getCondition().get();
+            if (join.getCondition().isPresent()) {
+                onPredicates = join.getCondition().get();
             }
 
-            List<Slot> leftInput = filter.child().left().getOutput();
-            List<Slot> rightInput = filter.child().right().getOutput();
+            List<Slot> leftInput = join.left().getOutput();
+            List<Slot> rightInput = join.right().getOutput();
 
             ExpressionUtils.extractConjunct(ExpressionUtils.and(onPredicates, wherePredicates)).forEach(predicate -> {
                 if (Objects.nonNull(getJoinCondition(predicate, leftInput, rightInput))) {
@@ -114,11 +113,11 @@ public class PushPredicateThroughJoin extends OneRewriteRuleFactory {
             otherConditions.addAll(eqConditions);
             Expression joinConditions = ExpressionUtils.and(otherConditions);
 
-            return pushDownPredicate(filter.child(), joinConditions, leftPredicates, rightPredicates);
+            return pushDownPredicate(join, joinConditions, leftPredicates, rightPredicates);
         }).toRule(RuleType.PUSH_DOWN_PREDICATE_THROUGH_JOIN);
     }
 
-    private Plan pushDownPredicate(LogicalBinaryPlan<LogicalJoin, GroupPlan, GroupPlan> joinPlan,
+    private Plan pushDownPredicate(LogicalJoin<GroupPlan, GroupPlan> joinPlan,
             Expression joinConditions, List<Expression> leftPredicates, List<Expression> rightPredicates) {
 
         Expression left = ExpressionUtils.and(leftPredicates);
@@ -128,14 +127,14 @@ public class PushPredicateThroughJoin extends OneRewriteRuleFactory {
         Plan leftPlan = joinPlan.left();
         Plan rightPlan = joinPlan.right();
         if (!left.equals(Literal.TRUE_LITERAL)) {
-            leftPlan = plan(new LogicalFilter(exprRewriter.rewrite(left)), leftPlan);
+            leftPlan = new LogicalFilter(exprRewriter.rewrite(left), leftPlan);
         }
 
         if (!right.equals(Literal.TRUE_LITERAL)) {
-            rightPlan = plan(new LogicalFilter(exprRewriter.rewrite(right)), rightPlan);
+            rightPlan = new LogicalFilter(exprRewriter.rewrite(right), rightPlan);
         }
 
-        return plan(new LogicalJoin(joinPlan.operator.getJoinType(), Optional.of(joinConditions)), leftPlan, rightPlan);
+        return new LogicalJoin(joinPlan.getJoinType(), Optional.of(joinConditions), leftPlan, rightPlan);
     }
 
     private Expression getJoinCondition(Expression predicate, List<Slot> leftOutputs, List<Slot> rightOutputs) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
index 52054e1646..70eeee0e88 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/AbstractTreeNode.java
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.trees;
 
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
 
 import com.google.common.collect.ImmutableList;
 
@@ -36,34 +35,26 @@ import java.util.Optional;
 public abstract class AbstractTreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>>
         implements TreeNode<NODE_TYPE> {
 
-    protected final NodeType type;
     protected final List<NODE_TYPE> children;
     // TODO: Maybe we should use a GroupPlan to avoid TreeNode hold the GroupExpression.
     // https://github.com/apache/doris/pull/9807#discussion_r884829067
     protected final Optional<GroupExpression> groupExpression;
 
-    public AbstractTreeNode(NodeType type, NODE_TYPE... children) {
-        this(type, Optional.empty(), children);
+    public AbstractTreeNode(NODE_TYPE... children) {
+        this(Optional.empty(), children);
     }
 
     /**
      * Constructor for plan node.
      *
-     * @param type node type
-     * @param groupExpression group expression related to the operator of this node
+     * @param groupExpression group expression related to the plan of this node
      * @param children children of this node
      */
-    public AbstractTreeNode(NodeType type, Optional<GroupExpression> groupExpression, NODE_TYPE... children) {
-        this.type = type;
+    public AbstractTreeNode(Optional<GroupExpression> groupExpression, NODE_TYPE... children) {
         this.children = ImmutableList.copyOf(children);
         this.groupExpression = Objects.requireNonNull(groupExpression, "groupExpression can not be null");
     }
 
-    @Override
-    public Operator getOperator() {
-        throw new RuntimeException();
-    }
-
     @Override
     public Optional<GroupExpression> getGroupExpression() {
         return groupExpression;
@@ -79,11 +70,6 @@ public abstract class AbstractTreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>>
         return children;
     }
 
-    @Override
-    public NodeType getType() {
-        return type;
-    }
-
     public int arity() {
         return children.size();
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
index 2e3802d6bf..93d9e97522 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/TreeNode.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.Operator;
 
 import com.alibaba.google.common.collect.ImmutableList;
 
@@ -35,13 +34,9 @@ import java.util.function.Predicate;
  */
 public interface TreeNode<NODE_TYPE extends TreeNode<NODE_TYPE>> {
 
-    Operator getOperator();
-
     // cache GroupExpression for fast exit from Memo.copyIn.
     Optional<GroupExpression> getGroupExpression();
 
-    NodeType getType();
-
     List<NODE_TYPE> children();
 
     NODE_TYPE child(int index);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
index 5941d6d7f1..7ea8a52a55 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Alias.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
 
@@ -47,7 +46,7 @@ public class Alias extends NamedExpression implements UnaryExpression {
     }
 
     private Alias(ExprId exprId, Expression child, String name) {
-        super(NodeType.ALIAS, child);
+        super(ExpressionType.ALIAS, child);
         this.exprId = exprId;
         this.name = name;
         this.qualifier = ImmutableList.of();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
index ddef7d956b..dd1fe72334 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/And.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
-import org.apache.doris.nereids.trees.NodeType;
-
 import com.google.common.base.Preconditions;
 
 import java.util.List;
@@ -34,7 +32,7 @@ public class And extends CompoundPredicate {
      * @param right right child of comparison predicate
      */
     public And(Expression left, Expression right) {
-        super(NodeType.AND, left, right);
+        super(ExpressionType.AND, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
index c7b64316c1..bd87c7540e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Arithmetic.java
@@ -21,7 +21,6 @@ package org.apache.doris.nereids.trees.expressions;
 import org.apache.doris.analysis.ArithmeticExpr;
 import org.apache.doris.analysis.ArithmeticExpr.Operator;
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
 
@@ -116,26 +115,26 @@ public abstract class Arithmetic extends Expression {
         return op;
     }
 
-    private static NodeType genNodeType(ArithmeticOperator op) {
+    private static ExpressionType genNodeType(ArithmeticOperator op) {
         switch (op) {
             case MULTIPLY:
-                return NodeType.MULTIPLY;
+                return ExpressionType.MULTIPLY;
             case DIVIDE:
-                return NodeType.DIVIDE;
+                return ExpressionType.DIVIDE;
             case MOD:
-                return NodeType.MOD;
+                return ExpressionType.MOD;
             case ADD:
-                return NodeType.ADD;
+                return ExpressionType.ADD;
             case SUBTRACT:
-                return NodeType.SUBTRACT;
+                return ExpressionType.SUBTRACT;
             case BITAND:
-                return NodeType.BITAND;
+                return ExpressionType.BITAND;
             case BITOR:
-                return NodeType.BITOR;
+                return ExpressionType.BITOR;
             case BITXOR:
-                return NodeType.BITXOR;
+                return ExpressionType.BITXOR;
             case BITNOT:
-                return NodeType.NOT;
+                return ExpressionType.NOT;
             default:
                 return null;
         }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
index 63087372fc..a851437f5b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Between.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -46,7 +45,7 @@ public class Between extends Expression implements TernaryExpression {
 
     public Between(Expression compareExpr, Expression lowerBound,
                    Expression upperBound) {
-        super(NodeType.BETWEEN, compareExpr, lowerBound, upperBound);
+        super(ExpressionType.BETWEEN, compareExpr, lowerBound, upperBound);
         this.compareExpr = compareExpr;
         this.lowerBound = lowerBound;
         this.upperBound = upperBound;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
index 97d0bca534..51b35f4c63 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ComparisonPredicate.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -37,7 +36,7 @@ public abstract class ComparisonPredicate extends Expression implements BinaryEx
      * @param left     left child of comparison predicate
      * @param right    right child of comparison predicate
      */
-    public ComparisonPredicate(NodeType nodeType, Expression left, Expression right) {
+    public ComparisonPredicate(ExpressionType nodeType, Expression left, Expression right) {
         super(nodeType, left, right);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
index bd5e505a71..ca60ef8e38 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/CompoundPredicate.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import java.util.List;
@@ -37,7 +36,7 @@ public class CompoundPredicate extends Expression implements BinaryExpression {
      * @param left  left child of comparison predicate
      * @param right right child of comparison predicate
      */
-    public CompoundPredicate(NodeType type, Expression left, Expression right) {
+    public CompoundPredicate(ExpressionType type, Expression left, Expression right) {
         super(type, left, right);
     }
 
@@ -81,11 +80,11 @@ public class CompoundPredicate extends Expression implements BinaryExpression {
         return nodeType + "(" + left() + ", " + right() + ")";
     }
 
-    public NodeType flip() {
-        if (getType() == NodeType.AND) {
-            return NodeType.OR;
+    public ExpressionType flip() {
+        if (getType() == ExpressionType.AND) {
+            return ExpressionType.OR;
         }
-        return NodeType.AND;
+        return ExpressionType.AND;
     }
 }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
index 0c5caf2212..ae98950e8d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/EqualTo.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -31,7 +30,7 @@ import java.util.List;
 public class EqualTo extends ComparisonPredicate {
 
     public EqualTo(Expression left, Expression right) {
-        super(NodeType.EQUAL_TO, left, right);
+        super(ExpressionType.EQUAL_TO, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
index e56103d956..6f84907758 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Expression.java
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
 import org.apache.doris.nereids.trees.AbstractTreeNode;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
 
@@ -34,10 +33,16 @@ import java.util.Objects;
  */
 public abstract class Expression extends AbstractTreeNode<Expression> {
 
+    protected final ExpressionType type;
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    public Expression(NodeType type, Expression... children) {
-        super(type, children);
+    public Expression(ExpressionType type, Expression... children) {
+        super(children);
+        this.type = Objects.requireNonNull(type, "type can not be null");
+    }
+
+    public ExpressionType getType() {
+        return type;
     }
 
     public DataType getDataType() throws UnboundException {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionType.java
similarity index 82%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionType.java
index 9cd79db55c..5387203243 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/NodeType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/ExpressionType.java
@@ -15,20 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.trees;
+package org.apache.doris.nereids.trees.expressions;
 
 /**
- * Types for all TreeNode in Nereids, include Plan and Expression.
+ * Types for all Expression in Nereids.
  */
-public enum NodeType {
-    // plan
-    LOGICAL,
-    PHYSICAL,
-    // group plan
-    GROUP,
-
-    // expressions
-    EXPRESSION,
+public enum ExpressionType {
     UNBOUND_FUNCTION,
     UNBOUND_ALIAS,
     UNBOUND_SLOT,
@@ -63,9 +55,5 @@ public enum NodeType {
     BITXOR,
     BITNOT,
     FACTORIAL,
-    FUNCTION_CALL,
-
-    // pattern
-    PATTERN
-    ;
+    FUNCTION_CALL
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
index 70a1c37643..bad29aae63 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThan.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -36,7 +35,7 @@ public class GreaterThan extends ComparisonPredicate {
      * @param right right child of greater than
      */
     public GreaterThan(Expression left, Expression right) {
-        super(NodeType.GREATER_THAN, left, right);
+        super(ExpressionType.GREATER_THAN, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
index fae5db7017..1b5ca42628 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/GreaterThanEqual.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -36,7 +35,7 @@ public class GreaterThanEqual extends ComparisonPredicate {
      * @param right right child of Greater Than And Equal
      */
     public GreaterThanEqual(Expression left, Expression right) {
-        super(NodeType.GREATER_THAN_EQUAL, left, right);
+        super(ExpressionType.GREATER_THAN_EQUAL, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
index 22ef01176c..af49c7dcd1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThan.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -36,7 +35,7 @@ public class LessThan extends ComparisonPredicate {
      * @param right right child of Less Than
      */
     public LessThan(Expression left, Expression right) {
-        super(NodeType.LESS_THAN, left, right);
+        super(ExpressionType.LESS_THAN, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
index b3fa4de423..3d994c1332 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/LessThanEqual.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -36,7 +35,7 @@ public class LessThanEqual extends ComparisonPredicate {
      * @param right right child of Less Than And Equal
      */
     public LessThanEqual(Expression left, Expression right) {
-        super(NodeType.LESS_THAN_EQUAL, left, right);
+        super(ExpressionType.LESS_THAN_EQUAL, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java
index f7c3bd2192..f7ea1bc94a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Like.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -31,7 +30,7 @@ import java.util.List;
 public class Like extends StringRegexPredicate {
 
     public Like(Expression left, Expression right) {
-        super(NodeType.LIKE, left, right);
+        super(ExpressionType.LIKE, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
index 55f8f5257e..0acefce225 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Literal.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -45,7 +44,7 @@ public class Literal extends Expression implements LeafExpression {
      * @param dataType logical data type in Nereids
      */
     public Literal(Object value, DataType dataType) {
-        super(NodeType.LITERAL);
+        super(ExpressionType.LITERAL);
         this.dataType = dataType;
         this.value = value;
     }
@@ -56,7 +55,7 @@ public class Literal extends Expression implements LeafExpression {
      * @param value real value stored in java object
      */
     public Literal(Object value) {
-        super(NodeType.LITERAL);
+        super(ExpressionType.LITERAL);
         this.value = value;
         if (value == null) {
             dataType = NullType.INSTANCE;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
index 8df2861bd9..4ab8221544 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NamedExpression.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 
 import org.apache.commons.collections.CollectionUtils;
 
@@ -29,7 +28,7 @@ import java.util.List;
  */
 public abstract class NamedExpression extends Expression {
 
-    public NamedExpression(NodeType type, Expression... children) {
+    public NamedExpression(ExpressionType type, Expression... children) {
         super(type, children);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
index 3b0d1973cc..8ca44bc9b2 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Not.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -32,7 +31,7 @@ import java.util.Objects;
 public class Not extends Expression implements UnaryExpression {
 
     public Not(Expression child) {
-        super(NodeType.NOT, child);
+        super(ExpressionType.NOT, child);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
index aa2c63bd63..8b43970674 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/NullSafeEqual.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -37,7 +36,7 @@ public class NullSafeEqual extends ComparisonPredicate {
      * @param right right child of Null Safe Equal
      */
     public NullSafeEqual(Expression left, Expression right) {
-        super(NodeType.NULL_SAFE_EQUAL, left, right);
+        super(ExpressionType.NULL_SAFE_EQUAL, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
index 67c805ecfd..489f37e50b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Or.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
-import org.apache.doris.nereids.trees.NodeType;
-
 import com.google.common.base.Preconditions;
 
 import java.util.List;
@@ -34,7 +32,7 @@ public class Or extends CompoundPredicate {
      * @param right right child of comparison predicate
      */
     public Or(Expression left, Expression right) {
-        super(NodeType.OR, left, right);
+        super(ExpressionType.OR, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Regexp.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Regexp.java
index 0f8cf5c814..8e7ffde84b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Regexp.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Regexp.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import com.google.common.base.Preconditions;
@@ -31,7 +30,7 @@ import java.util.List;
 public class Regexp extends StringRegexPredicate {
 
     public Regexp(Expression left, Expression right) {
-        super(NodeType.REGEXP, left, right);
+        super(ExpressionType.REGEXP, left, right);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
index f77f9cc2bf..7ba6802d56 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Slot.java
@@ -17,14 +17,12 @@
 
 package org.apache.doris.nereids.trees.expressions;
 
-import org.apache.doris.nereids.trees.NodeType;
-
 /**
  * Abstract class for all slot in expression.
  */
 public abstract class Slot extends NamedExpression implements LeafExpression {
 
-    public Slot(NodeType type) {
+    public Slot(ExpressionType type) {
         super(type);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
index a0561d194b..d57c723c64 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.catalog.Column;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DataType;
 
@@ -52,7 +51,7 @@ public class SlotReference extends Slot {
      * @param qualifier slot reference qualifier
      */
     public SlotReference(ExprId exprId, String name, DataType dataType, boolean nullable, List<String> qualifier) {
-        super(NodeType.SLOT_REFERENCE);
+        super(ExpressionType.SLOT_REFERENCE);
         this.exprId = exprId;
         this.name = name;
         this.dataType = dataType;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java
index 1fc2e485ee..742b2ab2fe 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/StringRegexPredicate.java
@@ -18,7 +18,6 @@
 package org.apache.doris.nereids.trees.expressions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.BooleanType;
 import org.apache.doris.nereids.types.DataType;
@@ -37,7 +36,7 @@ public abstract class StringRegexPredicate extends Expression implements BinaryE
      * @param left     left child of string regex
      * @param right    right child of string regex
      */
-    public StringRegexPredicate(NodeType nodeType, Expression left, Expression right) {
+    public StringRegexPredicate(ExpressionType nodeType, Expression left, Expression right) {
         super(nodeType, left, right);
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java
index 2571411cd5..ea9f9f1325 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/BoundFunction.java
@@ -18,8 +18,8 @@
 package org.apache.doris.nereids.trees.expressions.functions;
 
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 
 import java.util.List;
@@ -31,7 +31,7 @@ public abstract class BoundFunction extends Expression {
     private final String name;
 
     public BoundFunction(String name, Expression... arguments) {
-        super(NodeType.BOUND_FUNCTION, arguments);
+        super(ExpressionType.BOUND_FUNCTION, arguments);
         this.name = Objects.requireNonNull(name, "name can not be null");
     }
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
index 2bb4b9b2b9..cefbf5f941 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AbstractPlan.java
@@ -18,10 +18,8 @@
 package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.PlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.AbstractTreeNode;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.statistics.ExprStats;
 import org.apache.doris.statistics.StatisticalType;
 import org.apache.doris.statistics.StatsDeriveResult;
@@ -37,30 +35,34 @@ import java.util.Optional;
 /**
  * Abstract class for all concrete plan node.
  */
-public abstract class AbstractPlan<OP_TYPE extends PlanOperator>
-        extends AbstractTreeNode<Plan> implements Plan {
+public abstract class AbstractPlan extends AbstractTreeNode<Plan> implements Plan {
 
-    public final OP_TYPE operator;
     protected StatsDeriveResult statsDeriveResult;
     protected long limit;
 
+    protected final PlanType type;
     protected final LogicalProperties logicalProperties;
 
-    public AbstractPlan(NodeType type, OP_TYPE operator, LogicalProperties logicalProperties, Plan... children) {
-        this(type, operator, Optional.empty(), logicalProperties, children);
+    public AbstractPlan(PlanType type, Plan... children) {
+        this(type, Optional.empty(), Optional.empty(), children);
+    }
+
+    public AbstractPlan(PlanType type, Optional<LogicalProperties> optLogicalProperties, Plan... children) {
+        this(type, Optional.empty(), optLogicalProperties, children);
     }
 
     /** all parameter constructor. */
-    public AbstractPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
-                        LogicalProperties logicalProperties, Plan... children) {
-        super(type, groupExpression, children);
-        this.operator = Objects.requireNonNull(operator, "operator can not be null");
+    public AbstractPlan(PlanType type, Optional<GroupExpression> groupExpression,
+                        Optional<LogicalProperties> optLogicalProperties, Plan... children) {
+        super(groupExpression, children);
+        this.type = Objects.requireNonNull(type, "type can not be null");
+        LogicalProperties logicalProperties = optLogicalProperties.orElseGet(() -> computeLogicalProperties(children));
         this.logicalProperties = Objects.requireNonNull(logicalProperties, "logicalProperties can not be null");
     }
 
     @Override
-    public OP_TYPE getOperator() {
-        return operator;
+    public PlanType getType() {
+        return type;
     }
 
     /**
@@ -129,11 +131,6 @@ public abstract class AbstractPlan<OP_TYPE extends PlanOperator>
         return Collections.emptyList();
     }
 
-    @Override
-    public String toString() {
-        return operator.toString();
-    }
-
     @Override
     public boolean equals(Object o) {
         if (this == o) {
@@ -142,15 +139,14 @@ public abstract class AbstractPlan<OP_TYPE extends PlanOperator>
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        AbstractPlan<?> that = (AbstractPlan<?>) o;
+        AbstractPlan that = (AbstractPlan) o;
         return limit == that.limit
-                && Objects.equals(operator, that.operator)
                 && Objects.equals(statsDeriveResult, that.statsDeriveResult)
                 && Objects.equals(logicalProperties, that.logicalProperties);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(operator, statsDeriveResult, limit, logicalProperties);
+        return Objects.hash(statsDeriveResult, limit, logicalProperties);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/AggPhase.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AggPhase.java
similarity index 97%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/AggPhase.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AggPhase.java
index 7054799257..95f34e6a2e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/AggPhase.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/AggPhase.java
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans;
+package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.analysis.AggregateInfo;
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BinaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BinaryPlan.java
index 41e3c48201..11b6cd03d7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BinaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/BinaryPlan.java
@@ -17,7 +17,6 @@
 
 package org.apache.doris.nereids.trees.plans;
 
-import org.apache.doris.nereids.operators.plans.BinaryPlanOperator;
 import org.apache.doris.nereids.trees.BinaryNode;
 
 /**
@@ -25,7 +24,4 @@ import org.apache.doris.nereids.trees.BinaryNode;
  */
 public interface BinaryPlan<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
         extends Plan, BinaryNode<Plan, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
-
-    @Override
-    BinaryPlanOperator getOperator();
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java
index eea047fea1..a27a819d1d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/GroupPlan.java
@@ -19,14 +19,17 @@ package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.logical.GroupPlanOperator;
-import org.apache.doris.nereids.trees.NodeType;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalLeaf;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.statistics.ExprStats;
 import org.apache.doris.statistics.StatisticalType;
 import org.apache.doris.statistics.StatsDeriveResult;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.List;
 import java.util.Optional;
 
@@ -35,11 +38,11 @@ import java.util.Optional;
  * Used in {@link org.apache.doris.nereids.pattern.GroupExpressionMatching.GroupExpressionIterator},
  * as a place-holder when do match root.
  */
-public class GroupPlan extends LogicalLeafPlan<GroupPlanOperator> {
+public class GroupPlan extends LogicalLeaf {
     private final Group group;
 
     public GroupPlan(Group group) {
-        super(new GroupPlanOperator(), Optional.empty(), Optional.of(group.getLogicalProperties()));
+        super(PlanType.GROUP_PLAN, Optional.empty(), Optional.of(group.getLogicalProperties()));
         this.group = group;
     }
 
@@ -48,15 +51,15 @@ public class GroupPlan extends LogicalLeafPlan<GroupPlanOperator> {
         return Optional.empty();
     }
 
-    @Override
-    public NodeType getType() {
-        return NodeType.GROUP;
-    }
-
     public Group getGroup() {
         return group;
     }
 
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of();
+    }
+
     @Override
     public GroupPlan withOutput(List<Slot> output) {
         throw new IllegalStateException("GroupPlan can not invoke withOutput()");
@@ -69,36 +72,58 @@ public class GroupPlan extends LogicalLeafPlan<GroupPlanOperator> {
 
     @Override
     public List<StatsDeriveResult> getChildrenStats() {
-        throw new RuntimeException("GroupPlan can not invoke getChildrenStats()");
+        throw new IllegalStateException("GroupPlan can not invoke getChildrenStats()");
     }
 
     @Override
     public StatsDeriveResult getStatsDeriveResult() {
-        throw new RuntimeException("GroupPlan can not invoke getStatsDeriveResult()");
+        throw new IllegalStateException("GroupPlan can not invoke getStatsDeriveResult()");
     }
 
     @Override
     public StatisticalType getStatisticalType() {
-        throw new RuntimeException("GroupPlan can not invoke getStatisticalType()");
+        throw new IllegalStateException("GroupPlan can not invoke getStatisticalType()");
     }
 
     @Override
     public void setStatsDeriveResult(StatsDeriveResult result) {
-        throw new RuntimeException("GroupPlan can not invoke setStatsDeriveResult()");
+        throw new IllegalStateException("GroupPlan can not invoke setStatsDeriveResult()");
     }
 
     @Override
     public long getLimit() {
-        throw new RuntimeException("GroupPlan can not invoke getLimit()");
+        throw new IllegalStateException("GroupPlan can not invoke getLimit()");
     }
 
     @Override
     public List<? extends ExprStats> getConjuncts() {
-        throw new RuntimeException("GroupPlan can not invoke getConjuncts()");
+        throw new IllegalStateException("GroupPlan can not invoke getConjuncts()");
     }
 
     @Override
     public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        throw new RuntimeException("GroupPlan can not invoke withGroupExpression()");
+        throw new IllegalStateException("GroupPlan can not invoke withGroupExpression()");
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        throw new IllegalStateException("GroupPlan can not invoke withLogicalProperties()");
+    }
+
+    @Override
+    public List<Slot> computeOutput() {
+        throw new IllegalStateException("GroupPlan can not compute output."
+            + " You should invoke GroupPlan.getOutput()");
+    }
+
+    @Override
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        throw new IllegalStateException("GroupPlan can not compute logical properties."
+            + " You should invoke GroupPlan.getLogicalProperties()");
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitGroupPlan(this, context);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/JoinType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
similarity index 98%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/JoinType.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
index c4b4bcfd31..030482992a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/JoinType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/JoinType.java
@@ -15,7 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans;
+package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.analysis.JoinOperator;
 import org.apache.doris.common.AnalysisException;
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LeafPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LeafPlan.java
index 74df8c503c..f07bd6a556 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LeafPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/LeafPlan.java
@@ -17,19 +17,19 @@
 
 package org.apache.doris.nereids.trees.plans;
 
-import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
 import org.apache.doris.nereids.trees.LeafNode;
 
+import com.google.common.base.Preconditions;
+
+import java.util.List;
+
 /**
  * Abstract class for all plan node that have no child.
  */
 public interface LeafPlan extends Plan, LeafNode<Plan> {
-
-    @Override
-    LeafPlanOperator getOperator();
-
     @Override
-    default int arity() {
-        return 0;
+    default Plan withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.isEmpty());
+        return this;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
index 6ad14306d5..cf2f448dcf 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plan.java
@@ -18,10 +18,11 @@
 package org.apache.doris.nereids.trees.plans;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.PlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.TreeNode;
+import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 import org.apache.doris.statistics.PlanStats;
 
 import java.util.List;
@@ -32,15 +33,29 @@ import java.util.Optional;
  */
 public interface Plan extends TreeNode<Plan>, PlanStats {
 
-    PlanOperator getOperator();
+    PlanType getType();
+
+    <R, C> R accept(PlanVisitor<R, C> visitor, C context);
+
+    List<Expression> getExpressions();
 
     LogicalProperties getLogicalProperties();
 
+    default LogicalProperties computeLogicalProperties(Plan... inputs) {
+        throw new IllegalStateException("Not support compute logical properties for " + getClass().getName());
+    }
+
     List<Slot> getOutput();
 
+    default List<Slot> computeOutput(Plan... inputs) {
+        throw new IllegalStateException("Not support compute output for " + getClass().getName());
+    }
+
     String treeString();
 
-    Plan withOutput(List<Slot> output);
+    default Plan withOutput(List<Slot> output) {
+        return withLogicalProperties(Optional.of(getLogicalProperties().withOutput(output)));
+    }
 
     Plan withGroupExpression(Optional<GroupExpression> groupExpression);
 
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanOperatorVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanOperatorVisitor.java
deleted file mode 100644
index 06a2485795..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanOperatorVisitor.java
+++ /dev/null
@@ -1,93 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//  http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans;
-
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalRelation;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalAggregate;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalFilter;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHeapSort;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOlapScan;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalProject;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-/**
- * Base class for the processing of logical and physical plan.
- *
- * @param <R> Return type of each visit method.
- * @param <C> Context type.
- */
-public abstract class PlanOperatorVisitor<R, C> {
-
-    public abstract R visit(Plan plan, C context);
-
-    // *******************************
-    // Logical plans
-    // *******************************
-
-    public R visitLogicalRelation(LogicalLeafPlan<LogicalRelation> relation, C context) {
-        return visit(relation, context);
-    }
-
-    public R visitLogicalFilter(LogicalUnaryPlan<LogicalFilter, Plan> filter, C context) {
-        return visit(filter, context);
-    }
-
-    public R visitLogicalJoin(LogicalBinaryPlan<LogicalJoin, Plan, Plan> join, C context) {
-        return visit(join, context);
-    }
-
-    public R visitGroupPlan(GroupPlan groupPlan, C context) {
-        return visit(groupPlan, context);
-    }
-
-    // *******************************
-    // Physical plans
-    // *******************************
-
-    public R visitPhysicalAggregate(PhysicalUnaryPlan<PhysicalAggregate, Plan> agg, C context) {
-        return visit(agg, context);
-    }
-
-    public R visitPhysicalOlapScan(PhysicalLeafPlan<PhysicalOlapScan> olapScan, C context) {
-        return visit(olapScan, context);
-    }
-
-    public R visitPhysicalHeapSort(PhysicalUnaryPlan<PhysicalHeapSort, Plan> sort, C context) {
-        return visit(sort, context);
-    }
-
-    public R visitPhysicalHashJoin(PhysicalBinaryPlan<PhysicalHashJoin, Plan, Plan> hashJoin, C context) {
-        return visit(hashJoin, context);
-    }
-
-    public R visitPhysicalProject(PhysicalUnaryPlan<PhysicalProject, Plan> project, C context) {
-        return visit(project, context);
-    }
-
-    public R visitPhysicalFilter(PhysicalUnaryPlan<PhysicalFilter, Plan> filter, C context) {
-        return visit(filter, context);
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
similarity index 66%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index a7f72ca3b0..a7feb3d3dc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/OperatorType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -15,19 +15,12 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators;
+package org.apache.doris.nereids.trees.plans;
 
 /**
- * Types for all Operator in Nereids, include PlanOperator and Pattern placeholder type.
- * There are four types of Operator for pattern matching:
- * 1. ANY: match any operator
- * 2. MULTI: match multiple operators
- * 3. FIXED: the leaf node of pattern tree, which can be matched by a single operator
- * but this operator cannot be used in rules
- * 4. MULTI_FIXED: the leaf node of pattern tree, which can be matched by multiple operators,
- * but these operators cannot be used in rules
+ * Types for all Plan in Nereids.
  */
-public enum OperatorType {
+public enum PlanType {
     UNKNOWN,
 
     // logical plan
@@ -36,8 +29,9 @@ public enum OperatorType {
     LOGICAL_PROJECT,
     LOGICAL_FILTER,
     LOGICAL_JOIN,
-    LOGICAL_AGGREGATION,
+    LOGICAL_AGGREGATE,
     LOGICAL_SORT,
+    LOGICAL_OLAP_SCAN,
     GROUP_PLAN,
 
     // physical plan
@@ -45,7 +39,7 @@ public enum OperatorType {
     PHYSICAL_PROJECT,
     PHYSICAL_FILTER,
     PHYSICAL_BROADCAST_HASH_JOIN,
-    PHYSICAL_AGGREGATION,
+    PHYSICAL_AGGREGATE,
     PHYSICAL_SORT,
     PHYSICAL_HASH_JOIN,
     PHYSICAL_EXCHANGE,
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plans.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plans.java
deleted file mode 100644
index 73447f2ae3..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/Plans.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans;
-
-import org.apache.doris.nereids.operators.plans.logical.LogicalBinaryOperator;
-import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
-import org.apache.doris.nereids.operators.plans.logical.LogicalUnaryOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalBinaryOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalLeafOperator;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalUnaryOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalBinaryPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
-
-/**
- * An interface provided some builder of Plan.
- * Child Interface(PlanRuleFactory) can use to build some plan for transform rule.
- * You can simply use the override plan function to build a plan by the operator type.
- */
-public interface Plans {
-    default <OP_TYPE extends LogicalLeafOperator> LogicalLeafPlan<OP_TYPE> plan(OP_TYPE op) {
-        return new LogicalLeafPlan(op);
-    }
-
-    default <OP_TYPE extends LogicalUnaryOperator, CHILD_TYPE extends Plan> LogicalUnaryPlan<OP_TYPE, CHILD_TYPE>
-            plan(OP_TYPE op, CHILD_TYPE child) {
-        return new LogicalUnaryPlan(op, child);
-    }
-
-    default <OP_TYPE extends LogicalBinaryOperator, LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
-            LogicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>
-            plan(OP_TYPE op, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        return new LogicalBinaryPlan(op, leftChild, rightChild);
-    }
-
-    default <OP_TYPE extends PhysicalLeafOperator> PhysicalLeafPlan<OP_TYPE>
-            plan(OP_TYPE op, LogicalProperties logicalProperties) {
-        return new PhysicalLeafPlan(op, logicalProperties);
-    }
-
-    default <OP_TYPE extends PhysicalUnaryOperator, CHILD_TYPE extends Plan> PhysicalUnaryPlan<OP_TYPE, CHILD_TYPE>
-            plan(OP_TYPE op, LogicalProperties logicalProperties, CHILD_TYPE child) {
-        return new PhysicalUnaryPlan(op, logicalProperties, child);
-    }
-
-    default <OP_TYPE extends PhysicalBinaryOperator, LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
-            PhysicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE>
-            plan(OP_TYPE op, LogicalProperties logicalProperties,
-                LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        return new PhysicalBinaryPlan(op, logicalProperties, leftChild, rightChild);
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/UnaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/UnaryPlan.java
index af03827ae0..a392a5ff1a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/UnaryPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/UnaryPlan.java
@@ -17,7 +17,6 @@
 
 package org.apache.doris.nereids.trees.plans;
 
-import org.apache.doris.nereids.operators.plans.UnaryPlanOperator;
 import org.apache.doris.nereids.trees.UnaryNode;
 
 /**
@@ -25,7 +24,4 @@ import org.apache.doris.nereids.trees.UnaryNode;
  */
 public interface UnaryPlan<CHILD_TYPE extends Plan>
         extends Plan, UnaryNode<Plan, CHILD_TYPE> {
-
-    @Override
-    UnaryPlanOperator getOperator();
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
index f790829bba..091f7154b6 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/AbstractLogicalPlan.java
@@ -18,12 +18,11 @@
 package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.AbstractPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.List;
 import java.util.Optional;
@@ -31,22 +30,19 @@ import java.util.Optional;
 /**
  * Abstract class for all concrete logical plan.
  */
-public abstract class AbstractLogicalPlan<OP_TYPE extends LogicalOperator>
-        extends AbstractPlan<OP_TYPE> implements LogicalPlan {
+public abstract class AbstractLogicalPlan extends AbstractPlan implements LogicalPlan {
 
-    public AbstractLogicalPlan(NodeType type, OP_TYPE operator, Plan... children) {
-        super(type, operator, operator.computeLogicalProperties(children), children);
+    public AbstractLogicalPlan(PlanType type, Plan... children) {
+        super(type, children);
     }
 
-    public AbstractLogicalPlan(NodeType type, OP_TYPE operator,
-                               Optional<LogicalProperties> logicalProperties, Plan... children) {
-        super(type, operator, logicalProperties.orElseGet(() -> operator.computeLogicalProperties(children)), children);
+    public AbstractLogicalPlan(PlanType type, Optional<LogicalProperties> logicalProperties, Plan... children) {
+        super(type, logicalProperties, children);
     }
 
-    public AbstractLogicalPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
+    public AbstractLogicalPlan(PlanType type, Optional<GroupExpression> groupExpression,
                                Optional<LogicalProperties> logicalProperties, Plan... children) {
-        super(type, operator, groupExpression,
-                logicalProperties.orElseGet(() -> operator.computeLogicalProperties(children)), children);
+        super(type, groupExpression, logicalProperties, children);
     }
 
     @Override
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
similarity index 60%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
index 58ce3d1029..186f000ef1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalAggregate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalAggregate.java
@@ -15,23 +15,28 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.AggPhase;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
- * Logical Aggregation plan operator.
+ * Logical Aggregate plan.
  * <p>
  * eg:select a, sum(b), c from table group by a, c;
  * groupByExprList: Column field after group by. eg: a, c;
@@ -43,7 +48,7 @@ import java.util.Objects;
  * Note: In general, the output of agg is a subset of the group by column plus aggregate column.
  * In special cases. this relationship does not hold. for example, select k1+1, sum(v1) from table group by k1.
  */
-public class LogicalAggregate extends LogicalUnaryOperator {
+public class LogicalAggregate<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
 
     private final boolean disassembled;
     private final List<Expression> groupByExpressionList;
@@ -54,24 +59,37 @@ public class LogicalAggregate extends LogicalUnaryOperator {
     /**
      * Desc: Constructor for LogicalAggregate.
      */
-    public LogicalAggregate(List<Expression> groupByExpressionList, List<NamedExpression> outputExpressionList) {
-        this(groupByExpressionList, outputExpressionList, false, AggPhase.GLOBAL);
+    public LogicalAggregate(List<Expression> groupByExpressionList, List<NamedExpression> outputExpressionList,
+                            CHILD_TYPE child) {
+        this(groupByExpressionList, outputExpressionList, false, AggPhase.GLOBAL, child);
     }
 
     public LogicalAggregate(List<Expression> groupByExpressionList,
             List<NamedExpression> outputExpressionList,
-            boolean disassembled, AggPhase aggPhase) {
-        this(groupByExpressionList, outputExpressionList, null, disassembled, aggPhase);
+            boolean disassembled, AggPhase aggPhase, CHILD_TYPE child) {
+        this(groupByExpressionList, outputExpressionList, null, disassembled, aggPhase, child);
+    }
+
+    public LogicalAggregate(List<Expression> groupByExpressionList,
+                            List<NamedExpression> outputExpressionList,
+                            List<Expression> partitionExprList,
+                            boolean disassembled, AggPhase aggPhase,
+                            CHILD_TYPE child) {
+        this(groupByExpressionList, outputExpressionList, partitionExprList, disassembled, aggPhase,
+                Optional.empty(), Optional.empty(), child);
     }
 
     /**
      * Whole parameters constructor for LogicalAggregate.
      */
     public LogicalAggregate(List<Expression> groupByExpressionList,
-            List<NamedExpression> outputExpressionList,
-            List<Expression> partitionExprList,
-            boolean disassembled, AggPhase aggPhase) {
-        super(OperatorType.LOGICAL_AGGREGATION);
+                            List<NamedExpression> outputExpressionList,
+                            List<Expression> partitionExprList,
+                            boolean disassembled, AggPhase aggPhase,
+                            Optional<GroupExpression> groupExpression,
+                            Optional<LogicalProperties> logicalProperties,
+                            CHILD_TYPE child) {
+        super(PlanType.LOGICAL_AGGREGATE, groupExpression, logicalProperties, child);
         this.groupByExpressionList = groupByExpressionList;
         this.outputExpressionList = outputExpressionList;
         this.partitionExprList = partitionExprList;
@@ -109,6 +127,11 @@ public class LogicalAggregate extends LogicalUnaryOperator {
                 .collect(ImmutableList.toImmutableList());
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalAggregate((LogicalAggregate<Plan>) this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         return new ImmutableList.Builder<Expression>()
@@ -122,7 +145,7 @@ public class LogicalAggregate extends LogicalUnaryOperator {
     }
 
     /**
-     * Determine the equality with another operator
+     * Determine the equality with another plan
      */
     public boolean equals(Object o) {
         if (this == o) {
@@ -143,8 +166,30 @@ public class LogicalAggregate extends LogicalUnaryOperator {
         return Objects.hash(groupByExpressionList, outputExpressionList, partitionExprList, aggPhase);
     }
 
-    public LogicalAggregate withGroupByAndOutput(List<Expression> groupByExprList,
-            List<NamedExpression> outputExpressionList) {
-        return new LogicalAggregate(groupByExprList, outputExpressionList, partitionExprList, disassembled, aggPhase);
+    @Override
+    public LogicalAggregate<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new LogicalAggregate(groupByExpressionList, outputExpressionList,
+            partitionExprList, disassembled, aggPhase, children.get(0));
+    }
+
+    @Override
+    public LogicalAggregate<Plan> withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalAggregate(groupByExpressionList, outputExpressionList,
+            partitionExprList, disassembled, aggPhase, groupExpression,
+            Optional.of(logicalProperties), children.get(0));
+    }
+
+    @Override
+    public LogicalAggregate<Plan> withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalAggregate(groupByExpressionList, outputExpressionList,
+            partitionExprList, disassembled, aggPhase, Optional.empty(),
+            logicalProperties, children.get(0));
+    }
+
+    public LogicalAggregate<Plan> withGroupByAndOutput(List<Expression> groupByExprList,
+                                                 List<NamedExpression> outputExpressionList) {
+        return new LogicalAggregate(groupByExprList, outputExpressionList,
+            partitionExprList, disassembled, aggPhase, child());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinary.java
similarity index 55%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinary.java
index 3386341ea0..e747457b08 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalBinaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinary.java
@@ -15,17 +15,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.BinaryPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
+import org.apache.doris.nereids.trees.plans.BinaryPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalBinaryPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import com.google.common.base.Preconditions;
 
@@ -33,28 +30,34 @@ import java.util.List;
 import java.util.Optional;
 
 /**
- * Abstract class for all logical binary operator that have two inputs.
+ * Abstract class for all logical plan that have two children.
  */
-public abstract class LogicalBinaryOperator extends AbstractOperator
-        implements LogicalOperator, BinaryPlanOperator {
+public abstract class LogicalBinary<
+            LEFT_CHILD_TYPE extends Plan,
+            RIGHT_CHILD_TYPE extends Plan>
+        extends AbstractLogicalPlan
+        implements BinaryPlan<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
+
+    public LogicalBinary(PlanType type, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(type, Optional.empty(), leftChild, rightChild);
+    }
 
-    public LogicalBinaryOperator(OperatorType type) {
-        super(type);
+    public LogicalBinary(PlanType type, Optional<LogicalProperties> logicalProperties,
+                             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(type, logicalProperties, leftChild, rightChild);
     }
 
-    @Override
-    public final LogicalProperties computeLogicalProperties(Plan... inputs) {
-        Preconditions.checkArgument(inputs.length == 2);
-        return new LogicalProperties(() -> computeOutput(inputs[0], inputs[1]));
+    public LogicalBinary(PlanType type, Optional<GroupExpression> groupExpression,
+                             Optional<LogicalProperties> logicalProperties, LEFT_CHILD_TYPE leftChild,
+                             RIGHT_CHILD_TYPE rightChild) {
+        super(type, groupExpression, logicalProperties, leftChild, rightChild);
     }
 
     public abstract List<Slot> computeOutput(Plan left, Plan right);
 
     @Override
-    public LogicalBinaryPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new LogicalBinaryPlan(this, Optional.of(groupExpression), Optional.of(logicalProperties),
-                new GroupPlan(groupExpression.child(0)), new GroupPlan(groupExpression.child(1))
-        );
+    public final LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 2);
+        return new LogicalProperties(() -> computeOutput(inputs[0], inputs[1]));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java
deleted file mode 100644
index 8f845df0c5..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalBinaryPlan.java
+++ /dev/null
@@ -1,76 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.logical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.logical.LogicalBinaryOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.BinaryPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all logical plan that have two children.
- */
-public class LogicalBinaryPlan<
-            OP_TYPE extends LogicalBinaryOperator,
-            LEFT_CHILD_TYPE extends Plan,
-            RIGHT_CHILD_TYPE extends Plan>
-        extends AbstractLogicalPlan<OP_TYPE>
-        implements BinaryPlan<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
-
-    public LogicalBinaryPlan(OP_TYPE operator, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.LOGICAL, operator, Optional.empty(), leftChild, rightChild);
-    }
-
-    public LogicalBinaryPlan(OP_TYPE operator, Optional<LogicalProperties> logicalProperties,
-                             LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.LOGICAL, operator, logicalProperties, leftChild, rightChild);
-    }
-
-    public LogicalBinaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-             Optional<LogicalProperties> logicalProperties, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.LOGICAL, operator, groupExpression, logicalProperties, leftChild, rightChild);
-    }
-
-    @Override
-    public LogicalBinaryPlan<OP_TYPE, Plan, Plan> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 2);
-        return new LogicalBinaryPlan(operator, Optional.empty(), children.get(0), children.get(1));
-    }
-
-    @Override
-    public LogicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> withOutput(List<Slot> output) {
-        return new LogicalBinaryPlan<>(operator, Optional.of(logicalProperties.withOutput(output)), left(), right());
-    }
-
-    public LogicalBinaryPlan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new LogicalBinaryPlan<>(operator, groupExpression, Optional.of(logicalProperties), left(), right());
-    }
-
-    @Override
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new LogicalBinaryPlan<>(operator, groupExpression, logicalProperties, left(), right());
-    }
-}
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
new file mode 100644
index 0000000000..c677227667
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalFilter.java
@@ -0,0 +1,91 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.logical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Logical filter plan.
+ */
+public class LogicalFilter<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
+    private final Expression predicates;
+
+
+    public LogicalFilter(Expression predicates, CHILD_TYPE child) {
+        this(predicates, Optional.empty(), Optional.empty(), child);
+    }
+
+    public LogicalFilter(Expression predicates, Optional<GroupExpression> groupExpression,
+                         Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+        super(PlanType.LOGICAL_FILTER, groupExpression, logicalProperties, child);
+        this.predicates = Objects.requireNonNull(predicates, "predicates can not be null");
+    }
+
+    public Expression getPredicates() {
+        return predicates;
+    }
+
+    @Override
+    public List<Slot> computeOutput(Plan input) {
+        return input.getOutput();
+    }
+
+    @Override
+    public String toString() {
+        return "LogicalFilter (" + predicates + ")";
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalFilter((LogicalFilter<Plan>) this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of(predicates);
+    }
+
+    @Override
+    public LogicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new LogicalFilter<>(predicates, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalFilter<>(predicates, groupExpression, Optional.of(logicalProperties), child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalFilter<>(predicates, Optional.empty(), logicalProperties, child());
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
similarity index 65%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
index e5e61e7a15..2391baaa99 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalJoin.java
@@ -15,15 +15,19 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.JoinType;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.rules.exploration.JoinReorderContext;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
@@ -32,9 +36,10 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 
 /**
- * Logical join plan operator.
+ * Logical join plan.
  */
-public class LogicalJoin extends LogicalBinaryOperator {
+public class LogicalJoin<LEFT_CHILD_TYPE extends Plan, RIGHT_CHILD_TYPE extends Plan>
+        extends LogicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
 
     private final JoinType joinType;
     private final Optional<Expression> condition;
@@ -47,8 +52,13 @@ public class LogicalJoin extends LogicalBinaryOperator {
      *
      * @param joinType logical type for join
      */
-    public LogicalJoin(JoinType joinType) {
-        this(joinType, Optional.empty());
+    public LogicalJoin(JoinType joinType, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        this(joinType, Optional.empty(), Optional.empty(), Optional.empty(), leftChild, rightChild);
+    }
+
+    public LogicalJoin(JoinType joinType, Optional<Expression> condition,
+                       LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        this(joinType, condition, Optional.empty(), Optional.empty(), leftChild, rightChild);
     }
 
     /**
@@ -57,8 +67,10 @@ public class LogicalJoin extends LogicalBinaryOperator {
      * @param joinType logical type for join
      * @param condition on clause for join node
      */
-    public LogicalJoin(JoinType joinType, Optional<Expression> condition) {
-        super(OperatorType.LOGICAL_JOIN);
+    public LogicalJoin(JoinType joinType, Optional<Expression> condition,
+                       Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties,
+                       LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(PlanType.LOGICAL_JOIN, groupExpression, logicalProperties, leftChild, rightChild);
         this.joinType = Objects.requireNonNull(joinType, "joinType can not be null");
         this.condition = Objects.requireNonNull(condition, "condition can not be null");
     }
@@ -117,6 +129,11 @@ public class LogicalJoin extends LogicalBinaryOperator {
         return sb.append(")").toString();
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalJoin((LogicalJoin<Plan, Plan>) this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         return condition.<List<Expression>>map(ImmutableList::of).orElseGet(ImmutableList::of);
@@ -125,4 +142,21 @@ public class LogicalJoin extends LogicalBinaryOperator {
     public JoinReorderContext getJoinReorderContext() {
         return joinReorderContext;
     }
+
+    @Override
+    public LogicalBinary<Plan, Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new LogicalJoin<>(joinType, condition, children.get(0), children.get(1));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalJoin<>(joinType, condition, groupExpression,
+            Optional.of(logicalProperties), left(), right());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalJoin<>(joinType, condition, Optional.empty(), logicalProperties, left(), right());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeaf.java
similarity index 62%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeaf.java
index 8c414e869f..15cd6780d1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalLeafOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeaf.java
@@ -15,16 +15,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.plans.LeafPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import com.google.common.base.Preconditions;
 
@@ -32,26 +30,29 @@ import java.util.List;
 import java.util.Optional;
 
 /**
- * Abstract class for all logical operator that have no input.
+ * Abstract class for all logical plan that have no child.
  */
-public abstract class LogicalLeafOperator extends AbstractOperator
-        implements LogicalOperator, LeafPlanOperator {
+public abstract class LogicalLeaf extends AbstractLogicalPlan implements LeafPlan {
 
-    public LogicalLeafOperator(OperatorType type) {
-        super(type);
+    public LogicalLeaf(PlanType nodeType) {
+        super(nodeType);
     }
 
-    @Override
-    public LogicalProperties computeLogicalProperties(Plan... inputs) {
-        Preconditions.checkArgument(inputs.length == 0);
-        return new LogicalProperties(() -> computeOutput());
+    public LogicalLeaf(PlanType nodeType, Optional<LogicalProperties> logicalProperties) {
+        super(nodeType, logicalProperties);
+    }
+
+    public LogicalLeaf(PlanType nodeType, Optional<GroupExpression> groupExpression,
+                           Optional<LogicalProperties> logicalProperties) {
+        super(nodeType, groupExpression, logicalProperties);
     }
 
     public abstract List<Slot> computeOutput();
 
+
     @Override
-    public LogicalLeafPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new LogicalLeafPlan(this, Optional.of(groupExpression), Optional.of(logicalProperties));
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 0);
+        return new LogicalProperties(() -> computeOutput());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java
deleted file mode 100644
index 8b04f1fc37..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalLeafPlan.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.logical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.logical.LogicalLeafOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.LeafPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all logical plan that have no child.
- */
-public class LogicalLeafPlan<OP_TYPE extends LogicalLeafOperator>
-        extends AbstractLogicalPlan<OP_TYPE>
-        implements LeafPlan {
-
-    public LogicalLeafPlan(OP_TYPE operator) {
-        super(NodeType.LOGICAL, operator);
-    }
-
-    public LogicalLeafPlan(OP_TYPE operator, Optional<LogicalProperties> logicalProperties) {
-        super(NodeType.LOGICAL, operator, logicalProperties);
-    }
-
-    public LogicalLeafPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-                           Optional<LogicalProperties> logicalProperties) {
-        super(NodeType.LOGICAL, operator, groupExpression, logicalProperties);
-    }
-
-    @Override
-    public LogicalLeafPlan<OP_TYPE> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 0);
-        return new LogicalLeafPlan(operator, Optional.empty());
-    }
-
-    @Override
-    public LogicalLeafPlan<OP_TYPE> withOutput(List<Slot> output) {
-        return new LogicalLeafPlan<>(operator, Optional.of(logicalProperties.withOutput(output)));
-    }
-
-    @Override
-    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new LogicalLeafPlan<>(operator, groupExpression, Optional.of(logicalProperties));
-    }
-
-    @Override
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new LogicalLeafPlan<>(operator, groupExpression, logicalProperties);
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOlapScan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
similarity index 51%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOlapScan.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
index f10977966d..ed98dca195 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOlapScan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalOlapScan.java
@@ -15,31 +15,57 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.catalog.Table;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
- * Logical OlapScan operator.
+ * Logical OlapScan.
  */
 public class LogicalOlapScan extends LogicalRelation  {
 
+    public LogicalOlapScan(Table table, List<String> qualifier) {
+        this(table, qualifier, Optional.empty(), Optional.empty());
+    }
+
     /**
      * Constructor for LogicalOlapScan.
      *
      * @param table Doris table
      * @param qualifier qualified relation name
      */
-    public LogicalOlapScan(Table table, List<String> qualifier) {
-        super(table, qualifier);
+    public LogicalOlapScan(Table table, List<String> qualifier,
+                           Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties) {
+        super(PlanType.LOGICAL_OLAP_SCAN, table, qualifier, groupExpression, logicalProperties);
     }
 
     @Override
     public String toString() {
         return "ScanOlapTable([" + StringUtils.join(qualifier, ".") + "." + table.getName() + "])";
     }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalOlapScan(table, qualifier, groupExpression, Optional.of(logicalProperties));
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalOlapScan(table, qualifier, Optional.empty(), logicalProperties);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalOlapScan(this, context);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
index 5c65cd694f..9cb2fcad50 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalPlan.java
@@ -17,10 +17,8 @@
 
 package org.apache.doris.nereids.trees.plans.logical;
 
-import org.apache.doris.nereids.operators.plans.logical.LogicalOperator;
 import org.apache.doris.nereids.trees.plans.Plan;
 
-import java.util.List;
 import java.util.Optional;
 import java.util.function.BiFunction;
 import java.util.function.Supplier;
@@ -30,12 +28,6 @@ import java.util.function.Supplier;
  */
 public interface LogicalPlan extends Plan {
 
-    @Override
-    LogicalOperator getOperator();
-
-    @Override
-    LogicalPlan withChildren(List<Plan> children);
-
     /**
      * Map a [[LogicalPlan]] to another [[LogicalPlan]] if the passed context exists using the
      * passed function. The original plan is returned when the context does not exist.
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
similarity index 59%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
index a13d2e2785..6d9e75bffc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalProject.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalProject.java
@@ -15,34 +15,44 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
-import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
- * Logical project plan operator.
+ * Logical project plan.
  */
-public class LogicalProject extends LogicalUnaryOperator {
+public class LogicalProject<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
 
     private final List<NamedExpression> projects;
 
+    public LogicalProject(List<NamedExpression> projects, CHILD_TYPE child) {
+        this(projects, Optional.empty(), Optional.empty(), child);
+    }
+
     /**
      * Constructor for LogicalProject.
      *
      * @param projects project list
      */
-    public LogicalProject(List<NamedExpression> projects) {
-        super(OperatorType.LOGICAL_PROJECT);
+    public LogicalProject(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
+                          Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+        super(PlanType.LOGICAL_PROJECT, groupExpression, logicalProperties, child);
         this.projects = ImmutableList.copyOf(Objects.requireNonNull(projects, "projects can not be null"));
     }
 
@@ -67,6 +77,11 @@ public class LogicalProject extends LogicalUnaryOperator {
         return "LogicalProject (" + StringUtils.join(projects, ", ") + ")";
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalProject((LogicalProject<Plan>) this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         return new ImmutableList.Builder<Expression>().addAll(projects).build();
@@ -88,4 +103,20 @@ public class LogicalProject extends LogicalUnaryOperator {
     public int hashCode() {
         return Objects.hash(projects);
     }
+
+    @Override
+    public LogicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new LogicalProject<>(projects, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalProject<>(projects, groupExpression, Optional.of(logicalProperties), child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalProject<>(projects, Optional.empty(), logicalProperties, child());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRelation.java
similarity index 69%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRelation.java
index c7f3363b5b..f551ff959e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalRelation.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalRelation.java
@@ -15,36 +15,45 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.catalog.Table;
-import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
 import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
- * Logical relation plan operator.
+ * Logical relation plan.
  */
-public abstract class LogicalRelation extends LogicalLeafOperator {
+public abstract class LogicalRelation extends LogicalLeaf {
 
     protected final Table table;
     protected final List<String> qualifier;
 
+    public LogicalRelation(PlanType type, Table table, List<String> qualifier) {
+        this(type, table, qualifier, Optional.empty(), Optional.empty());
+    }
+
     /**
      * Constructor for LogicalRelationPlan.
      *
      * @param table Doris table
      * @param qualifier qualified relation name
      */
-    public LogicalRelation(Table table, List<String> qualifier) {
-        super(OperatorType.LOGICAL_BOUND_RELATION);
+    public LogicalRelation(PlanType type, Table table, List<String> qualifier,
+                           Optional<GroupExpression> groupExpression, Optional<LogicalProperties> logicalProperties) {
+        super(type, groupExpression, logicalProperties);
         this.table = Objects.requireNonNull(table, "table can not be null");
         this.qualifier = ImmutableList.copyOf(Objects.requireNonNull(qualifier, "qualifier can not be null"));
     }
@@ -70,6 +79,11 @@ public abstract class LogicalRelation extends LogicalLeafOperator {
                 .collect(ImmutableList.toImmutableList());
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalRelation(this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         return ImmutableList.of();
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSort.java
similarity index 58%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSort.java
index 16a8e396f4..e43c7bb3c5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalSort.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSort.java
@@ -15,39 +15,50 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
-import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.OrderKey;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
- * Logical Sort plan operator.
+ * Logical Sort plan.
  * <p>
  * eg: select * from table order by a, b desc;
  * orderKeys: list of column information after order by. eg:[a, asc],[b, desc].
  * OrderKey: Contains order expression information and sorting method. Default is ascending.
  */
-public class LogicalSort extends LogicalUnaryOperator {
+public class LogicalSort<CHILD_TYPE extends Plan> extends LogicalUnary<CHILD_TYPE> {
 
     // Default offset is 0.
     private int offset = 0;
 
     private final List<OrderKey> orderKeys;
 
+
+    public LogicalSort(List<OrderKey> orderKeys, CHILD_TYPE child) {
+        this(orderKeys, Optional.empty(), Optional.empty(), child);
+    }
+
     /**
      * Constructor for LogicalSort.
      */
-    public LogicalSort(List<OrderKey> orderKeys) {
-        super(OperatorType.LOGICAL_SORT);
+    public LogicalSort(List<OrderKey> orderKeys, Optional<GroupExpression> groupExpression,
+                       Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+        super(PlanType.LOGICAL_SORT, groupExpression, logicalProperties, child);
         this.orderKeys = Objects.requireNonNull(orderKeys, "orderKeys can not be null");
     }
 
@@ -73,10 +84,31 @@ public class LogicalSort extends LogicalUnaryOperator {
         return "Sort (" + StringUtils.join(orderKeys, ", ") + ")";
     }
 
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitLogicalSort((LogicalSort<Plan>) this, context);
+    }
+
     @Override
     public List<Expression> getExpressions() {
         return orderKeys.stream()
                 .map(OrderKey::getExpr)
                 .collect(ImmutableList.toImmutableList());
     }
+
+    @Override
+    public LogicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new LogicalSort<>(orderKeys, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new LogicalSort<>(orderKeys, groupExpression, Optional.of(logicalProperties), child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new LogicalSort<>(orderKeys, Optional.empty(), logicalProperties, child());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnary.java
similarity index 59%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnary.java
index dcf15aa7f0..a0b2ce9423 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalUnaryOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnary.java
@@ -15,17 +15,14 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.logical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.UnaryPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.UnaryPlan;
 
 import com.google.common.base.Preconditions;
 
@@ -33,28 +30,30 @@ import java.util.List;
 import java.util.Optional;
 
 /**
- * Abstract class for all logical operator that have one input.
+ * Abstract class for all logical plan that have one child.
  */
-public abstract class LogicalUnaryOperator extends AbstractOperator
-        implements LogicalOperator, UnaryPlanOperator {
+public abstract class LogicalUnary<CHILD_TYPE extends Plan>
+        extends AbstractLogicalPlan
+        implements UnaryPlan<CHILD_TYPE> {
 
-    public LogicalUnaryOperator(OperatorType type) {
-        super(type);
+    public LogicalUnary(PlanType type, CHILD_TYPE child) {
+        super(type, child);
     }
 
-    @Override
-    public LogicalProperties computeLogicalProperties(Plan... inputs) {
-        Preconditions.checkArgument(inputs.length == 1);
-        return new LogicalProperties(() -> computeOutput(inputs[0]));
+    public LogicalUnary(PlanType type, Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+        super(type, logicalProperties, child);
+    }
+
+    public LogicalUnary(PlanType type, Optional<GroupExpression> groupExpression,
+                            Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
+        super(type, groupExpression, logicalProperties, child);
     }
 
     public abstract List<Slot> computeOutput(Plan input);
 
     @Override
-    public LogicalUnaryPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new LogicalUnaryPlan(this, Optional.of(groupExpression),
-            Optional.of(logicalProperties), new GroupPlan(groupExpression.child(0))
-        );
+    public LogicalProperties computeLogicalProperties(Plan... inputs) {
+        Preconditions.checkArgument(inputs.length == 1);
+        return new LogicalProperties(() -> computeOutput(inputs[0]));
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java
deleted file mode 100644
index df86bdbad9..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalUnaryPlan.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.logical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.logical.LogicalUnaryOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.UnaryPlan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all logical plan that have one child.
- */
-public class LogicalUnaryPlan<OP_TYPE extends LogicalUnaryOperator, CHILD_TYPE extends Plan>
-        extends AbstractLogicalPlan<OP_TYPE>
-        implements UnaryPlan<CHILD_TYPE> {
-
-    public LogicalUnaryPlan(OP_TYPE operator, CHILD_TYPE child) {
-        super(NodeType.LOGICAL, operator, child);
-    }
-
-    public LogicalUnaryPlan(OP_TYPE operator, Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
-        super(NodeType.LOGICAL, operator, logicalProperties, child);
-    }
-
-    public LogicalUnaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-                            Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
-        super(NodeType.LOGICAL, operator, groupExpression, logicalProperties, child);
-    }
-
-    @Override
-    public LogicalUnaryPlan<OP_TYPE, Plan> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 1);
-        return new LogicalUnaryPlan(operator, Optional.empty(), children.get(0));
-    }
-
-    @Override
-    public LogicalUnaryPlan<OP_TYPE, CHILD_TYPE> withOutput(List<Slot> output) {
-        return new LogicalUnaryPlan<>(operator, Optional.of(logicalProperties.withOutput(output)), child());
-    }
-
-    @Override
-    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new LogicalUnaryPlan<>(operator, groupExpression, Optional.of(logicalProperties), child());
-    }
-
-    @Override
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new LogicalUnaryPlan<>(operator, groupExpression, logicalProperties, child());
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
index 409bb57881..f60c8b6591 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/AbstractPhysicalPlan.java
@@ -18,13 +18,12 @@
 package org.apache.doris.nereids.trees.plans.physical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.AbstractPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
 
 import java.util.List;
 import java.util.Optional;
@@ -32,34 +31,30 @@ import java.util.Optional;
 /**
  * Abstract class for all concrete physical plan.
  */
-public abstract class AbstractPhysicalPlan<OP_TYPE extends PhysicalOperator>
-        extends AbstractPlan<OP_TYPE>
-        implements PhysicalPlan {
+public abstract class AbstractPhysicalPlan extends AbstractPlan implements PhysicalPlan {
 
     protected final PhysicalProperties physicalProperties;
 
     /**
      * create physical plan by op, logicalProperties and children.
      */
-    public AbstractPhysicalPlan(NodeType type, OP_TYPE operator,
-            LogicalProperties logicalProperties, Plan... children) {
-        super(type, operator, logicalProperties, children);
+    public AbstractPhysicalPlan(PlanType type, LogicalProperties logicalProperties, Plan... children) {
+        super(type, Optional.empty(), Optional.of(logicalProperties), children);
         // TODO: compute physical properties
         this.physicalProperties = new PhysicalProperties();
     }
 
     /**
-     * create physical plan by op, logicalProperties and children.
+     * create physical plan by groupExpression, logicalProperties and children.
      *
      * @param type node type
-     * @param operator physical operator
-     * @param groupExpression group expression contains operator
+     * @param groupExpression group expression contains plan
      * @param logicalProperties logical properties of this plan
      * @param children children of this plan
      */
-    public AbstractPhysicalPlan(NodeType type, OP_TYPE operator, Optional<GroupExpression> groupExpression,
-            LogicalProperties logicalProperties, Plan... children) {
-        super(type, operator, groupExpression, logicalProperties, children);
+    public AbstractPhysicalPlan(PlanType type, Optional<GroupExpression> groupExpression,
+                                LogicalProperties logicalProperties, Plan... children) {
+        super(type, groupExpression, Optional.of(logicalProperties), children);
         // TODO: compute physical properties
         this.physicalProperties = new PhysicalProperties();
     }
@@ -74,6 +69,7 @@ public abstract class AbstractPhysicalPlan<OP_TYPE extends PhysicalOperator>
         return logicalProperties;
     }
 
+    @Override
     public PhysicalProperties getPhysicalProperties() {
         return physicalProperties;
     }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalAggregate.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAggregate.java
similarity index 54%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalAggregate.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAggregate.java
index 0b19dd99f7..28563a1283 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalAggregate.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalAggregate.java
@@ -15,24 +15,27 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.physical;
+package org.apache.doris.nereids.trees.plans.physical;
 
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.AggPhase;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
+import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
- * Physical aggregation plan operator.
+ * Physical aggregation plan.
  */
-public class PhysicalAggregate extends PhysicalUnaryOperator {
+public class PhysicalAggregate<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
 
     private final List<Expression> groupByExprList;
 
@@ -44,6 +47,14 @@ public class PhysicalAggregate extends PhysicalUnaryOperator {
 
     private final boolean usingStream;
 
+
+    public PhysicalAggregate(List<Expression> groupByExprList, List<NamedExpression> outputExpressionList,
+                             List<Expression> partitionExprList, AggPhase aggPhase, boolean usingStream,
+                             LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(groupByExprList, outputExpressionList, partitionExprList, aggPhase, usingStream,
+                Optional.empty(), logicalProperties, child);
+    }
+
     /**
      * Constructor of PhysicalAggNode.
      *
@@ -53,8 +64,10 @@ public class PhysicalAggregate extends PhysicalUnaryOperator {
      * @param usingStream whether it's stream agg.
      */
     public PhysicalAggregate(List<Expression> groupByExprList, List<NamedExpression> outputExpressionList,
-            List<Expression> partitionExprList, AggPhase aggPhase, boolean usingStream) {
-        super(OperatorType.PHYSICAL_AGGREGATION);
+                             List<Expression> partitionExprList, AggPhase aggPhase, boolean usingStream,
+                             Optional<GroupExpression> groupExpression, LogicalProperties logicalProperties,
+                             CHILD_TYPE child) {
+        super(PlanType.PHYSICAL_AGGREGATE, groupExpression, logicalProperties, child);
         this.groupByExprList = groupByExprList;
         this.outputExpressionList = outputExpressionList;
         this.aggPhase = aggPhase;
@@ -83,8 +96,8 @@ public class PhysicalAggregate extends PhysicalUnaryOperator {
     }
 
     @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalAggregate((PhysicalUnaryPlan<PhysicalAggregate, Plan>) plan, context);
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalAggregate((PhysicalAggregate<Plan>) this, context);
     }
 
     @Override
@@ -99,4 +112,23 @@ public class PhysicalAggregate extends PhysicalUnaryOperator {
         return "PhysicalAggregate([key=" + groupByExprList
                 + "], [output=" + outputExpressionList + "])";
     }
+
+    @Override
+    public PhysicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PhysicalAggregate<>(groupByExprList, outputExpressionList, partitionExprList, aggPhase,
+            usingStream, logicalProperties, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalAggregate<>(groupByExprList, outputExpressionList, partitionExprList, aggPhase,
+            usingStream, groupExpression, logicalProperties, child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalAggregate<>(groupByExprList, outputExpressionList, partitionExprList, aggPhase,
+            usingStream, Optional.empty(), logicalProperties.get(), child());
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
new file mode 100644
index 0000000000..d8160dd7bb
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinary.java
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.plans.BinaryPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+
+import java.util.Optional;
+
+/**
+ * Abstract class for all physical plan that have two children.
+ */
+public abstract class PhysicalBinary<
+            LEFT_CHILD_TYPE extends Plan,
+            RIGHT_CHILD_TYPE extends Plan>
+        extends AbstractPhysicalPlan
+        implements BinaryPlan<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
+
+    public PhysicalBinary(PlanType type, LogicalProperties logicalProperties,
+                              LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(type, logicalProperties, leftChild, rightChild);
+    }
+
+    public PhysicalBinary(PlanType type, Optional<GroupExpression> groupExpression,
+                              LogicalProperties logicalProperties, LEFT_CHILD_TYPE leftChild,
+                              RIGHT_CHILD_TYPE rightChild) {
+        super(type, groupExpression, logicalProperties, leftChild, rightChild);
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java
deleted file mode 100644
index c17ab725ba..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalBinaryPlan.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.physical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalBinaryOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.BinaryPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all physical plan that have two children.
- */
-public class PhysicalBinaryPlan<
-            OP_TYPE extends PhysicalBinaryOperator,
-            LEFT_CHILD_TYPE extends Plan,
-            RIGHT_CHILD_TYPE extends Plan>
-        extends AbstractPhysicalPlan<OP_TYPE>
-        implements BinaryPlan<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
-
-    public PhysicalBinaryPlan(OP_TYPE operator, LogicalProperties logicalProperties,
-            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.PHYSICAL, operator, logicalProperties, leftChild, rightChild);
-    }
-
-    public PhysicalBinaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-            LogicalProperties logicalProperties, LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
-        super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties, leftChild, rightChild);
-    }
-
-    @Override
-    public PhysicalBinaryPlan<OP_TYPE, Plan, Plan> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 2);
-        return new PhysicalBinaryPlan(operator, logicalProperties, children.get(0), children.get(1));
-    }
-
-    @Override
-    public PhysicalBinaryPlan<OP_TYPE, LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> withOutput(List<Slot> output) {
-        LogicalProperties logicalProperties = new LogicalProperties(() -> output);
-        return new PhysicalBinaryPlan<>(operator, logicalProperties, left(), right());
-    }
-
-    @Override
-    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new PhysicalBinaryPlan<>(operator, groupExpression, logicalProperties, left(), right());
-    }
-
-    @Override
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new PhysicalBinaryPlan<>(operator, groupExpression, logicalProperties.get(), left(), right());
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalDistribution.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalDistribution.java
new file mode 100644
index 0000000000..bec30a6393
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalDistribution.java
@@ -0,0 +1,77 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.DistributionSpec;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Enforcer plan.
+ */
+public class PhysicalDistribution<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
+
+    protected DistributionSpec distributionSpec;
+
+    public PhysicalDistribution(DistributionSpec spec, LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(spec, Optional.empty(), logicalProperties, child);
+    }
+
+    public PhysicalDistribution(DistributionSpec spec, Optional<GroupExpression> groupExpression,
+                                LogicalProperties logicalProperties, CHILD_TYPE child) {
+        super(PlanType.PHYSICAL_DISTRIBUTION, groupExpression, logicalProperties, child);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalDistribution((PhysicalDistribution<Plan>) this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of();
+    }
+
+    @Override
+    public Plan withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PhysicalDistribution<>(distributionSpec, Optional.empty(),
+            logicalProperties, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalDistribution<>(distributionSpec, groupExpression, logicalProperties, child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalDistribution<>(distributionSpec, Optional.empty(),
+            logicalProperties.get(), child());
+    }
+}
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
new file mode 100644
index 0000000000..65acb24e60
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalFilter.java
@@ -0,0 +1,85 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Physical filter plan.
+ */
+public class PhysicalFilter<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
+
+    private final Expression predicates;
+
+    public PhysicalFilter(Expression predicates, LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(predicates, Optional.empty(), logicalProperties, child);
+    }
+
+    public PhysicalFilter(Expression predicates, 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");
+    }
+
+    public Expression getPredicates() {
+        return predicates;
+    }
+
+    @Override
+    public String toString() {
+        return "Filter (" + predicates + ")";
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of(predicates);
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalFilter((PhysicalFilter<Plan>) this, context);
+    }
+
+    @Override
+    public PhysicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PhysicalFilter<>(predicates, logicalProperties, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalFilter<>(predicates, groupExpression, logicalProperties, child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalFilter<>(predicates, Optional.empty(), logicalProperties.get(), child());
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
new file mode 100644
index 0000000000..144f7cd70f
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHashJoin.java
@@ -0,0 +1,112 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.JoinType;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Physical hash join plan.
+ */
+public class PhysicalHashJoin<
+        LEFT_CHILD_TYPE extends Plan,
+        RIGHT_CHILD_TYPE extends Plan>
+        extends PhysicalBinary<LEFT_CHILD_TYPE, RIGHT_CHILD_TYPE> {
+
+    private final JoinType joinType;
+
+    private final Optional<Expression> condition;
+
+
+    public PhysicalHashJoin(JoinType joinType, Optional<Expression> condition, LogicalProperties logicalProperties,
+                            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        this(joinType, condition, Optional.empty(), logicalProperties, leftChild, rightChild);
+    }
+
+    /**
+     * Constructor of PhysicalHashJoinNode.
+     *
+     * @param joinType Which join type, left semi join, inner join...
+     * @param condition join condition.
+     */
+    public PhysicalHashJoin(JoinType joinType, Optional<Expression> condition,
+                            Optional<GroupExpression> groupExpression, LogicalProperties logicalProperties,
+                            LEFT_CHILD_TYPE leftChild, RIGHT_CHILD_TYPE rightChild) {
+        super(PlanType.PHYSICAL_HASH_JOIN, groupExpression, logicalProperties, leftChild, rightChild);
+        this.joinType = Objects.requireNonNull(joinType, "joinType can not be null");
+        this.condition = Objects.requireNonNull(condition, "condition can not be null");
+    }
+
+    public JoinType getJoinType() {
+        return joinType;
+    }
+
+    public Optional<Expression> getCondition() {
+        return condition;
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalHashJoin((PhysicalHashJoin<Plan, Plan>) this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return condition.<List<Expression>>map(ImmutableList::of).orElseGet(ImmutableList::of);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("PhysicalHashJoin ([").append(joinType).append("]");
+        if (condition.isPresent()) {
+            sb.append(", [").append(condition.get()).append("]");
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    @Override
+    public PhysicalBinary<Plan, Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 2);
+        return new PhysicalHashJoin<>(joinType, condition, logicalProperties, children.get(0), children.get(1));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalHashJoin<>(joinType, condition, groupExpression, logicalProperties, left(), right());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalHashJoin<>(joinType, condition, Optional.empty(),
+            logicalProperties.get(), left(), right());
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHeapSort.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHeapSort.java
new file mode 100644
index 0000000000..e695ce2b4c
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalHeapSort.java
@@ -0,0 +1,101 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.properties.OrderKey;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Physical sort plan.
+ */
+public class PhysicalHeapSort<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
+    private final long limit;
+    // Default offset is 0.
+    private final int offset;
+
+    private final List<OrderKey> orderKeys;
+
+
+    public PhysicalHeapSort(List<OrderKey> orderKeys, long limit, int offset,
+                            LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(orderKeys, limit, offset, Optional.empty(), logicalProperties, child);
+    }
+
+    /**
+     * Constructor of PhysicalHashJoinNode.
+     */
+    public PhysicalHeapSort(List<OrderKey> orderKeys, long limit, int offset,
+                            Optional<GroupExpression> groupExpression, LogicalProperties logicalProperties,
+                            CHILD_TYPE child) {
+        super(PlanType.PHYSICAL_SORT, groupExpression, logicalProperties, child);
+        this.offset = offset;
+        this.limit = limit;
+        this.orderKeys = orderKeys;
+    }
+
+    public int getOffset() {
+        return offset;
+    }
+
+    public List<OrderKey> getOrderKeys() {
+        return orderKeys;
+    }
+
+    public boolean hasLimit() {
+        return limit > -1;
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalHeapSort((PhysicalHeapSort<Plan>) this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return orderKeys.stream()
+            .map(OrderKey::getExpr)
+            .collect(ImmutableList.toImmutableList());
+    }
+
+    @Override
+    public PhysicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PhysicalHeapSort<>(orderKeys, limit, offset, logicalProperties, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalHeapSort<>(orderKeys, limit, offset, groupExpression, logicalProperties, child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalHeapSort<>(orderKeys, limit, offset, Optional.empty(), logicalProperties.get(), child());
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/Operator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
similarity index 54%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/Operator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
index 1e85102971..86a0af7b7b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/Operator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeaf.java
@@ -15,24 +15,26 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators;
+package org.apache.doris.nereids.trees.plans.physical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.trees.TreeNode;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.plans.LeafPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+
+import java.util.Optional;
 
 /**
- * interface for all concrete operator.
+ * Abstract class for all physical plan that have no child.
  */
-public interface Operator {
-    OperatorType getType();
-
-    <NODE_TYPE extends TreeNode<NODE_TYPE>> NODE_TYPE toTreeNode(GroupExpression groupExpression);
-
-    <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context);
+public abstract class PhysicalLeaf extends AbstractPhysicalPlan implements LeafPlan {
 
-    <R, C> R accept(OperatorVisitor<R, C> visitor, Operator operator, C context);
+    public PhysicalLeaf(PlanType type, LogicalProperties logicalProperties) {
+        super(type, logicalProperties);
+    }
 
-    <R, C> R accept(OperatorVisitor<R, C> visitor, C context);
+    public PhysicalLeaf(PlanType type, Optional<GroupExpression> groupExpression,
+                            LogicalProperties logicalProperties) {
+        super(type, groupExpression, logicalProperties);
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java
deleted file mode 100644
index dcc7d99c98..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalLeafPlan.java
+++ /dev/null
@@ -1,69 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.physical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalLeafOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.LeafPlan;
-import org.apache.doris.nereids.trees.plans.Plan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all physical plan that have no child.
- */
-public class PhysicalLeafPlan<OP_TYPE extends PhysicalLeafOperator>
-        extends AbstractPhysicalPlan<OP_TYPE>
-        implements LeafPlan {
-
-    public PhysicalLeafPlan(OP_TYPE operator, LogicalProperties logicalProperties) {
-        super(NodeType.PHYSICAL, operator, logicalProperties);
-    }
-
-    public PhysicalLeafPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-                            LogicalProperties logicalProperties) {
-        super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties);
-    }
-
-    @Override
-    public PhysicalLeafPlan<OP_TYPE> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 0);
-        return new PhysicalLeafPlan(operator, logicalProperties);
-    }
-
-    @Override
-    public PhysicalLeafPlan<OP_TYPE> withOutput(List<Slot> output) {
-        LogicalProperties logicalProperties = new LogicalProperties(() -> output);
-        return new PhysicalLeafPlan<>(operator, logicalProperties);
-    }
-
-    @Override
-    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new PhysicalLeafPlan<>(operator, groupExpression, logicalProperties);
-    }
-
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new PhysicalLeafPlan<>(operator, groupExpression, logicalProperties.get());
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOlapScan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOlapScan.java
similarity index 68%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOlapScan.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOlapScan.java
index ea279e39d1..93bf57d90b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalOlapScan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalOlapScan.java
@@ -15,26 +15,26 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.physical;
+package org.apache.doris.nereids.trees.plans.physical;
 
 import org.apache.doris.catalog.OlapTable;
 import org.apache.doris.catalog.Partition;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.PlanOperatorVisitor;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
 
-import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import org.apache.commons.lang3.StringUtils;
 
 import java.util.List;
+import java.util.Optional;
 
 /**
- * Physical olap scan plan operator.
+ * Physical olap scan plan.
  */
-public class PhysicalOlapScan extends PhysicalScan {
+public class PhysicalOlapScan extends PhysicalRelation {
     private final long selectedIndexId;
     private final List<Long> selectedTabletId;
     private final List<Long> selectedPartitionId;
@@ -47,8 +47,9 @@ public class PhysicalOlapScan extends PhysicalScan {
      * @param olapTable OlapTable in Doris
      * @param qualifier table's name
      */
-    public PhysicalOlapScan(OlapTable olapTable, List<String> qualifier) {
-        super(OperatorType.PHYSICAL_OLAP_SCAN, qualifier);
+    public PhysicalOlapScan(OlapTable olapTable, List<String> qualifier,
+                            Optional<GroupExpression> groupExpression, LogicalProperties logicalProperties) {
+        super(PlanType.PHYSICAL_OLAP_SCAN, qualifier, groupExpression, logicalProperties);
         this.olapTable = olapTable;
         this.selectedIndexId = olapTable.getBaseIndexId();
         this.selectedTabletId = Lists.newArrayList();
@@ -81,12 +82,17 @@ public class PhysicalOlapScan extends PhysicalScan {
     }
 
     @Override
-    public <R, C> R accept(PlanOperatorVisitor<R, C> visitor, Plan plan, C context) {
-        return visitor.visitPhysicalOlapScan((PhysicalLeafPlan<PhysicalOlapScan>) plan, context);
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalOlapScan(this, context);
     }
 
     @Override
-    public List<Expression> getExpressions() {
-        return ImmutableList.of();
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalOlapScan(olapTable, qualifier, groupExpression, logicalProperties);
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalOlapScan(olapTable, qualifier, Optional.empty(), logicalProperties.get());
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalPlan.java
index e0b1ff34b8..54c5871767 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalPlan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalPlan.java
@@ -17,13 +17,12 @@
 
 package org.apache.doris.nereids.trees.plans.physical;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalOperator;
+import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 /**
  * interface for all physical plan.
  */
 public interface PhysicalPlan extends Plan {
-    @Override
-    PhysicalOperator getOperator();
+    PhysicalProperties getPhysicalProperties();
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
new file mode 100644
index 0000000000..09671b1239
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalProject.java
@@ -0,0 +1,86 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.physical;
+
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.NamedExpression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.base.Preconditions;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Physical project plan.
+ */
+public class PhysicalProject<CHILD_TYPE extends Plan> extends PhysicalUnary<CHILD_TYPE> {
+
+    private final List<NamedExpression> projects;
+
+    public PhysicalProject(List<NamedExpression> projects, LogicalProperties logicalProperties, CHILD_TYPE child) {
+        this(projects, Optional.empty(), logicalProperties, child);
+    }
+
+    public PhysicalProject(List<NamedExpression> projects, Optional<GroupExpression> groupExpression,
+                           LogicalProperties logicalProperties, CHILD_TYPE child) {
+        super(PlanType.PHYSICAL_PROJECT, groupExpression, logicalProperties, child);
+        this.projects = Objects.requireNonNull(projects, "projects can not be null");
+    }
+
+    public List<NamedExpression> getProjects() {
+        return projects;
+    }
+
+    @Override
+    public String toString() {
+        return "Project (" + StringUtils.join(projects, ", ") + ")";
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalProject((PhysicalProject<Plan>) this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return (List) projects;
+    }
+
+    @Override
+    public PhysicalUnary<Plan> withChildren(List<Plan> children) {
+        Preconditions.checkArgument(children.size() == 1);
+        return new PhysicalProject<>(projects, logicalProperties, children.get(0));
+    }
+
+    @Override
+    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+        return new PhysicalProject<>(projects, groupExpression, logicalProperties, child());
+    }
+
+    @Override
+    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+        return new PhysicalProject<>(projects, Optional.empty(), logicalProperties.get(), child());
+    }
+}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalScan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalRelation.java
similarity index 54%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalScan.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalRelation.java
index 9fdde430de..6faee76563 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalScan.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalRelation.java
@@ -15,17 +15,24 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.physical;
+package org.apache.doris.nereids.trees.plans.physical;
 
-import org.apache.doris.nereids.operators.OperatorType;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+
+import com.google.common.collect.ImmutableList;
 
 import java.util.List;
 import java.util.Objects;
+import java.util.Optional;
 
 /**
- * Abstract class for all physical scan operator.
+ * Abstract class for all physical scan plan.
  */
-public abstract class PhysicalScan extends PhysicalLeafOperator {
+public abstract class PhysicalRelation extends PhysicalLeaf {
 
     protected final List<String> qualifier;
 
@@ -35,12 +42,23 @@ public abstract class PhysicalScan extends PhysicalLeafOperator {
      * @param type node type
      * @param qualifier table's name
      */
-    public PhysicalScan(OperatorType type, List<String> qualifier) {
-        super(type);
+    public PhysicalRelation(PlanType type, List<String> qualifier, Optional<GroupExpression> groupExpression,
+                            LogicalProperties logicalProperties) {
+        super(type, groupExpression, logicalProperties);
         this.qualifier = Objects.requireNonNull(qualifier, "qualifier can not be null");
     }
 
     public List<String> getQualifier() {
         return qualifier;
     }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitPhysicalScan(this, context);
+    }
+
+    @Override
+    public List<Expression> getExpressions() {
+        return ImmutableList.of();
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
similarity index 53%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
index fda9e7e600..f4a004d86e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/physical/PhysicalLeafOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnary.java
@@ -15,30 +15,29 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.physical;
+package org.apache.doris.nereids.trees.plans.physical;
 
 import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.AbstractOperator;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.LeafPlanOperator;
 import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.plans.physical.PhysicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.UnaryPlan;
 
 import java.util.Optional;
 
 /**
- * Abstract class for all physical operator that have no input.
+ * Abstract class for all physical plan that have one child.
  */
-public abstract class PhysicalLeafOperator extends AbstractOperator
-        implements PhysicalOperator, LeafPlanOperator {
+public abstract class PhysicalUnary<CHILD_TYPE extends Plan>
+        extends AbstractPhysicalPlan
+        implements UnaryPlan<CHILD_TYPE> {
 
-    public PhysicalLeafOperator(OperatorType type) {
-        super(type);
+    public PhysicalUnary(PlanType type, LogicalProperties logicalProperties, CHILD_TYPE child) {
+        super(type, logicalProperties, child);
     }
 
-    @Override
-    public PhysicalLeafPlan toTreeNode(GroupExpression groupExpression) {
-        LogicalProperties logicalProperties = groupExpression.getParent().getLogicalProperties();
-        return new PhysicalLeafPlan(this, Optional.of(groupExpression), logicalProperties);
+    public PhysicalUnary(PlanType type, Optional<GroupExpression> groupExpression,
+                             LogicalProperties logicalProperties, CHILD_TYPE child) {
+        super(type, groupExpression, logicalProperties, child);
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java
deleted file mode 100644
index 1b6b5f09a5..0000000000
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/physical/PhysicalUnaryPlan.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.nereids.trees.plans.physical;
-
-import org.apache.doris.nereids.memo.GroupExpression;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalUnaryOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
-import org.apache.doris.nereids.trees.NodeType;
-import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.UnaryPlan;
-
-import com.google.common.base.Preconditions;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Abstract class for all physical plan that have one child.
- */
-public class PhysicalUnaryPlan<OP_TYPE extends PhysicalUnaryOperator, CHILD_TYPE extends Plan>
-        extends AbstractPhysicalPlan<OP_TYPE>
-        implements UnaryPlan<CHILD_TYPE> {
-
-    public PhysicalUnaryPlan(OP_TYPE operator, LogicalProperties logicalProperties, CHILD_TYPE child) {
-        super(NodeType.PHYSICAL, operator, logicalProperties, child);
-    }
-
-    public PhysicalUnaryPlan(OP_TYPE operator, Optional<GroupExpression> groupExpression,
-                             LogicalProperties logicalProperties, CHILD_TYPE child) {
-        super(NodeType.PHYSICAL, operator, groupExpression, logicalProperties, child);
-    }
-
-    @Override
-    public PhysicalUnaryPlan<OP_TYPE, Plan> withChildren(List<Plan> children) {
-        Preconditions.checkArgument(children.size() == 1);
-        return new PhysicalUnaryPlan(operator, logicalProperties, children.get(0));
-    }
-
-    @Override
-    public PhysicalUnaryPlan<OP_TYPE, CHILD_TYPE> withOutput(List<Slot> output) {
-        LogicalProperties logicalProperties = new LogicalProperties(() -> output);
-        return new PhysicalUnaryPlan<>(operator, logicalProperties, child());
-    }
-
-    @Override
-    public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
-        return new PhysicalUnaryPlan<>(operator, groupExpression, logicalProperties, child());
-    }
-
-    @Override
-    public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
-        return new PhysicalUnaryPlan<>(operator, groupExpression, logicalProperties.get(), child());
-    }
-}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanRewriter.java
similarity index 53%
copy from fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
copy to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanRewriter.java
index 5204b37467..ff11a75df4 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/implementation/LogicalJoinToHashJoin.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanRewriter.java
@@ -15,23 +15,30 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.rules.implementation;
+package org.apache.doris.nereids.trees.plans.visitor;
 
-import org.apache.doris.nereids.operators.plans.physical.PhysicalHashJoin;
-import org.apache.doris.nereids.rules.Rule;
-import org.apache.doris.nereids.rules.RuleType;
 import org.apache.doris.nereids.trees.plans.Plan;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- * Implementation rule that convert logical join to physical hash join.
+ * Default implementation for plan rewriting, delegating to child plans and rewrite current root
+ * when any one of its children changed.
  */
-public class LogicalJoinToHashJoin extends OneImplementationRuleFactory {
+public abstract class DefaultPlanRewriter<C> extends PlanVisitor<Plan, C> {
+
     @Override
-    public Rule<Plan> build() {
-        return logicalJoin().then(join -> plan(
-            new PhysicalHashJoin(join.operator.getJoinType(), join.operator.getCondition()),
-            join.getLogicalProperties(),
-            join.left(), join.right()
-        )).toRule(RuleType.LOGICAL_JOIN_TO_HASH_JOIN_RULE);
+    public Plan visit(Plan expr, C context) {
+        List<Plan> newChildren = new ArrayList<>();
+        boolean hasNewChildren = false;
+        for (Plan child : expr.children()) {
+            Plan newChild = child.accept(this, context);
+            if (newChild != child) {
+                hasNewChildren = true;
+            }
+            newChildren.add(newChild);
+        }
+        return hasNewChildren ? expr.withChildren(newChildren) : expr;
     }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanVisitor.java
similarity index 70%
rename from fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanVisitor.java
index 0e38c86d0c..d9dd69d97b 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/operators/plans/logical/LogicalOperator.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/DefaultPlanVisitor.java
@@ -15,15 +15,19 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.operators.plans.logical;
+package org.apache.doris.nereids.trees.plans.visitor;
 
-import org.apache.doris.nereids.operators.plans.PlanOperator;
-import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.plans.Plan;
 
 /**
- * interface for all concrete logical plan operator.
+ * Use the visitor to iterate over all plans for plan.
  */
-public interface LogicalOperator extends PlanOperator {
-    LogicalProperties computeLogicalProperties(Plan... inputs);
+public class DefaultPlanVisitor<R, C> extends PlanVisitor<R, C> {
+    @Override
+    public R visit(Plan plan, C context) {
+        for (Plan child : plan.children()) {
+            child.accept(this, context);
+        }
+        return null;
+    }
 }
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
new file mode 100644
index 0000000000..a6d306b6d6
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/PlanVisitor.java
@@ -0,0 +1,125 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//  http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.nereids.trees.plans.visitor;
+
+import org.apache.doris.nereids.analyzer.UnboundRelation;
+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.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.logical.LogicalSort;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalAggregate;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalDistribution;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalFilter;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHashJoin;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalHeapSort;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalOlapScan;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalProject;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
+
+/**
+ * Base class for the processing of logical and physical plan.
+ *
+ * @param <R> Return type of each visit method.
+ * @param <C> Context type.
+ */
+public abstract class PlanVisitor<R, C> {
+
+    public abstract R visit(Plan plan, C context);
+
+    // *******************************
+    // Logical plans
+    // *******************************
+
+    public R visitUnboundRelation(UnboundRelation relation, C context) {
+        return visit(relation, context);
+    }
+
+    public R visitLogicalRelation(LogicalRelation relation, C context) {
+        return visit(relation, context);
+    }
+
+
+    public R visitLogicalAggregate(LogicalAggregate<Plan> relation, C context) {
+        return visit(relation, context);
+    }
+
+    public R visitLogicalFilter(LogicalFilter<Plan> filter, C context) {
+        return visit(filter, context);
+    }
+
+    public R visitLogicalOlapScan(LogicalOlapScan olapScan, C context) {
+        return visitLogicalRelation(olapScan, context);
+    }
+
+    public R visitLogicalProject(LogicalProject<Plan> project, C context) {
+        return visit(project, context);
+    }
+
+    public R visitLogicalSort(LogicalSort<Plan> sort, C context) {
+        return visit(sort, context);
+    }
+
+    public R visitLogicalJoin(LogicalJoin<Plan, Plan> join, C context) {
+        return visit(join, context);
+    }
+
+    public R visitGroupPlan(GroupPlan groupPlan, C context) {
+        return visit(groupPlan, context);
+    }
+
+    // *******************************
+    // Physical plans
+    // *******************************
+
+    public R visitPhysicalAggregate(PhysicalAggregate<Plan> agg, C context) {
+        return visit(agg, context);
+    }
+
+    public R visitPhysicalScan(PhysicalRelation scan, C context) {
+        return visit(scan, context);
+    }
+
+    public R visitPhysicalOlapScan(PhysicalOlapScan olapScan, C context) {
+        return visitPhysicalScan(olapScan, context);
+    }
+
+    public R visitPhysicalHeapSort(PhysicalHeapSort<Plan> sort, C context) {
+        return visit(sort, context);
+    }
+
+    public R visitPhysicalHashJoin(PhysicalHashJoin<Plan, Plan> hashJoin, C context) {
+        return visit(hashJoin, context);
+    }
+
+    public R visitPhysicalProject(PhysicalProject<Plan> project, C context) {
+        return visit(project, context);
+    }
+
+    public R visitPhysicalFilter(PhysicalFilter<Plan> filter, C context) {
+        return visit(filter, context);
+    }
+
+    public R visitPhysicalDistribution(PhysicalDistribution<Plan> distribution, C context) {
+        return visit(distribution, context);
+    }
+}
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 d5c80fe562..e95a5571e7 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
@@ -17,9 +17,9 @@
 
 package org.apache.doris.nereids.util;
 
-import org.apache.doris.nereids.trees.NodeType;
 import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.ExpressionType;
 import org.apache.doris.nereids.trees.expressions.Literal;
 
 import com.google.common.base.Preconditions;
@@ -41,24 +41,24 @@ public class ExpressionUtils {
     }
 
     public static List<Expression> extractConjunct(Expression expr) {
-        return extract(NodeType.AND, expr);
+        return extract(ExpressionType.AND, expr);
     }
 
     public static List<Expression> extractDisjunct(Expression expr) {
-        return extract(NodeType.OR, expr);
+        return extract(ExpressionType.OR, expr);
     }
 
     public static List<Expression> extract(CompoundPredicate expr) {
         return extract(expr.getType(), expr);
     }
 
-    private static List<Expression> extract(NodeType op, Expression expr) {
+    private static List<Expression> extract(ExpressionType op, Expression expr) {
         List<Expression> result = Lists.newArrayList();
         extract(op, expr, result);
         return result;
     }
 
-    private static void extract(NodeType op, Expression expr, List<Expression> result) {
+    private static void extract(ExpressionType op, Expression expr, List<Expression> result) {
         if (expr instanceof CompoundPredicate && expr.getType() == op) {
             CompoundPredicate predicate = (CompoundPredicate) expr;
             extract(op, predicate.left(), result);
@@ -69,30 +69,30 @@ public class ExpressionUtils {
     }
 
     public static Expression and(List<Expression> expressions) {
-        return combine(NodeType.AND, expressions);
+        return combine(ExpressionType.AND, expressions);
     }
 
     public static Expression and(Expression... expressions) {
-        return combine(NodeType.AND, Lists.newArrayList(expressions));
+        return combine(ExpressionType.AND, Lists.newArrayList(expressions));
     }
 
     public static Expression or(Expression... expressions) {
-        return combine(NodeType.OR, Lists.newArrayList(expressions));
+        return combine(ExpressionType.OR, Lists.newArrayList(expressions));
     }
 
     public static Expression or(List<Expression> expressions) {
-        return combine(NodeType.OR, expressions);
+        return combine(ExpressionType.OR, expressions);
     }
 
     /**
      * Use AND/OR to combine expressions together.
      */
-    public static Expression combine(NodeType op, List<Expression> expressions) {
-        Preconditions.checkArgument(op == NodeType.AND || op == NodeType.OR);
+    public static Expression combine(ExpressionType op, List<Expression> expressions) {
+        Preconditions.checkArgument(op == ExpressionType.AND || op == ExpressionType.OR);
         Objects.requireNonNull(expressions, "expressions is null");
 
-        Expression shortCircuit = (op == NodeType.AND ? Literal.FALSE_LITERAL : Literal.TRUE_LITERAL);
-        Expression skip = (op == NodeType.AND ? Literal.TRUE_LITERAL : Literal.FALSE_LITERAL);
+        Expression shortCircuit = (op == ExpressionType.AND ? Literal.FALSE_LITERAL : Literal.TRUE_LITERAL);
+        Expression skip = (op == ExpressionType.AND ? Literal.TRUE_LITERAL : Literal.FALSE_LITERAL);
         LinkedHashSet<Expression> distinctExpressions = Sets.newLinkedHashSetWithExpectedSize(expressions.size());
         for (Expression expression : expressions) {
             if (expression.equals(shortCircuit)) {
@@ -104,6 +104,6 @@ public class ExpressionUtils {
 
         Optional<Expression> result =
                 distinctExpressions.stream().reduce((left, right) -> new CompoundPredicate(op, left, right));
-        return result.orElse(new Literal(op == NodeType.AND));
+        return result.orElse(new Literal(op == ExpressionType.AND));
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
index 5ecd7f7fa9..1bd5984790 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/AnalyzeSSBTest.java
@@ -175,7 +175,7 @@ public class AnalyzeSSBTest extends TestWithFeService {
             }
         }
 
-        List<Expression> expressions = plan.getOperator().getExpressions();
+        List<Expression> expressions = plan.getExpressions();
         return expressions.stream().allMatch(this::checkExpressionBound);
     }
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
index d3416e463a..eae74eaf15 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/RewriteTopDownJobTest.java
@@ -19,6 +19,7 @@ package org.apache.doris.nereids.jobs;
 
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Table;
+import org.apache.doris.catalog.TableIf.TableType;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.PlannerContext;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
@@ -26,9 +27,7 @@ import org.apache.doris.nereids.jobs.rewrite.RewriteTopDownJob;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.GroupExpression;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.rules.RuleType;
@@ -36,7 +35,9 @@ import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
 import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
 import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.StringType;
 import org.apache.doris.qe.ConnectContext;
@@ -47,30 +48,32 @@ import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
 import java.util.List;
+import java.util.Optional;
 
-public class RewriteTopDownJobTest implements Plans {
+public class RewriteTopDownJobTest {
     public static class FakeRule extends OneRewriteRuleFactory {
         @Override
         public Rule<Plan> build() {
-            return unboundRelation().then(unboundRelation -> plan(
-                new LogicalOlapScan(new Table(0, "test", Table.TableType.OLAP, ImmutableList.of(
-                    new Column("id", Type.INT),
-                    new Column("name", Type.STRING)
-                )), Lists.newArrayList("test"))
-            )).toRule(RuleType.BINDING_RELATION);
+            return unboundRelation().then(unboundRelation -> {
+                        Table olapTable = new Table(0, "test", TableType.OLAP, ImmutableList.of(
+                                new Column("id", Type.INT),
+                                new Column("name", Type.STRING)
+                        ));
+                        return new LogicalBoundRelation(olapTable, Lists.newArrayList("test"));
+                    }
+                ).toRule(RuleType.BINDING_RELATION);
         }
     }
 
     @Test
     public void testSimplestScene() {
-        UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
-        Plan leaf = plan(unboundRelation);
+        Plan leaf = new UnboundRelation(Lists.newArrayList("test"));
         LogicalProject project = new LogicalProject(ImmutableList.of(
-            new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test")))
+                new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test"))),
+                leaf
         );
-        Plan root = plan(project, leaf);
         Memo memo = new Memo();
-        memo.initialize(root);
+        memo.initialize(project);
 
         PlannerContext plannerContext = new PlannerContext(memo, new ConnectContext());
         JobContext jobContext = new JobContext(plannerContext, new PhysicalProperties(), Double.MAX_VALUE);
@@ -89,7 +92,7 @@ public class RewriteTopDownJobTest implements Plans {
         Assertions.assertEquals(output.get(0).getName(), "name");
         Assertions.assertEquals(output.get(0).getDataType(), StringType.INSTANCE);
         Assertions.assertEquals(1, rootGroupExpression.children().size());
-        Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, rootGroupExpression.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_PROJECT, rootGroupExpression.getPlan().getType());
 
         Group leafGroup = rootGroupExpression.child(0);
         output = leafGroup.getLogicalProperties().getOutput();
@@ -100,6 +103,28 @@ public class RewriteTopDownJobTest implements Plans {
         Assertions.assertEquals(output.get(1).getDataType(), StringType.INSTANCE);
         Assertions.assertEquals(1, leafGroup.getLogicalExpressions().size());
         GroupExpression leafGroupExpression = leafGroup.getLogicalExpression();
-        Assertions.assertEquals(OperatorType.LOGICAL_BOUND_RELATION, leafGroupExpression.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_BOUND_RELATION, leafGroupExpression.getPlan().getType());
+    }
+
+    private static class LogicalBoundRelation extends LogicalRelation {
+
+        public LogicalBoundRelation(Table table, List<String> qualifier) {
+            super(PlanType.LOGICAL_BOUND_RELATION, table, qualifier);
+        }
+
+        public LogicalBoundRelation(Table table, List<String> qualifier, Optional<GroupExpression> groupExpression,
+                Optional<LogicalProperties> logicalProperties) {
+            super(PlanType.LOGICAL_BOUND_RELATION, table, qualifier, groupExpression, logicalProperties);
+        }
+
+        @Override
+        public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+            return new LogicalBoundRelation(table, qualifier, groupExpression, Optional.of(logicalProperties));
+        }
+
+        @Override
+        public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+            return new LogicalBoundRelation(table, qualifier, Optional.empty(), logicalProperties);
+        }
     }
 }
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 40b8eeed1e..1e1486dab7 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
@@ -18,11 +18,9 @@
 package org.apache.doris.nereids.memo;
 
 import org.apache.doris.nereids.analyzer.UnboundRelation;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
-import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.types.StringType;
 
 import com.google.common.collect.ImmutableList;
@@ -30,31 +28,33 @@ import com.google.common.collect.Lists;
 import org.junit.Assert;
 import org.junit.Test;
 
-public class MemoTest implements Plans {
+public class MemoTest {
     @Test
     public void testInitialize() {
         UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
         LogicalProject insideProject = new LogicalProject(
-                ImmutableList.of(new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test"))));
+                ImmutableList.of(new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test"))),
+                unboundRelation
+        );
         LogicalProject rootProject = new LogicalProject(
-                ImmutableList.of(new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test"))));
+                ImmutableList.of(new SlotReference("name", StringType.INSTANCE, true, ImmutableList.of("test"))),
+                insideProject
+        );
 
         // Project -> Project -> Relation
-        Plan root = plan(rootProject, plan(insideProject, plan(unboundRelation)));
-
         Memo memo = new Memo();
-        memo.initialize(root);
+        memo.initialize(rootProject);
 
         Group rootGroup = memo.getRoot();
 
         Assert.assertEquals(3, memo.getGroups().size());
         Assert.assertEquals(3, memo.getGroupExpressions().size());
 
-        Assert.assertEquals(OperatorType.LOGICAL_PROJECT, rootGroup.logicalExpressionsAt(0).getOperator().getType());
-        Assert.assertEquals(OperatorType.LOGICAL_PROJECT,
-                rootGroup.logicalExpressionsAt(0).child(0).logicalExpressionsAt(0).getOperator().getType());
-        Assert.assertEquals(OperatorType.LOGICAL_UNBOUND_RELATION,
+        Assert.assertEquals(PlanType.LOGICAL_PROJECT, rootGroup.logicalExpressionsAt(0).getPlan().getType());
+        Assert.assertEquals(PlanType.LOGICAL_PROJECT,
+                rootGroup.logicalExpressionsAt(0).child(0).logicalExpressionsAt(0).getPlan().getType());
+        Assert.assertEquals(PlanType.LOGICAL_UNBOUND_RELATION,
                 rootGroup.logicalExpressionsAt(0).child(0).logicalExpressionsAt(0).child(0).logicalExpressionsAt(0)
-                        .getOperator().getType());
+                        .getPlan().getType());
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
index ff1850921c..08fbc07b38 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java
@@ -18,8 +18,9 @@
 package org.apache.doris.nereids.parser;
 
 import org.apache.doris.nereids.exceptions.ParseException;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.Plan;
 import org.apache.doris.nereids.trees.plans.logical.LogicalPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
@@ -66,7 +67,7 @@ public class NereidsParserTest {
         String sql = "select `AD``D` from t1 where a = 1";
         NereidsParser nereidsParser = new NereidsParser();
         LogicalPlan logicalPlan = nereidsParser.parseSingle(sql);
-        LogicalProject logicalProject = (LogicalProject) logicalPlan.getOperator();
+        LogicalProject<Plan> logicalProject = (LogicalProject) logicalPlan;
         Assertions.assertEquals("AD`D", logicalProject.getProjects().get(0).getName());
     }
 }
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 7e877e3b4e..3437b78c96 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
@@ -19,13 +19,12 @@ package org.apache.doris.nereids.pattern;
 
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.rules.RulePromise;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalJoin;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
@@ -34,16 +33,14 @@ import org.junit.jupiter.api.Test;
 
 import java.util.Iterator;
 
-public class GroupExpressionMatchingTest implements Plans {
+public class GroupExpressionMatchingTest {
 
     @Test
     public void testLeafNode() {
-        Pattern pattern = new Pattern<>(OperatorType.LOGICAL_UNBOUND_RELATION);
+        Pattern pattern = new Pattern<>(PlanType.LOGICAL_UNBOUND_RELATION);
 
-        UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
-        Plan plan = plan(unboundRelation);
         Memo memo = new Memo();
-        memo.initialize(plan);
+        memo.initialize(new UnboundRelation(Lists.newArrayList("test")));
 
         GroupExpressionMatching groupExpressionMatching
                 = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
@@ -51,24 +48,21 @@ public class GroupExpressionMatchingTest implements Plans {
 
         Assertions.assertTrue(iterator.hasNext());
         Plan actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_UNBOUND_RELATION, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_UNBOUND_RELATION, actual.getType());
         Assertions.assertFalse(iterator.hasNext());
     }
 
     @Test
     public void testDepth2() {
-        Pattern pattern = new Pattern<>(OperatorType.LOGICAL_PROJECT,
-                new Pattern<>(OperatorType.LOGICAL_UNBOUND_RELATION));
+        Pattern pattern = new Pattern<>(PlanType.LOGICAL_PROJECT,
+                new Pattern<>(PlanType.LOGICAL_UNBOUND_RELATION));
 
-        UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
-        Plan leaf = plan(unboundRelation);
-        LogicalProject project = new LogicalProject(Lists.newArrayList());
-        Plan root = plan(project, leaf);
+        Plan leaf = new UnboundRelation(Lists.newArrayList("test"));
+        LogicalProject root = new LogicalProject(Lists.newArrayList(), leaf);
         Memo memo = new Memo();
         memo.initialize(root);
 
-        UnboundRelation anotherUnboundRelation = new UnboundRelation(Lists.newArrayList("test2"));
-        Plan anotherLeaf = plan(anotherUnboundRelation);
+        Plan anotherLeaf = new UnboundRelation(Lists.newArrayList("test2"));
         memo.copyIn(anotherLeaf, memo.getRoot().getLogicalExpression().child(0), false);
 
         GroupExpressionMatching groupExpressionMatching
@@ -78,30 +72,27 @@ public class GroupExpressionMatchingTest implements Plans {
         Assertions.assertTrue(iterator.hasNext());
         Plan actual;
         actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_PROJECT, actual.getType());
         Assertions.assertEquals(1, actual.arity());
-        Assertions.assertEquals(OperatorType.LOGICAL_UNBOUND_RELATION, actual.child(0).getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_UNBOUND_RELATION, actual.child(0).getType());
         Assertions.assertTrue(iterator.hasNext());
         actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_PROJECT, actual.getType());
         Assertions.assertEquals(1, actual.arity());
-        Assertions.assertEquals(OperatorType.LOGICAL_UNBOUND_RELATION, actual.child(0).getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_UNBOUND_RELATION, actual.child(0).getType());
         Assertions.assertFalse(iterator.hasNext());
     }
 
     @Test
     public void testDepth2WithGroup() {
-        Pattern pattern = new Pattern<>(OperatorType.LOGICAL_PROJECT, Pattern.GROUP);
+        Pattern pattern = new Pattern<>(PlanType.LOGICAL_PROJECT, Pattern.GROUP);
 
-        UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
-        Plan leaf = plan(unboundRelation);
-        LogicalProject project = new LogicalProject(Lists.newArrayList());
-        Plan root = plan(project, leaf);
+        Plan leaf = new UnboundRelation(Lists.newArrayList("test"));
+        LogicalProject root = new LogicalProject(Lists.newArrayList(), leaf);
         Memo memo = new Memo();
         memo.initialize(root);
 
-        UnboundRelation anotherUnboundRelation = new UnboundRelation(Lists.newArrayList("test2"));
-        Plan anotherLeaf = plan(anotherUnboundRelation);
+        Plan anotherLeaf = new UnboundRelation(Lists.newArrayList("test2"));
         memo.copyIn(anotherLeaf, memo.getRoot().getLogicalExpression().child(0), false);
 
         GroupExpressionMatching groupExpressionMatching
@@ -111,9 +102,9 @@ public class GroupExpressionMatchingTest implements Plans {
         Assertions.assertTrue(iterator.hasNext());
         Plan actual;
         actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_PROJECT, actual.getType());
         Assertions.assertEquals(1, actual.arity());
-        Assertions.assertEquals(OperatorType.GROUP_PLAN, actual.child(0).getOperator().getType());
+        Assertions.assertEquals(PlanType.GROUP_PLAN, actual.child(0).getType());
         Assertions.assertFalse(iterator.hasNext());
     }
 
@@ -121,10 +112,8 @@ public class GroupExpressionMatchingTest implements Plans {
     public void testLeafAny() {
         Pattern pattern = Pattern.ANY;
 
-        UnboundRelation unboundRelation = new UnboundRelation(Lists.newArrayList("test"));
-        Plan plan = plan(unboundRelation);
         Memo memo = new Memo();
-        memo.initialize(plan);
+        memo.initialize(new UnboundRelation(Lists.newArrayList("test")));
 
         GroupExpressionMatching groupExpressionMatching
                 = new GroupExpressionMatching(pattern, memo.getRoot().getLogicalExpression());
@@ -132,20 +121,18 @@ public class GroupExpressionMatchingTest implements Plans {
 
         Assertions.assertTrue(iterator.hasNext());
         Plan actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_UNBOUND_RELATION, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_UNBOUND_RELATION, actual.getType());
         Assertions.assertFalse(iterator.hasNext());
     }
 
     @Test
     public void testAnyWithChild() {
-        Plan root = plan(
-                new LogicalProject(Lists.newArrayList()),
-                plan(new UnboundRelation(Lists.newArrayList("test")))
-        );
+        Plan root = new LogicalProject(Lists.newArrayList(),
+                new UnboundRelation(Lists.newArrayList("test")));
         Memo memo = new Memo();
         memo.initialize(root);
 
-        Plan anotherLeaf = plan(new UnboundRelation(ImmutableList.of("test2")));
+        Plan anotherLeaf = new UnboundRelation(ImmutableList.of("test2"));
         memo.copyIn(anotherLeaf, memo.getRoot().getLogicalExpression().child(0), false);
 
         GroupExpressionMatching groupExpressionMatching
@@ -154,18 +141,18 @@ public class GroupExpressionMatchingTest implements Plans {
 
         Assertions.assertTrue(iterator.hasNext());
         Plan actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_PROJECT, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_PROJECT, actual.getType());
         Assertions.assertEquals(1, actual.arity());
-        Assertions.assertEquals(OperatorType.GROUP_PLAN, actual.child(0).getOperator().getType());
+        Assertions.assertEquals(PlanType.GROUP_PLAN, actual.child(0).getType());
 
         Assertions.assertFalse(iterator.hasNext());
     }
 
     @Test
     public void testInnerLogicalJoinMatch() {
-        Plan root = plan(new LogicalJoin(JoinType.INNER_JOIN),
-                plan(new UnboundRelation(ImmutableList.of("a"))),
-                plan(new UnboundRelation(ImmutableList.of("b")))
+        Plan root = new LogicalJoin(JoinType.INNER_JOIN,
+                new UnboundRelation(ImmutableList.of("a")),
+                new UnboundRelation(ImmutableList.of("b"))
         );
 
         Memo memo = new Memo();
@@ -177,17 +164,17 @@ public class GroupExpressionMatchingTest implements Plans {
 
         Assertions.assertTrue(iterator.hasNext());
         Plan actual = iterator.next();
-        Assertions.assertEquals(OperatorType.LOGICAL_JOIN, actual.getOperator().getType());
+        Assertions.assertEquals(PlanType.LOGICAL_JOIN, actual.getType());
         Assertions.assertEquals(2, actual.arity());
-        Assertions.assertEquals(OperatorType.GROUP_PLAN, actual.child(0).getOperator().getType());
-        Assertions.assertEquals(OperatorType.GROUP_PLAN, actual.child(1).getOperator().getType());
+        Assertions.assertEquals(PlanType.GROUP_PLAN, actual.child(0).getType());
+        Assertions.assertEquals(PlanType.GROUP_PLAN, actual.child(1).getType());
     }
 
     @Test
     public void testInnerLogicalJoinMismatch() {
-        Plan root = plan(new LogicalJoin(JoinType.LEFT_OUTER_JOIN),
-                plan(new UnboundRelation(ImmutableList.of("a"))),
-                plan(new UnboundRelation(ImmutableList.of("b")))
+        Plan root = new LogicalJoin(JoinType.LEFT_OUTER_JOIN,
+                new UnboundRelation(ImmutableList.of("a")),
+                new UnboundRelation(ImmutableList.of("b"))
         );
 
         Memo memo = new Memo();
@@ -202,9 +189,9 @@ public class GroupExpressionMatchingTest implements Plans {
 
     @Test
     public void testTopMatchButChildrenNotMatch() {
-        Plan root = plan(new LogicalJoin(JoinType.LEFT_OUTER_JOIN),
-                plan(new UnboundRelation(ImmutableList.of("a"))),
-                plan(new UnboundRelation(ImmutableList.of("b")))
+        Plan root = new LogicalJoin(JoinType.LEFT_OUTER_JOIN,
+                new UnboundRelation(ImmutableList.of("a")),
+                new UnboundRelation(ImmutableList.of("b"))
         );
 
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java
index cfea47f716..cdf325413f 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/plan/TestPlanOutput.java
@@ -23,14 +23,14 @@ import org.apache.doris.catalog.Table;
 import org.apache.doris.catalog.Type;
 import org.apache.doris.nereids.analyzer.UnboundRelation;
 import org.apache.doris.nereids.exceptions.UnboundException;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
-import org.apache.doris.nereids.operators.plans.logical.LogicalRelation;
-import org.apache.doris.nereids.operators.plans.physical.PhysicalScan;
-import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.memo.GroupExpression;
+import org.apache.doris.nereids.properties.LogicalProperties;
 import org.apache.doris.nereids.trees.expressions.Slot;
-import org.apache.doris.nereids.trees.plans.Plans;
-import org.apache.doris.nereids.trees.plans.logical.LogicalLeafPlan;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
+import org.apache.doris.nereids.trees.plans.physical.PhysicalRelation;
 import org.apache.doris.nereids.types.IntegerType;
 import org.apache.doris.nereids.types.StringType;
 
@@ -39,17 +39,16 @@ import org.junit.Test;
 import org.junit.jupiter.api.Assertions;
 
 import java.util.List;
+import java.util.Optional;
 
-public class TestPlanOutput implements Plans {
+public class TestPlanOutput {
     @Test
     public void testComputeOutput() {
         Table table = new Table(0L, "a", Table.TableType.OLAP, ImmutableList.<Column>of(
             new Column("id", Type.INT, true, AggregateType.NONE, "0", ""),
             new Column("name", Type.STRING, true, AggregateType.NONE, "", "")
         ));
-        LogicalLeafPlan<LogicalRelation> relationPlan = plan(
-            new LogicalOlapScan(table, ImmutableList.of("a"))
-        );
+        LogicalRelation relationPlan = new LogicalOlapScan(table, ImmutableList.of("a"));
         List<Slot> output = relationPlan.getOutput();
         Assertions.assertEquals(2, output.size());
         Assertions.assertEquals(output.get(0).getName(), "id");
@@ -64,9 +63,7 @@ public class TestPlanOutput implements Plans {
     @Test
     public void testLazyComputeOutput() {
         // not throw exception when create new UnboundRelation
-        LogicalLeafPlan<UnboundRelation> relationPlan = plan(
-            new UnboundRelation(ImmutableList.of("a"))
-        );
+        UnboundRelation relationPlan = new UnboundRelation(ImmutableList.of("a"));
 
         try {
             // throw exception when getOutput
@@ -83,13 +80,11 @@ public class TestPlanOutput implements Plans {
             new Column("id", Type.INT, true, AggregateType.NONE, "0", ""),
             new Column("name", Type.STRING, true, AggregateType.NONE, "", "")
         ));
-        LogicalLeafPlan<LogicalRelation> relationPlan = plan(
-            new LogicalOlapScan(table, ImmutableList.of("a"))
-        );
+        LogicalRelation relationPlan = new LogicalOlapScan(table, ImmutableList.of("a"));
 
         List<Slot> output = relationPlan.getOutput();
         // column prune
-        LogicalLeafPlan<LogicalRelation> newPlan = relationPlan.withOutput(ImmutableList.of(output.get(0)));
+        Plan newPlan = relationPlan.withOutput(ImmutableList.of(output.get(0)));
         output = newPlan.getOutput();
         Assertions.assertEquals(1, output.size());
         Assertions.assertEquals(output.get(0).getName(), "id");
@@ -99,11 +94,16 @@ public class TestPlanOutput implements Plans {
 
     @Test(expected = NullPointerException.class)
     public void testPhysicalPlanMustHaveLogicalProperties() {
-        plan(new PhysicalScan(OperatorType.PHYSICAL_OLAP_SCAN, ImmutableList.of("tbl")) {
+        new PhysicalRelation(PlanType.PHYSICAL_OLAP_SCAN, ImmutableList.of("tbl"), Optional.empty(), null) {
             @Override
-            public List<Expression> getExpressions() {
-                return ImmutableList.of();
+            public Plan withGroupExpression(Optional<GroupExpression> groupExpression) {
+                return null;
             }
-        }, null);
+
+            @Override
+            public Plan withLogicalProperties(Optional<LogicalProperties> logicalProperties) {
+                return null;
+            }
+        };
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProjectTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProjectTest.java
index 61cab85dc5..5ffb9856b8 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProjectTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/implementation/LogicalProjectToPhysicalProjectTest.java
@@ -21,13 +21,12 @@ import org.apache.doris.nereids.PlannerContext;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.OperatorType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.trees.plans.GroupPlan;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.Lists;
@@ -37,11 +36,10 @@ import org.junit.Test;
 
 import java.util.List;
 
-public class LogicalProjectToPhysicalProjectTest implements Plans {
+public class LogicalProjectToPhysicalProjectTest {
     @Test
     public void projectionImplTest(@Mocked Group group) {
-        LogicalProject logicalProject = new LogicalProject(Lists.newArrayList());
-        Plan plan = plan(logicalProject, new GroupPlan(group));
+        Plan plan = new LogicalProject(Lists.newArrayList(), new GroupPlan(group));
 
         Rule<Plan> rule = new LogicalProjectToPhysicalProject().build();
 
@@ -52,6 +50,6 @@ public class LogicalProjectToPhysicalProjectTest implements Plans {
         Assert.assertEquals(1, transform.size());
 
         Plan implPlan = transform.get(0);
-        Assert.assertEquals(OperatorType.PHYSICAL_PROJECT, implPlan.getOperator().getType());
+        Assert.assertEquals(PlanType.PHYSICAL_PROJECT, implPlan.getType());
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java
index 9e7d868afd..b735e8049e 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/AggregateDisassembleTest.java
@@ -25,9 +25,6 @@ import org.apache.doris.nereids.PlannerContext;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.jobs.rewrite.RewriteTopDownJob;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.plans.AggPhase;
-import org.apache.doris.nereids.operators.plans.logical.LogicalAggregate;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.rewrite.AggregateDisassemble;
 import org.apache.doris.nereids.trees.expressions.Add;
@@ -37,9 +34,11 @@ import org.apache.doris.nereids.trees.expressions.Literal;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.expressions.SlotReference;
 import org.apache.doris.nereids.trees.expressions.functions.Sum;
+import org.apache.doris.nereids.trees.plans.AggPhase;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
-import org.apache.doris.nereids.trees.plans.logical.LogicalUnaryPlan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalUnary;
 import org.apache.doris.qe.ConnectContext;
 
 import com.google.common.collect.ImmutableList;
@@ -52,7 +51,7 @@ import org.junit.jupiter.api.TestInstance;
 import java.util.List;
 
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class AggregateDisassembleTest implements Plans {
+public class AggregateDisassembleTest {
     private Plan rStudent;
 
     @BeforeAll
@@ -61,7 +60,7 @@ public class AggregateDisassembleTest implements Plans {
                 ImmutableList.of(new Column("id", Type.INT, true, AggregateType.NONE, true, "0", ""),
                         new Column("name", Type.STRING, true, AggregateType.NONE, true, "", ""),
                         new Column("age", Type.INT, true, AggregateType.NONE, true, "", "")));
-        rStudent = plan(new LogicalOlapScan(student, ImmutableList.of("student")));
+        rStudent = new LogicalOlapScan(student, ImmutableList.of("student"));
     }
 
     /**
@@ -80,7 +79,7 @@ public class AggregateDisassembleTest implements Plans {
         List<NamedExpression> outputExpressionList = Lists.newArrayList(
                 rStudent.getOutput().get(2).toSlot(),
                 new Alias(new Sum(rStudent.getOutput().get(0).toSlot()), "sum"));
-        Plan root = plan(new LogicalAggregate(groupExpressionList, outputExpressionList), rStudent);
+        Plan root = new LogicalAggregate(groupExpressionList, outputExpressionList, rStudent);
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -94,11 +93,11 @@ public class AggregateDisassembleTest implements Plans {
 
         Plan after = memo.copyOut();
 
-        Assertions.assertTrue(after instanceof LogicalUnaryPlan);
-        Assertions.assertTrue(after.getOperator() instanceof LogicalAggregate);
-        Assertions.assertTrue(after.child(0) instanceof LogicalUnaryPlan);
-        LogicalAggregate global = (LogicalAggregate) after.getOperator();
-        LogicalAggregate local = (LogicalAggregate) after.child(0).getOperator();
+        Assertions.assertTrue(after instanceof LogicalUnary);
+        Assertions.assertTrue(after instanceof LogicalAggregate);
+        Assertions.assertTrue(after.child(0) instanceof LogicalUnary);
+        LogicalAggregate<Plan> global = (LogicalAggregate) after;
+        LogicalAggregate<Plan> local = (LogicalAggregate) after.child(0);
         Assertions.assertEquals(AggPhase.GLOBAL, global.getAggPhase());
         Assertions.assertEquals(AggPhase.LOCAL, local.getAggPhase());
 
@@ -149,7 +148,7 @@ public class AggregateDisassembleTest implements Plans {
         List<NamedExpression> outputExpressionList = Lists.newArrayList(
                 new Alias(new Add(rStudent.getOutput().get(2).toSlot(), new Literal(1)), "key"),
                 new Alias(new Sum(rStudent.getOutput().get(0).toSlot()), "sum"));
-        Plan root = plan(new LogicalAggregate(groupExpressionList, outputExpressionList), rStudent);
+        Plan root = new LogicalAggregate<>(groupExpressionList, outputExpressionList, rStudent);
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -163,11 +162,11 @@ public class AggregateDisassembleTest implements Plans {
 
         Plan after = memo.copyOut();
 
-        Assertions.assertTrue(after instanceof LogicalUnaryPlan);
-        Assertions.assertTrue(after.getOperator() instanceof LogicalAggregate);
-        Assertions.assertTrue(after.child(0) instanceof LogicalUnaryPlan);
-        LogicalAggregate global = (LogicalAggregate) after.getOperator();
-        LogicalAggregate local = (LogicalAggregate) after.child(0).getOperator();
+        Assertions.assertTrue(after instanceof LogicalUnary);
+        Assertions.assertTrue(after instanceof LogicalAggregate);
+        Assertions.assertTrue(after.child(0) instanceof LogicalUnary);
+        LogicalAggregate<Plan> global = (LogicalAggregate) after;
+        LogicalAggregate<Plan> local = (LogicalAggregate) after.child(0);
         Assertions.assertEquals(AggPhase.GLOBAL, global.getAggPhase());
         Assertions.assertEquals(AggPhase.LOCAL, local.getAggPhase());
 
@@ -216,7 +215,7 @@ public class AggregateDisassembleTest implements Plans {
         List<Expression> groupExpressionList = Lists.newArrayList();
         List<NamedExpression> outputExpressionList = Lists.newArrayList(
                 new Alias(new Sum(rStudent.getOutput().get(0).toSlot()), "sum"));
-        Plan root = plan(new LogicalAggregate(groupExpressionList, outputExpressionList), rStudent);
+        Plan root = new LogicalAggregate(groupExpressionList, outputExpressionList, rStudent);
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -230,11 +229,11 @@ public class AggregateDisassembleTest implements Plans {
 
         Plan after = memo.copyOut();
 
-        Assertions.assertTrue(after instanceof LogicalUnaryPlan);
-        Assertions.assertTrue(after.getOperator() instanceof LogicalAggregate);
-        Assertions.assertTrue(after.child(0) instanceof LogicalUnaryPlan);
-        LogicalAggregate global = (LogicalAggregate) after.getOperator();
-        LogicalAggregate local = (LogicalAggregate) after.child(0).getOperator();
+        Assertions.assertTrue(after instanceof LogicalUnary);
+        Assertions.assertTrue(after instanceof LogicalAggregate);
+        Assertions.assertTrue(after.child(0) instanceof LogicalUnary);
+        LogicalAggregate<Plan> global = (LogicalAggregate) after;
+        LogicalAggregate<Plan> local = (LogicalAggregate) after.child(0);
         Assertions.assertEquals(AggPhase.GLOBAL, global.getAggPhase());
         Assertions.assertEquals(AggPhase.LOCAL, local.getAggPhase());
 
@@ -272,7 +271,7 @@ public class AggregateDisassembleTest implements Plans {
                 rStudent.getOutput().get(2).toSlot());
         List<NamedExpression> outputExpressionList = Lists.newArrayList(
                 new Alias(new Sum(rStudent.getOutput().get(0).toSlot()), "sum"));
-        Plan root = plan(new LogicalAggregate(groupExpressionList, outputExpressionList), rStudent);
+        Plan root = new LogicalAggregate(groupExpressionList, outputExpressionList, rStudent);
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -286,11 +285,11 @@ public class AggregateDisassembleTest implements Plans {
 
         Plan after = memo.copyOut();
 
-        Assertions.assertTrue(after instanceof LogicalUnaryPlan);
-        Assertions.assertTrue(after.getOperator() instanceof LogicalAggregate);
-        Assertions.assertTrue(after.child(0) instanceof LogicalUnaryPlan);
-        LogicalAggregate global = (LogicalAggregate) after.getOperator();
-        LogicalAggregate local = (LogicalAggregate) after.child(0).getOperator();
+        Assertions.assertTrue(after instanceof LogicalUnary);
+        Assertions.assertTrue(after instanceof LogicalAggregate);
+        Assertions.assertTrue(after.child(0) instanceof LogicalUnary);
+        LogicalAggregate<Plan> global = (LogicalAggregate) after;
+        LogicalAggregate<Plan> local = (LogicalAggregate) after.child(0);
         Assertions.assertEquals(AggPhase.GLOBAL, global.getAggPhase());
         Assertions.assertEquals(AggPhase.LOCAL, local.getAggPhase());
 
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
index ab42450eaf..9b2c2ff397 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/ColumnPruningTest.java
@@ -21,11 +21,11 @@ import org.apache.doris.nereids.PlannerContext;
 import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.jobs.rewrite.RewriteTopDownJob;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
-import org.apache.doris.nereids.operators.plans.logical.LogicalRelation;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.utframe.TestWithFeService;
 
@@ -78,9 +78,9 @@ public class ColumnPruningTest extends TestWithFeService {
         Plan l20 = l1.child(0).child(0);
         Plan l21 = l1.child(0).child(1);
 
-        LogicalProject p1 = (LogicalProject) l1.getOperator();
-        LogicalProject p20 = (LogicalProject) l20.getOperator();
-        LogicalProject p21 = (LogicalProject) l21.getOperator();
+        LogicalProject p1 = (LogicalProject) l1;
+        LogicalProject p20 = (LogicalProject) l20;
+        LogicalProject p21 = (LogicalProject) l21;
 
         List<String> target;
         List<String> source;
@@ -117,9 +117,9 @@ public class ColumnPruningTest extends TestWithFeService {
         Plan l20 = l1.child(0).child(0);
         Plan l21 = l1.child(0).child(1);
 
-        LogicalProject p1 = (LogicalProject) l1.getOperator();
-        LogicalProject p20 = (LogicalProject) l20.getOperator();
-        Assertions.assertTrue(l21.getOperator() instanceof LogicalRelation);
+        LogicalProject p1 = (LogicalProject) l1;
+        LogicalProject p20 = (LogicalProject) l20;
+        Assertions.assertTrue(l21 instanceof LogicalRelation);
 
         List<String> target;
         List<String> source;
@@ -148,7 +148,7 @@ public class ColumnPruningTest extends TestWithFeService {
         Plan out = process(memo);
 
         Plan l1 = out.child(0).child(0);
-        LogicalProject p1 = (LogicalProject) l1.getOperator();
+        LogicalProject p1 = (LogicalProject) l1;
 
         List<String> target;
         List<String> source;
@@ -180,15 +180,15 @@ public class ColumnPruningTest extends TestWithFeService {
         Plan l20Left = l20.child(0).child(0);
         Plan l20Right = l20.child(0).child(1);
 
-        Assertions.assertTrue(l20.getOperator() instanceof LogicalProject);
-        Assertions.assertTrue(l20Left.getOperator() instanceof LogicalProject);
-        Assertions.assertTrue(l20Right.getOperator() instanceof LogicalRelation);
+        Assertions.assertTrue(l20 instanceof LogicalProject);
+        Assertions.assertTrue(l20Left instanceof LogicalProject);
+        Assertions.assertTrue(l20Right instanceof LogicalRelation);
 
-        LogicalProject p1 = (LogicalProject) l1.getOperator();
-        LogicalProject p20 = (LogicalProject) l20.getOperator();
-        LogicalProject p21 = (LogicalProject) l21.getOperator();
+        LogicalProject p1 = (LogicalProject) l1;
+        LogicalProject p20 = (LogicalProject) l20;
+        LogicalProject p21 = (LogicalProject) l21;
 
-        LogicalProject p20lo = (LogicalProject) l20Left.getOperator();
+        LogicalProject p20lo = (LogicalProject) l20Left;
 
         List<String> target;
         List<String> source;
@@ -222,7 +222,7 @@ public class ColumnPruningTest extends TestWithFeService {
         return memo.copyOut();
     }
 
-    private List<String> getStringList(LogicalProject p) {
+    private List<String> getStringList(LogicalProject<Plan> p) {
         return p.getProjects().stream().map(NamedExpression::getQualifiedName).collect(Collectors.toList());
     }
 }
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java
index 86753e89f6..86f9d66aef 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/logical/PushDownPredicateTest.java
@@ -26,12 +26,6 @@ import org.apache.doris.nereids.jobs.JobContext;
 import org.apache.doris.nereids.jobs.rewrite.RewriteTopDownJob;
 import org.apache.doris.nereids.memo.Group;
 import org.apache.doris.nereids.memo.Memo;
-import org.apache.doris.nereids.operators.Operator;
-import org.apache.doris.nereids.operators.plans.JoinType;
-import org.apache.doris.nereids.operators.plans.logical.LogicalFilter;
-import org.apache.doris.nereids.operators.plans.logical.LogicalJoin;
-import org.apache.doris.nereids.operators.plans.logical.LogicalOlapScan;
-import org.apache.doris.nereids.operators.plans.logical.LogicalProject;
 import org.apache.doris.nereids.properties.PhysicalProperties;
 import org.apache.doris.nereids.rules.Rule;
 import org.apache.doris.nereids.trees.expressions.Add;
@@ -44,8 +38,12 @@ import org.apache.doris.nereids.trees.expressions.GreaterThanEqual;
 import org.apache.doris.nereids.trees.expressions.LessThanEqual;
 import org.apache.doris.nereids.trees.expressions.Literal;
 import org.apache.doris.nereids.trees.expressions.Subtract;
+import org.apache.doris.nereids.trees.plans.JoinType;
 import org.apache.doris.nereids.trees.plans.Plan;
-import org.apache.doris.nereids.trees.plans.Plans;
+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.trees.plans.logical.LogicalProject;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.ImmutableList;
@@ -62,7 +60,7 @@ import java.util.Optional;
  * plan rewrite ut.
  */
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
-public class PushDownPredicateTest implements Plans {
+public class PushDownPredicateTest {
 
     private Table student;
     private Table score;
@@ -92,11 +90,11 @@ public class PushDownPredicateTest implements Plans {
                         new Column("name", Type.STRING, true, AggregateType.NONE, "", ""),
                         new Column("teacher", Type.STRING, true, AggregateType.NONE, "", "")));
 
-        rStudent = plan(new LogicalOlapScan(student, ImmutableList.of("student")));
+        rStudent = new LogicalOlapScan(student, ImmutableList.of("student"));
 
-        rScore = plan(new LogicalOlapScan(score, ImmutableList.of("score")));
+        rScore = new LogicalOlapScan(score, ImmutableList.of("score"));
 
-        rCourse = plan(new LogicalOlapScan(course, ImmutableList.of("course")));
+        rCourse = new LogicalOlapScan(course, ImmutableList.of("course"));
     }
 
     @Test
@@ -113,12 +111,13 @@ public class PushDownPredicateTest implements Plans {
         Expression whereCondition = ExpressionUtils.and(whereCondition1, whereCondition2);
 
 
-        Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.of(onCondition)), rStudent, rScore);
-        Plan filter = plan(new LogicalFilter(whereCondition), join);
+        Plan join = new LogicalJoin(JoinType.INNER_JOIN, Optional.of(onCondition), rStudent, rScore);
+        Plan filter = new LogicalFilter(whereCondition, join);
 
-        Plan root = plan(new LogicalProject(
-                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
-                filter);
+        Plan root = new LogicalProject(
+                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2)),
+                filter
+        );
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -137,9 +136,9 @@ public class PushDownPredicateTest implements Plans {
         System.out.println(memo.copyOut().treeString());
         System.out.println(11);
 
-        Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+        Plan op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getPlan();
 
         Assertions.assertTrue(op1 instanceof LogicalJoin);
         Assertions.assertTrue(op2 instanceof LogicalFilter);
@@ -163,12 +162,13 @@ public class PushDownPredicateTest implements Plans {
         Expression whereCondition3 = new GreaterThan(rScore.getOutput().get(2), Literal.of(60));
         Expression whereCondition = ExpressionUtils.and(whereCondition1, whereCondition2, whereCondition3);
 
-        Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), rStudent, rScore);
-        Plan filter = plan(new LogicalFilter(whereCondition), join);
+        Plan join = new LogicalJoin(JoinType.INNER_JOIN, Optional.empty(), rStudent, rScore);
+        Plan filter = new LogicalFilter(whereCondition, join);
 
-        Plan root = plan(new LogicalProject(
-                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
-                filter);
+        Plan root = new LogicalProject(
+                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2)),
+                filter
+        );
 
         Memo memo = new Memo();
         memo.initialize(root);
@@ -186,9 +186,9 @@ public class PushDownPredicateTest implements Plans {
         Group rootGroup = memo.getRoot();
         System.out.println(memo.copyOut().treeString());
 
-        Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+        Plan op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getPlan();
 
         Assertions.assertTrue(op1 instanceof LogicalJoin);
         Assertions.assertTrue(op2 instanceof LogicalFilter);
@@ -228,13 +228,14 @@ public class PushDownPredicateTest implements Plans {
 
         Expression whereCondition = ExpressionUtils.and(whereCondition1, whereCondition2, whereCondition3, whereCondition4);
 
-        Plan join = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), rStudent, rScore);
-        Plan join1 = plan(new LogicalJoin(JoinType.INNER_JOIN, Optional.empty()), join, rCourse);
-        Plan filter = plan(new LogicalFilter(whereCondition), join1);
+        Plan join = new LogicalJoin(JoinType.INNER_JOIN, Optional.empty(), rStudent, rScore);
+        Plan join1 = new LogicalJoin(JoinType.INNER_JOIN, Optional.empty(), join, rCourse);
+        Plan filter = new LogicalFilter(whereCondition, join1);
 
-        Plan root = plan(new LogicalProject(
-                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2))),
-                filter);
+        Plan root = new LogicalProject(
+                Lists.newArrayList(rStudent.getOutput().get(1), rCourse.getOutput().get(1), rScore.getOutput().get(2)),
+                filter
+        );
 
 
         Memo memo = new Memo();
@@ -252,10 +253,10 @@ public class PushDownPredicateTest implements Plans {
 
         Group rootGroup = memo.getRoot();
         System.out.println(memo.copyOut().treeString());
-        Operator join2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator join3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getOperator();
-        Operator op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getOperator();
+        Plan join2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan join3 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op1 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().getPlan();
+        Plan op2 = rootGroup.getLogicalExpression().child(0).getLogicalExpression().child(0).getLogicalExpression().child(1).getLogicalExpression().getPlan();
 
         Assertions.assertTrue(join2 instanceof LogicalJoin);
         Assertions.assertTrue(join3 instanceof LogicalJoin);


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