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

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


The following commit(s) were added to refs/heads/master by this push:
     new da40e1c7670 [feature](nereids) Matiarilzed view query rewrite util 
implementation (#27568)
da40e1c7670 is described below

commit da40e1c7670492c747c284d7af7af2f874d01038
Author: seawinde <149132972+seawi...@users.noreply.github.com>
AuthorDate: Tue Dec 5 11:48:04 2023 +0800

    [feature](nereids) Matiarilzed view query rewrite util implementation 
(#27568)
    
    The basic util implementatation which is used by materialized view rewrite
---
 .../mv/AbstractMaterializedViewJoinRule.java       |   1 +
 .../mv/AbstractMaterializedViewRule.java           |   9 +-
 .../nereids/rules/exploration/mv/Predicates.java   |  56 ++--
 .../rules/exploration/mv/PredicatesSplitter.java   | 112 ++++++++
 .../rules/exploration/mv/RelationMapping.java      |  63 -----
 .../nereids/rules/exploration/mv/SlotMapping.java  |  49 ----
 .../nereids/rules/exploration/mv/StructInfo.java   |   2 +-
 .../mv/mapping/ExpressionIndexMapping.java         |  48 ++++
 .../exploration/mv/{ => mapping}/Mapping.java      |  61 ++--
 .../exploration/mv/mapping/RelationMapping.java    | 113 ++++++++
 .../rules/exploration/mv/mapping/SlotMapping.java  |  84 ++++++
 .../expressions/visitor/ExpressionVisitors.java    |  49 ----
 .../plans/visitor/ExpressionLineageReplacer.java   | 160 +++++++++++
 .../apache/doris/nereids/util/ExpressionUtils.java |  30 +-
 .../nereids/rules/exploration/mv/MappingTest.java  | 309 +++++++++++++++++++++
 .../expression/ExpressionRewriteTestHelper.java    |   2 +-
 .../rules/expression/PredicatesSplitterTest.java   | 109 ++++++++
 .../doris/nereids/util/ExpressionUtilsTest.java    | 106 ++++++-
 18 files changed, 1117 insertions(+), 246 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
index 17df9ef88ed..899ba216e99 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewJoinRule.java
@@ -17,6 +17,7 @@
 
 package org.apache.doris.nereids.rules.exploration.mv;
 
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
 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;
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
index c52acdb3fda..2dc64ccfeab 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/AbstractMaterializedViewRule.java
@@ -20,8 +20,10 @@ package org.apache.doris.nereids.rules.exploration.mv;
 import org.apache.doris.catalog.TableIf;
 import org.apache.doris.nereids.CascadesContext;
 import org.apache.doris.nereids.memo.Group;
-import 
org.apache.doris.nereids.rules.exploration.mv.Mapping.ExpressionIndexMapping;
 import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
+import 
org.apache.doris.nereids.rules.exploration.mv.mapping.ExpressionIndexMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.NamedExpression;
 import org.apache.doris.nereids.trees.plans.Plan;
@@ -141,8 +143,9 @@ public abstract class AbstractMaterializedViewRule {
                 targetTopExpressions, targetStructInfo.getOriginalPlan(), 
Sets.newHashSet(), Sets.newHashSet());
         SlotMapping sourceToTargetSlotMapping = 
SlotMapping.generate(sourceToTargetMapping);
         // mv sql plan expressions transform to query based
-        List<? extends Expression> queryBasedExpressions = 
ExpressionUtils.permute(shuttledTargetExpressions,
-                sourceToTargetSlotMapping.inverse());
+        List<? extends Expression> queryBasedExpressions = 
ExpressionUtils.replace(
+                
shuttledTargetExpressions.stream().map(Expression.class::cast).collect(Collectors.toList()),
+                sourceToTargetSlotMapping.inverse().getSlotMap());
         // mv sql query based expression and index mapping
         ExpressionIndexMapping.generate(queryBasedExpressions);
         // TODO visit source expression and replace the expression with 
expressionIndexMapping
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
index 40b91994be7..43aab2389ca 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Predicates.java
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.rules.exploration.mv;
 
 import org.apache.doris.nereids.trees.expressions.Expression;
 import org.apache.doris.nereids.trees.expressions.literal.BooleanLiteral;
-import 
org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitors.PredicatesSpliter;
 import org.apache.doris.nereids.util.ExpressionUtils;
 
 import com.google.common.collect.ImmutableList;
@@ -55,8 +54,7 @@ public class Predicates {
      * Split the expression to equal, range and residual predicate.
      * */
     public static SplitPredicate splitPredicates(Expression expression) {
-        PredicatesSpliter predicatesSplit = new PredicatesSpliter(expression);
-        expression.accept(predicatesSplit, null);
+        PredicatesSplitter predicatesSplit = new 
PredicatesSplitter(expression);
         return predicatesSplit.getSplitPredicate();
     }
 
@@ -64,26 +62,26 @@ public class Predicates {
      * The split different representation for predicate expression, such as 
equal, range and residual predicate.
      * */
     public static final class SplitPredicate {
-        private final Expression equalPredicates;
-        private final Expression rangePredicates;
-        private final Expression residualPredicates;
-
-        public SplitPredicate(Expression equalPredicates, Expression 
rangePredicates, Expression residualPredicates) {
-            this.equalPredicates = equalPredicates;
-            this.rangePredicates = rangePredicates;
-            this.residualPredicates = residualPredicates;
+        private final Expression equalPredicate;
+        private final Expression rangePredicate;
+        private final Expression residualPredicate;
+
+        public SplitPredicate(Expression equalPredicate, Expression 
rangePredicate, Expression residualPredicate) {
+            this.equalPredicate = equalPredicate;
+            this.rangePredicate = rangePredicate;
+            this.residualPredicate = residualPredicate;
         }
 
-        public Expression getEqualPredicates() {
-            return equalPredicates;
+        public Expression getEqualPredicate() {
+            return equalPredicate;
         }
 
-        public Expression getRangePredicates() {
-            return rangePredicates;
+        public Expression getRangePredicate() {
+            return rangePredicate;
         }
 
-        public Expression getResidualPredicates() {
-            return residualPredicates;
+        public Expression getResidualPredicate() {
+            return residualPredicate;
         }
 
         public static SplitPredicate empty() {
@@ -103,29 +101,25 @@ public class Predicates {
          * isEmpty
          * */
         public boolean isEmpty() {
-            return equalPredicates == null
-                    && rangePredicates == null
-                    && residualPredicates == null;
-        }
-
-        public Expression composedExpression() {
-            return ExpressionUtils.and(equalPredicates, rangePredicates, 
residualPredicates);
+            return equalPredicate == null
+                    && rangePredicate == null
+                    && residualPredicate == null;
         }
 
         public List<Expression> toList() {
-            return ImmutableList.of(equalPredicates, rangePredicates, 
residualPredicates);
+            return ImmutableList.of(equalPredicate, rangePredicate, 
residualPredicate);
         }
 
         /**
          * Check the predicates in SplitPredicate is whether all true or not
          */
         public boolean isAlwaysTrue() {
-            return equalPredicates instanceof BooleanLiteral
-                    && rangePredicates instanceof BooleanLiteral
-                    && residualPredicates instanceof BooleanLiteral
-                    && ((BooleanLiteral) equalPredicates).getValue()
-                    && ((BooleanLiteral) rangePredicates).getValue()
-                    && ((BooleanLiteral) residualPredicates).getValue();
+            return equalPredicate instanceof BooleanLiteral
+                    && rangePredicate instanceof BooleanLiteral
+                    && residualPredicate instanceof BooleanLiteral
+                    && ((BooleanLiteral) equalPredicate).getValue()
+                    && ((BooleanLiteral) rangePredicate).getValue()
+                    && ((BooleanLiteral) residualPredicate).getValue();
         }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.java
new file mode 100644
index 00000000000..e90d6583eac
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/PredicatesSplitter.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.rules.exploration.mv;
+
+import org.apache.doris.nereids.trees.expressions.Alias;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
+import org.apache.doris.nereids.trees.expressions.CompoundPredicate;
+import org.apache.doris.nereids.trees.expressions.EqualPredicate;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.util.ExpressionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Split the expression to equal, range and residual predicate.
+ * Should instance when used.
+ * TODO support complex predicate split
+ */
+public class PredicatesSplitter {
+
+    private final List<Expression> equalPredicates = new ArrayList<>();
+    private final List<Expression> rangePredicates = new ArrayList<>();
+    private final List<Expression> residualPredicates = new ArrayList<>();
+    private final List<Expression> conjunctExpressions;
+
+    private final PredicateExtract instance = new PredicateExtract();
+
+    public PredicatesSplitter(Expression target) {
+        this.conjunctExpressions = ExpressionUtils.extractConjunction(target);
+        for (Expression expression : conjunctExpressions) {
+            expression.accept(instance, expression);
+        }
+    }
+
+    /**
+     * PredicateExtract
+     */
+    public class PredicateExtract extends DefaultExpressionVisitor<Void, 
Expression> {
+
+        @Override
+        public Void visitComparisonPredicate(ComparisonPredicate 
comparisonPredicate, Expression sourceExpression) {
+            Expression leftArg = comparisonPredicate.getArgument(0);
+            Expression rightArg = comparisonPredicate.getArgument(1);
+            boolean leftArgOnlyContainsColumnRef = 
containOnlyColumnRef(leftArg, true);
+            boolean rightArgOnlyContainsColumnRef = 
containOnlyColumnRef(rightArg, true);
+            if (comparisonPredicate instanceof EqualPredicate) {
+                if (leftArgOnlyContainsColumnRef && 
rightArgOnlyContainsColumnRef) {
+                    equalPredicates.add(comparisonPredicate);
+                    return null;
+                } else {
+                    residualPredicates.add(comparisonPredicate);
+                }
+            } else if ((leftArgOnlyContainsColumnRef && rightArg instanceof 
Literal)
+                    || (rightArgOnlyContainsColumnRef && leftArg instanceof 
Literal)) {
+                rangePredicates.add(comparisonPredicate);
+            } else {
+                residualPredicates.add(comparisonPredicate);
+            }
+            return null;
+        }
+
+        @Override
+        public Void visitCompoundPredicate(CompoundPredicate 
compoundPredicate, Expression context) {
+            if (compoundPredicate instanceof Or) {
+                residualPredicates.add(compoundPredicate);
+                return null;
+            }
+            return super.visit(compoundPredicate, context);
+        }
+    }
+
+    public Predicates.SplitPredicate getSplitPredicate() {
+        return Predicates.SplitPredicate.of(
+                equalPredicates.isEmpty() ? null : 
ExpressionUtils.and(equalPredicates),
+                rangePredicates.isEmpty() ? null : 
ExpressionUtils.and(rangePredicates),
+                residualPredicates.isEmpty() ? null : 
ExpressionUtils.and(residualPredicates));
+    }
+
+    private static boolean containOnlyColumnRef(Expression expression, boolean 
allowCast) {
+        if (expression instanceof SlotReference && ((SlotReference) 
expression).isColumnFromTable()) {
+            return true;
+        }
+        if (allowCast && expression instanceof Cast) {
+            return containOnlyColumnRef(((Cast) expression).child(), true);
+        }
+        if (expression instanceof Alias) {
+            return containOnlyColumnRef(((Alias) expression).child(), true);
+        }
+        return false;
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
deleted file mode 100644
index ea91104a246..00000000000
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/RelationMapping.java
+++ /dev/null
@@ -1,63 +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.rules.exploration.mv;
-
-import org.apache.doris.catalog.TableIf;
-import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Multimap;
-
-import java.util.List;
-
-/**
- * Relation mapping
- * such as query pattern is a1 left join a2 left join b
- * view pattern is a1 left join a2 left join b. the mapping will be
- * [{a1:a1, a2:a2, b:b}, {a1:a2, a2:a1, b:b}]
- */
-public class RelationMapping extends Mapping {
-
-    private final BiMap<MappedRelation, MappedRelation> mappedRelationMap;
-
-    public RelationMapping(BiMap<MappedRelation, MappedRelation> 
mappedRelationMap) {
-        this.mappedRelationMap = mappedRelationMap;
-    }
-
-    public BiMap<MappedRelation, MappedRelation> getMappedRelationMap() {
-        return mappedRelationMap;
-    }
-
-    /**
-     * Generate mapping according to source and target relation
-     */
-    public static List<RelationMapping> generate(List<CatalogRelation> source, 
List<CatalogRelation> target) {
-        Multimap<TableIf, CatalogRelation> queryTableRelationIdMap = 
ArrayListMultimap.create();
-        for (CatalogRelation relation : source) {
-            queryTableRelationIdMap.put(relation.getTable(), relation);
-        }
-        Multimap<TableIf, CatalogRelation> viewTableRelationIdMap = 
ArrayListMultimap.create();
-        for (CatalogRelation relation : target) {
-            viewTableRelationIdMap.put(relation.getTable(), relation);
-        }
-        // todo generate relation map
-        return ImmutableList.of();
-    }
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
deleted file mode 100644
index 7c50d79c6a2..00000000000
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/SlotMapping.java
+++ /dev/null
@@ -1,49 +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.rules.exploration.mv;
-
-import com.google.common.collect.BiMap;
-
-/**
- * SlotMapping, this is open generated from relationMapping
- */
-public class SlotMapping extends Mapping {
-
-    private final BiMap<MappedSlot, MappedSlot> relationSlotMap;
-
-    public SlotMapping(BiMap<MappedSlot, MappedSlot> relationSlotMap) {
-        this.relationSlotMap = relationSlotMap;
-    }
-
-    public BiMap<MappedSlot, MappedSlot> getRelationSlotMap() {
-        return relationSlotMap;
-    }
-
-    public SlotMapping inverse() {
-        return SlotMapping.of(relationSlotMap.inverse());
-    }
-
-    public static SlotMapping of(BiMap<MappedSlot, MappedSlot> 
relationSlotMap) {
-        return new SlotMapping(relationSlotMap);
-    }
-
-    public static SlotMapping generate(RelationMapping relationMapping) {
-        // TODO implement
-        return SlotMapping.of(null);
-    }
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
index 141b8a98bcc..ff9dee3ca60 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/StructInfo.java
@@ -52,7 +52,7 @@ public class StructInfo {
         // construct equivalenceClass according to equals predicates
         this.equivalenceClass = new EquivalenceClass();
         SplitPredicate splitPredicate = 
Predicates.splitPredicates(predicates.composedExpression());
-        for (Expression expression : 
ExpressionUtils.extractConjunction(splitPredicate.getEqualPredicates())) {
+        for (Expression expression : 
ExpressionUtils.extractConjunction(splitPredicate.getEqualPredicate())) {
             EqualTo equalTo = (EqualTo) expression;
             equivalenceClass.addEquivalenceClass(
                     (SlotReference) equalTo.getArguments().get(0),
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
new file mode 100644
index 00000000000..f63017633a8
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/ExpressionIndexMapping.java
@@ -0,0 +1,48 @@
+// 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.rules.exploration.mv.mapping;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+import java.util.List;
+
+/**
+ * Expression and it's index mapping
+ */
+public class ExpressionIndexMapping extends Mapping {
+    private final Multimap<Expression, Integer> expressionIndexMapping;
+
+    public ExpressionIndexMapping(Multimap<Expression, Integer> 
expressionIndexMapping) {
+        this.expressionIndexMapping = expressionIndexMapping;
+    }
+
+    public Multimap<Expression, Integer> getExpressionIndexMapping() {
+        return expressionIndexMapping;
+    }
+
+    public static ExpressionIndexMapping generate(List<? extends Expression> 
expressions) {
+        Multimap<Expression, Integer> expressionIndexMapping = 
ArrayListMultimap.create();
+        for (int i = 0; i < expressions.size(); i++) {
+            expressionIndexMapping.put(expressions.get(i), i);
+        }
+        return new ExpressionIndexMapping(expressionIndexMapping);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
similarity index 69%
rename from 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
rename to 
fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
index 487fc92ce50..17a412dab10 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/Mapping.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/Mapping.java
@@ -15,18 +15,15 @@
 // specific language governing permissions and limitations
 // under the License.
 
-package org.apache.doris.nereids.rules.exploration.mv;
+package org.apache.doris.nereids.rules.exploration.mv.mapping;
 
 import org.apache.doris.nereids.trees.expressions.ExprId;
-import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
 import org.apache.doris.nereids.trees.plans.RelationId;
 import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
 
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Multimap;
-
-import java.util.List;
 import java.util.Objects;
+import javax.annotation.Nullable;
 
 /**
  * Mapping slot from query to view or inversely,
@@ -38,6 +35,7 @@ public abstract class Mapping {
      * The relation for mapping
      */
     public static final class MappedRelation {
+
         public final RelationId relationId;
         public final CatalogRelation belongedRelation;
 
@@ -46,7 +44,7 @@ public abstract class Mapping {
             this.belongedRelation = belongedRelation;
         }
 
-        public MappedRelation of(RelationId relationId, CatalogRelation 
belongedRelation) {
+        public static MappedRelation of(RelationId relationId, CatalogRelation 
belongedRelation) {
             return new MappedRelation(relationId, belongedRelation);
         }
 
@@ -82,15 +80,31 @@ public abstract class Mapping {
     public static final class MappedSlot {
 
         public final ExprId exprId;
+        public final Slot slot;
+        @Nullable
         public final CatalogRelation belongedRelation;
 
-        public MappedSlot(ExprId exprId, CatalogRelation belongedRelation) {
+        public MappedSlot(ExprId exprId,
+                Slot slot,
+                CatalogRelation belongedRelation) {
             this.exprId = exprId;
+            this.slot = slot;
             this.belongedRelation = belongedRelation;
         }
 
-        public MappedSlot of(ExprId exprId, CatalogRelation belongedRelation) {
-            return new MappedSlot(exprId, belongedRelation);
+        public static MappedSlot of(ExprId exprId,
+                Slot slot,
+                CatalogRelation belongedRelation) {
+            return new MappedSlot(exprId, slot, belongedRelation);
+        }
+
+        public static MappedSlot of(Slot slot,
+                CatalogRelation belongedRelation) {
+            return new MappedSlot(slot.getExprId(), slot, belongedRelation);
+        }
+
+        public static MappedSlot of(Slot slot) {
+            return new MappedSlot(slot.getExprId(), slot, null);
         }
 
         public ExprId getExprId() {
@@ -101,6 +115,10 @@ public abstract class Mapping {
             return belongedRelation;
         }
 
+        public Slot getSlot() {
+            return slot;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (this == o) {
@@ -118,27 +136,4 @@ public abstract class Mapping {
             return Objects.hash(exprId);
         }
     }
-
-    /**
-     * Expression and it's index mapping
-     */
-    public static class ExpressionIndexMapping extends Mapping {
-        private final Multimap<Expression, Integer> expressionIndexMapping;
-
-        public ExpressionIndexMapping(Multimap<Expression, Integer> 
expressionIndexMapping) {
-            this.expressionIndexMapping = expressionIndexMapping;
-        }
-
-        public Multimap<Expression, Integer> getExpressionIndexMapping() {
-            return expressionIndexMapping;
-        }
-
-        public static ExpressionIndexMapping generate(List<? extends 
Expression> expressions) {
-            Multimap<Expression, Integer> expressionIndexMapping = 
ArrayListMultimap.create();
-            for (int i = 0; i < expressions.size(); i++) {
-                expressionIndexMapping.put(expressions.get(i), i);
-            }
-            return new ExpressionIndexMapping(expressionIndexMapping);
-        }
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
new file mode 100644
index 00000000000..fdf47d13433
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/RelationMapping.java
@@ -0,0 +1,113 @@
+// 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.rules.exploration.mv.mapping;
+
+import org.apache.doris.catalog.TableIf;
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableBiMap.Builder;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Relation mapping
+ * such as query pattern is a1 left join a2 left join b
+ * view pattern is a1 left join a2 left join b. the mapping will be
+ * [{a1:a1, a2:a2, b:b}, {a1:a2, a2:a1, b:b}]
+ */
+public class RelationMapping extends Mapping {
+
+    private final ImmutableBiMap<MappedRelation, MappedRelation> 
mappedRelationMap;
+
+    public RelationMapping(ImmutableBiMap<MappedRelation, MappedRelation> 
mappedRelationMap) {
+        this.mappedRelationMap = mappedRelationMap;
+    }
+
+    public BiMap<MappedRelation, MappedRelation> getMappedRelationMap() {
+        return mappedRelationMap;
+    }
+
+    public static RelationMapping of(ImmutableBiMap<MappedRelation, 
MappedRelation> mappedRelationMap) {
+        return new RelationMapping(mappedRelationMap);
+    }
+
+    /**
+     * Generate mapping according to source and target relation
+     */
+    public static List<RelationMapping> generate(List<CatalogRelation> 
sources, List<CatalogRelation> targets) {
+        // Construct tmp map, key is the table qualifier, value is the 
corresponding catalog relations
+        LinkedListMultimap<Long, MappedRelation> sourceTableRelationIdMap = 
LinkedListMultimap.create();
+        for (CatalogRelation relation : sources) {
+            
sourceTableRelationIdMap.put(getTableQualifier(relation.getTable()),
+                    MappedRelation.of(relation.getRelationId(), relation));
+        }
+        LinkedListMultimap<Long, MappedRelation> targetTableRelationIdMap = 
LinkedListMultimap.create();
+        for (CatalogRelation relation : targets) {
+            
targetTableRelationIdMap.put(getTableQualifier(relation.getTable()),
+                    MappedRelation.of(relation.getRelationId(), relation));
+        }
+        Set<Long> sourceTableKeySet = sourceTableRelationIdMap.keySet();
+        List<List<Pair<MappedRelation, MappedRelation>>> mappedRelations = new 
ArrayList<>();
+
+        for (Long sourceTableQualifier : sourceTableKeySet) {
+            List<MappedRelation> sourceMappedRelations = 
sourceTableRelationIdMap.get(sourceTableQualifier);
+            List<MappedRelation> targetMappedRelations = 
targetTableRelationIdMap.get(sourceTableQualifier);
+            if (targetMappedRelations.isEmpty()) {
+                continue;
+            }
+            // if source and target relation appear once, just map them
+            if (targetMappedRelations.size() == 1 && 
sourceMappedRelations.size() == 1) {
+                
mappedRelations.add(ImmutableList.of(Pair.of(sourceMappedRelations.get(0),
+                        targetMappedRelations.get(0))));
+                continue;
+            }
+            // relation appear more than once, should cartesian them
+            ImmutableList<Pair<MappedRelation, MappedRelation>> 
relationMapping = Lists.cartesianProduct(
+                            
sourceTableRelationIdMap.get(sourceTableQualifier), targetMappedRelations)
+                    .stream()
+                    .map(listPair -> Pair.of(listPair.get(0), listPair.get(1)))
+                    .collect(ImmutableList.toImmutableList());
+            mappedRelations.add(relationMapping);
+        }
+
+        int mappedRelationCount = mappedRelations.size();
+
+        return Lists.cartesianProduct(mappedRelations).stream()
+                .map(mappedRelationList -> {
+                    Builder<MappedRelation, MappedRelation> mapBuilder = 
ImmutableBiMap.builder();
+                    for (int relationIndex = 0; relationIndex < 
mappedRelationCount; relationIndex++) {
+                        
mapBuilder.put(mappedRelationList.get(relationIndex).key(),
+                                mappedRelationList.get(relationIndex).value());
+                    }
+                    return RelationMapping.of(mapBuilder.build());
+                })
+                .collect(ImmutableList.toImmutableList());
+    }
+
+    private static Long getTableQualifier(TableIf tableIf) {
+        return tableIf.getId();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
new file mode 100644
index 00000000000..520d6be9cdd
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/exploration/mv/mapping/SlotMapping.java
@@ -0,0 +1,84 @@
+// 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.rules.exploration.mv.mapping;
+
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Slot;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * SlotMapping, this is open generated from relationMapping
+ */
+public class SlotMapping extends Mapping {
+
+    private final BiMap<MappedSlot, MappedSlot> slotMapping;
+
+    public SlotMapping(BiMap<MappedSlot, MappedSlot> slotMapping) {
+        this.slotMapping = slotMapping;
+    }
+
+    public BiMap<MappedSlot, MappedSlot> getSlotBiMap() {
+        return slotMapping;
+    }
+
+    public SlotMapping inverse() {
+        return slotMapping == null
+                ? SlotMapping.of(HashBiMap.create()) : 
SlotMapping.of(slotMapping.inverse());
+    }
+
+    public static SlotMapping of(BiMap<MappedSlot, MappedSlot> 
relationSlotMap) {
+        return new SlotMapping(relationSlotMap);
+    }
+
+    /**
+     * SlotMapping, this is open generated from relationMapping
+     */
+    @Nullable
+    public static SlotMapping generate(RelationMapping relationMapping) {
+        BiMap<MappedSlot, MappedSlot> relationSlotMap = HashBiMap.create();
+        BiMap<MappedRelation, MappedRelation> mappedRelationMap = 
relationMapping.getMappedRelationMap();
+        for (Map.Entry<MappedRelation, MappedRelation> mappedRelationEntry : 
mappedRelationMap.entrySet()) {
+            Map<String, Slot> targetNameSlotMap =
+                    
mappedRelationEntry.getValue().getBelongedRelation().getOutput().stream()
+                            .collect(Collectors.toMap(Slot::getName, slot -> 
slot));
+            for (Slot sourceSlot : 
mappedRelationEntry.getKey().getBelongedRelation().getOutput()) {
+                Slot targetSlot = targetNameSlotMap.get(sourceSlot.getName());
+                // source slot can not map from target, bail out
+                if (targetSlot == null) {
+                    return null;
+                }
+                relationSlotMap.put(MappedSlot.of(sourceSlot, 
mappedRelationEntry.getKey().getBelongedRelation()),
+                        MappedSlot.of(targetSlot, 
mappedRelationEntry.getValue().getBelongedRelation()));
+            }
+        }
+        return SlotMapping.of(relationSlotMap);
+    }
+
+    /**
+     * SlotMapping, getSlotMap
+     */
+    public Map<? extends Expression, ? extends Expression> getSlotMap() {
+        return (Map) this.getSlotBiMap();
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
index 86949f63e16..513da0e93d9 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/visitor/ExpressionVisitors.java
@@ -17,16 +17,9 @@
 
 package org.apache.doris.nereids.trees.expressions.visitor;
 
-import org.apache.doris.nereids.rules.exploration.mv.Predicates;
-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.WindowExpression;
 import 
org.apache.doris.nereids.trees.expressions.functions.agg.AggregateFunction;
-import org.apache.doris.nereids.util.ExpressionUtils;
-
-import java.util.ArrayList;
-import java.util.List;
 
 /**
  * This is the factory for all ExpressionVisitor instance.
@@ -61,46 +54,4 @@ public class ExpressionVisitors {
             return true;
         }
     }
-
-    /**
-     * Split the expression to equal, range and residual predicate.
-     * Should instance when used.
-     */
-    public static class PredicatesSpliter extends 
DefaultExpressionVisitor<Void, Void> {
-
-        private List<Expression> equalPredicates = new ArrayList<>();
-        private List<Expression> rangePredicates = new ArrayList<>();
-        private List<Expression> residualPredicates = new ArrayList<>();
-        private final Expression target;
-
-        public PredicatesSpliter(Expression target) {
-            this.target = target;
-        }
-
-        @Override
-        public Void visitComparisonPredicate(ComparisonPredicate 
comparisonPredicate, Void context) {
-            // TODO Smallest implement, complete later
-            if (comparisonPredicate instanceof EqualTo) {
-                Expression leftArgument = comparisonPredicate.getArgument(0);
-                Expression rightArgument = comparisonPredicate.getArgument(1);
-                if (leftArgument.isSlot() && rightArgument.isSlot()) {
-                    equalPredicates.add(comparisonPredicate);
-                } else {
-                    rangePredicates.add(comparisonPredicate);
-                }
-            }
-            return super.visit(comparisonPredicate, context);
-        }
-
-        public Expression getTarget() {
-            return target;
-        }
-
-        public Predicates.SplitPredicate getSplitPredicate() {
-            return Predicates.SplitPredicate.of(
-                    equalPredicates.isEmpty() ? null : 
ExpressionUtils.and(equalPredicates),
-                    rangePredicates.isEmpty() ? null : 
ExpressionUtils.and(rangePredicates),
-                    residualPredicates.isEmpty() ? null : 
ExpressionUtils.and(residualPredicates));
-        }
-    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
new file mode 100644
index 00000000000..888b538d049
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/ExpressionLineageReplacer.java
@@ -0,0 +1,160 @@
+// 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.catalog.TableIf.TableType;
+import org.apache.doris.nereids.trees.expressions.Alias;
+import 
org.apache.doris.nereids.trees.expressions.ArrayItemReference.ArrayItemSlot;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+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.visitor.DefaultExpressionRewriter;
+import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.trees.plans.Plan;
+import 
org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer.ExpressionReplaceContext;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * ExpressionLineageReplacer
+ * Get from rewrite plan and can also get from plan struct info, if from plan 
struct info it depends on
+ * the nodes from graph.
+ */
+public class ExpressionLineageReplacer extends DefaultPlanVisitor<Expression, 
ExpressionReplaceContext> {
+
+    public static final ExpressionLineageReplacer INSTANCE = new 
ExpressionLineageReplacer();
+
+    @Override
+    public Expression visit(Plan plan, ExpressionReplaceContext context) {
+        List<? extends Expression> expressions = plan.getExpressions();
+        Map<ExprId, Expression> targetExpressionMap = 
context.getExprIdExpressionMap();
+        // Filter the namedExpression used by target and collect the 
namedExpression
+        expressions.stream()
+                .filter(expression -> expression instanceof NamedExpression
+                        && targetExpressionMap.containsKey(((NamedExpression) 
expression).getExprId()))
+                .forEach(expression -> 
expression.accept(NamedExpressionCollector.INSTANCE, context));
+        return super.visit(plan, context);
+    }
+
+    /**
+     * Replace the expression with lineage according the exprIdExpressionMap
+     */
+    public static class ExpressionReplacer extends 
DefaultExpressionRewriter<Map<ExprId, Expression>> {
+
+        public static final ExpressionReplacer INSTANCE = new 
ExpressionReplacer();
+
+        @Override
+        public Expression visitNamedExpression(NamedExpression namedExpression,
+                Map<ExprId, Expression> exprIdExpressionMap) {
+            if (exprIdExpressionMap.containsKey(namedExpression.getExprId())) {
+                return 
super.visit(exprIdExpressionMap.get(namedExpression.getExprId()), 
exprIdExpressionMap);
+            }
+            return super.visitNamedExpression(namedExpression, 
exprIdExpressionMap);
+        }
+    }
+
+    /**
+     * The Collector for target named expressions
+     * TODO Collect named expression by targetTypes, tableIdentifiers
+     */
+    public static class NamedExpressionCollector
+            extends DefaultExpressionVisitor<Void, ExpressionReplaceContext> {
+
+        public static final NamedExpressionCollector INSTANCE = new 
NamedExpressionCollector();
+
+        @Override
+        public Void visitSlotReference(SlotReference slotReference, 
ExpressionReplaceContext context) {
+            context.getExprIdExpressionMap().put(slotReference.getExprId(), 
slotReference);
+            return super.visitSlotReference(slotReference, context);
+        }
+
+        @Override
+        public Void visitArrayItemSlot(ArrayItemSlot arrayItemSlot, 
ExpressionReplaceContext context) {
+            context.getExprIdExpressionMap().put(arrayItemSlot.getExprId(), 
arrayItemSlot);
+            return super.visitArrayItemSlot(arrayItemSlot, context);
+        }
+
+        @Override
+        public Void visitAlias(Alias alias, ExpressionReplaceContext context) {
+            // remove the alias
+            if 
(context.getExprIdExpressionMap().containsKey(alias.getExprId())) {
+                context.getExprIdExpressionMap().put(alias.getExprId(), 
alias.child());
+            }
+            return super.visitAlias(alias, context);
+        }
+    }
+
+    /**
+     * The context for replacing the expression with lineage
+     */
+    public static class ExpressionReplaceContext {
+        private final List<Expression> targetExpressions;
+        private final Set<TableType> targetTypes;
+        private final Set<String> tableIdentifiers;
+        private Map<ExprId, Expression> exprIdExpressionMap;
+        private List<Expression> replacedExpressions;
+
+        /**ExpressionReplaceContext*/
+        public ExpressionReplaceContext(List<Expression> targetExpressions,
+                Set<TableType> targetTypes,
+                Set<String> tableIdentifiers) {
+            this.targetExpressions = targetExpressions;
+            this.targetTypes = targetTypes;
+            this.tableIdentifiers = tableIdentifiers;
+            // collect only named expressions and replace them with linage 
identifier later
+            this.exprIdExpressionMap = targetExpressions.stream()
+                    .map(each -> 
each.collectToList(NamedExpression.class::isInstance))
+                    .flatMap(Collection::stream)
+                    .map(NamedExpression.class::cast)
+                    .collect(Collectors.toMap(NamedExpression::getExprId, expr 
-> expr));
+        }
+
+        public List<Expression> getTargetExpressions() {
+            return targetExpressions;
+        }
+
+        public Set<TableType> getTargetTypes() {
+            return targetTypes;
+        }
+
+        public Set<String> getTableIdentifiers() {
+            return tableIdentifiers;
+        }
+
+        public Map<ExprId, Expression> getExprIdExpressionMap() {
+            return exprIdExpressionMap;
+        }
+
+        /**
+         * getReplacedExpressions
+         */
+        public List<Expression> getReplacedExpressions() {
+            if (this.replacedExpressions == null) {
+                this.replacedExpressions = targetExpressions.stream()
+                        .map(original -> 
original.accept(ExpressionReplacer.INSTANCE, getExprIdExpressionMap()))
+                        .collect(Collectors.toList());
+            }
+            return this.replacedExpressions;
+        }
+    }
+}
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 3149d140fe4..c44e888fbc8 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
@@ -19,7 +19,6 @@ package org.apache.doris.nereids.util;
 
 import org.apache.doris.catalog.TableIf.TableType;
 import org.apache.doris.nereids.CascadesContext;
-import org.apache.doris.nereids.rules.exploration.mv.SlotMapping;
 import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
 import org.apache.doris.nereids.rules.expression.rules.FoldConstantRule;
 import org.apache.doris.nereids.trees.TreeNode;
@@ -48,6 +47,7 @@ import 
org.apache.doris.nereids.trees.expressions.literal.Literal;
 import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
 import 
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
 import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.visitor.ExpressionLineageReplacer;
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
@@ -206,28 +206,28 @@ public class ExpressionUtils {
     }
 
     /**
-     * Replace the slot in expression with the lineage identifier from 
specified
-     * baseTable sets or target table types.
-     * <p>
-     * For example as following:
+     * Replace the slot in expressions with the lineage identifier from 
specifiedbaseTable sets or target table types
+     * example as following:
      * select a + 10 as a1, d from (
      * select b - 5 as a, d from table
      * );
-     * after shuttle a1, d in select will be b - 5 + 10, d
+     * op expression before is: a + 10 as a1, d. after is: b - 5 + 10, d
+     * todo to get from plan struct info
      */
-    public static List<? extends Expression> 
shuttleExpressionWithLineage(List<? extends Expression> expression,
+    public static List<? extends Expression> 
shuttleExpressionWithLineage(List<? extends Expression> expressions,
             Plan plan,
             Set<TableType> targetTypes,
             Set<String> tableIdentifiers) {
-        return ImmutableList.of();
-    }
 
-    /**
-     * Replace the slot in expressions according to the slotMapping
-     * if any slot cannot be mapped then return null
-     */
-    public static List<? extends Expression> permute(List<? extends 
Expression> expressions, SlotMapping slotMapping) {
-        return ImmutableList.of();
+        ExpressionLineageReplacer.ExpressionReplaceContext replaceContext =
+                new ExpressionLineageReplacer.ExpressionReplaceContext(
+                        
expressions.stream().map(NamedExpression.class::cast).collect(Collectors.toList()),
+                        targetTypes,
+                        tableIdentifiers);
+
+        plan.accept(ExpressionLineageReplacer.INSTANCE, replaceContext);
+        // Replace expressions by expression map
+        return replaceContext.getReplacedExpressions();
     }
 
     /**
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
new file mode 100644
index 00000000000..39f6033b4a7
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/exploration/mv/MappingTest.java
@@ -0,0 +1,309 @@
+// 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.rules.exploration.mv;
+
+import org.apache.doris.nereids.rules.exploration.mv.mapping.RelationMapping;
+import org.apache.doris.nereids.rules.exploration.mv.mapping.SlotMapping;
+import org.apache.doris.nereids.trees.expressions.ExprId;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.RelationId;
+import org.apache.doris.nereids.trees.plans.algebra.CatalogRelation;
+import org.apache.doris.nereids.trees.plans.visitor.DefaultPlanVisitor;
+import org.apache.doris.nereids.util.PlanChecker;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**MappingTest*/
+public class MappingTest extends TestWithFeService {
+
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("mapping_test");
+        useDatabase("mapping_test");
+
+        createTable("CREATE TABLE IF NOT EXISTS lineitem (\n"
+                + "  L_ORDERKEY    INTEGER NOT NULL,\n"
+                + "  L_PARTKEY     INTEGER NOT NULL\n"
+                + ")\n"
+                + "DUPLICATE KEY(L_ORDERKEY, L_PARTKEY)\n"
+                + "DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+        createTable("CREATE TABLE IF NOT EXISTS orders  (\n"
+                + "  O_ORDERKEY       INTEGER NOT NULL,\n"
+                + "  O_CUSTKEY        INTEGER NOT NULL,\n"
+                + "  O_ORDERSTATUS    CHAR(1) NOT NULL\n"
+                + ")\n"
+                + "DUPLICATE KEY(O_ORDERKEY, O_CUSTKEY)\n"
+                + "DISTRIBUTED BY HASH(O_ORDERKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+        createTable("CREATE TABLE IF NOT EXISTS customer (\n"
+                + "  C_CUSTKEY     INTEGER NOT NULL,\n"
+                + "  C_NAME        VARCHAR(25) NOT NULL,\n"
+                + ")\n"
+                + "DUPLICATE KEY(C_CUSTKEY, C_NAME)\n"
+                + "DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+    }
+
+    // test the num of source and target table is same
+    @Test
+    public void testGenerateMapping1() {
+        Plan sourcePlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  orders,\n"
+                        + "  lineitem,\n"
+                        + "  customer\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey\n"
+                        + "  AND l_orderkey = o_orderkey")
+                .getPlan();
+
+        Plan targetPlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  customer,\n"
+                        + "  orders,\n"
+                        + "  lineitem\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey\n"
+                        + "  AND l_orderkey = o_orderkey")
+                .getPlan();
+        List<CatalogRelation> sourceRelations = new ArrayList<>();
+        sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+        List<CatalogRelation> targetRelations = new ArrayList<>();
+        targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+        List<RelationMapping> generateRelationMapping = 
RelationMapping.generate(sourceRelations, targetRelations);
+        Assertions.assertNotNull(generateRelationMapping);
+        Assertions.assertEquals(1, generateRelationMapping.size());
+
+        // expected slot mapping
+        BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+        expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+        expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+        expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+        expectedSlotMapping.put(new ExprId(3), new ExprId(5));
+        expectedSlotMapping.put(new ExprId(4), new ExprId(6));
+        expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+        expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+        // expected relation mapping
+        BiMap<RelationId, RelationId> expectedRelationMapping = 
HashBiMap.create();
+        expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+        expectedRelationMapping.put(new RelationId(1), new RelationId(2));
+        expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+        assertRelationMapping(generateRelationMapping.get(0), 
expectedRelationMapping, expectedSlotMapping);
+    }
+
+    // test the num of source table is less than target table
+    @Test
+    public void testGenerateMapping2() {
+        Plan sourcePlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  orders,\n"
+                        + "  customer\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey")
+                .getPlan();
+
+        Plan targetPlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  customer,\n"
+                        + "  orders,\n"
+                        + "  lineitem\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey\n"
+                        + "  AND l_orderkey = o_orderkey")
+                .getPlan();
+        List<CatalogRelation> sourceRelations = new ArrayList<>();
+        sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+        List<CatalogRelation> targetRelations = new ArrayList<>();
+        targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+        List<RelationMapping> generateRelationMapping = 
RelationMapping.generate(sourceRelations, targetRelations);
+        Assertions.assertNotNull(generateRelationMapping);
+        Assertions.assertEquals(1, generateRelationMapping.size());
+
+        // expected slot mapping
+        BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+        expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+        expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+        expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+        expectedSlotMapping.put(new ExprId(3), new ExprId(0));
+        expectedSlotMapping.put(new ExprId(4), new ExprId(1));
+        // expected relation mapping
+        BiMap<RelationId, RelationId> expectedRelationMapping = 
HashBiMap.create();
+        expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+        expectedRelationMapping.put(new RelationId(1), new RelationId(0));
+        assertRelationMapping(generateRelationMapping.get(0), 
expectedRelationMapping, expectedSlotMapping);
+    }
+
+    // test the num of source table is more than target table
+    @Test
+    public void testGenerateMapping3() {
+        Plan sourcePlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  orders,\n"
+                        + "  lineitem,\n"
+                        + "  customer\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey\n"
+                        + "  AND l_orderkey = o_orderkey")
+                .getPlan();
+
+        Plan targetPlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  customer,\n"
+                        + "  orders\n"
+                        + "WHERE\n"
+                        + "  c_custkey = o_custkey\n")
+                .getPlan();
+        List<CatalogRelation> sourceRelations = new ArrayList<>();
+        sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+        List<CatalogRelation> targetRelations = new ArrayList<>();
+        targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+        List<RelationMapping> generateRelationMapping = 
RelationMapping.generate(sourceRelations, targetRelations);
+        Assertions.assertNotNull(generateRelationMapping);
+        Assertions.assertEquals(1, generateRelationMapping.size());
+
+        // expected slot mapping
+        BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+        expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+        expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+        expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+        expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+        expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+        // expected relation mapping
+        BiMap<RelationId, RelationId> expectedRelationMapping = 
HashBiMap.create();
+        expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+        expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+        assertRelationMapping(generateRelationMapping.get(0), 
expectedRelationMapping, expectedSlotMapping);
+    }
+
+    // test table of source query is repeated
+    @Test
+    public void testGenerateMapping4() {
+        Plan sourcePlan = PlanChecker.from(connectContext)
+                .analyze("SELECT orders.*, l1.* "
+                        + "FROM\n"
+                        + "  orders,\n"
+                        + "  lineitem l1,\n"
+                        + "  lineitem l2\n"
+                        + "WHERE\n"
+                        + "  l1.l_orderkey = l2.l_orderkey\n"
+                        + "  AND l1.l_orderkey = o_orderkey")
+                .getPlan();
+
+        Plan targetPlan = PlanChecker.from(connectContext)
+                .analyze("SELECT * "
+                        + "FROM\n"
+                        + "  lineitem,\n"
+                        + "  orders\n"
+                        + "WHERE\n"
+                        + " l_orderkey = o_orderkey")
+                .getPlan();
+        List<CatalogRelation> sourceRelations = new ArrayList<>();
+        sourcePlan.accept(RelationCollector.INSTANCE, sourceRelations);
+
+        List<CatalogRelation> targetRelations = new ArrayList<>();
+        targetPlan.accept(RelationCollector.INSTANCE, targetRelations);
+
+        List<RelationMapping> generateRelationMapping = 
RelationMapping.generate(sourceRelations, targetRelations);
+        Assertions.assertNotNull(generateRelationMapping);
+        Assertions.assertEquals(2, generateRelationMapping.size());
+
+        // expected slot mapping
+        BiMap<ExprId, ExprId> expectedSlotMapping = HashBiMap.create();
+        expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+        expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+        expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+        expectedSlotMapping.put(new ExprId(3), new ExprId(0));
+        expectedSlotMapping.put(new ExprId(4), new ExprId(1));
+        // expected relation mapping
+        BiMap<RelationId, RelationId> expectedRelationMapping = 
HashBiMap.create();
+        expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+        expectedRelationMapping.put(new RelationId(1), new RelationId(0));
+        assertRelationMapping(generateRelationMapping.get(0), 
expectedRelationMapping, expectedSlotMapping);
+
+        // expected slot mapping
+        expectedSlotMapping = HashBiMap.create();
+        expectedSlotMapping.put(new ExprId(0), new ExprId(2));
+        expectedSlotMapping.put(new ExprId(1), new ExprId(3));
+        expectedSlotMapping.put(new ExprId(2), new ExprId(4));
+        expectedSlotMapping.put(new ExprId(5), new ExprId(0));
+        expectedSlotMapping.put(new ExprId(6), new ExprId(1));
+        // expected relation mapping
+        expectedRelationMapping = HashBiMap.create();
+        expectedRelationMapping.put(new RelationId(0), new RelationId(1));
+        expectedRelationMapping.put(new RelationId(2), new RelationId(0));
+        assertRelationMapping(generateRelationMapping.get(1), 
expectedRelationMapping, expectedSlotMapping);
+    }
+
+    private void assertRelationMapping(RelationMapping relationMapping,
+            BiMap<RelationId, RelationId> expectRelationMapping,
+            BiMap<ExprId, ExprId> expectSlotMapping) {
+        // check relation mapping
+        BiMap<RelationId, RelationId> generatedRelationMapping = 
HashBiMap.create();
+        relationMapping.getMappedRelationMap().forEach((key, value) ->
+                generatedRelationMapping.put(key.getRelationId(), 
value.getRelationId()));
+        Assertions.assertEquals(generatedRelationMapping, 
expectRelationMapping);
+
+        // Generate slot mapping from relationMapping and check
+        SlotMapping slotMapping = SlotMapping.generate(relationMapping);
+        Assertions.assertNotNull(slotMapping);
+        BiMap<ExprId, ExprId> generatedSlotMapping = HashBiMap.create();
+        slotMapping.getSlotBiMap().forEach((key, value) ->
+                generatedSlotMapping.put(key.getExprId(), value.getExprId())
+        );
+        Assertions.assertEquals(generatedSlotMapping, expectSlotMapping);
+    }
+
+    protected static class RelationCollector extends DefaultPlanVisitor<Void, 
List<CatalogRelation>> {
+
+        public static final RelationCollector INSTANCE = new 
RelationCollector();
+
+        @Override
+        public Void visit(Plan plan, List<CatalogRelation> catalogRelations) {
+            if (plan instanceof CatalogRelation) {
+                catalogRelations.add((CatalogRelation) plan);
+            }
+            return super.visit(plan, catalogRelations);
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
index e8cc4dd266a..ffd3c29a18d 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/ExpressionRewriteTestHelper.java
@@ -108,7 +108,7 @@ public abstract class ExpressionRewriteTestHelper {
         return FunctionBinder.INSTANCE.rewrite(expression, null);
     }
 
-    private DataType getType(char t) {
+    protected DataType getType(char t) {
         switch (t) {
             case 'T':
                 return TinyIntType.INSTANCE;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
new file mode 100644
index 00000000000..4a8f2e981ef
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/expression/PredicatesSplitterTest.java
@@ -0,0 +1,109 @@
+// 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.rules.expression;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.nereids.analyzer.UnboundSlot;
+import org.apache.doris.nereids.rules.exploration.mv.Predicates;
+import org.apache.doris.nereids.rules.exploration.mv.Predicates.SplitPredicate;
+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 com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * PredicatesSplitterTest
+ */
+public class PredicatesSplitterTest extends ExpressionRewriteTestHelper {
+
+    @Test
+    public void testSplitPredicates() {
+        assetEquals("a = b and (c = d or a = 10) and a > 7 and 10 > d",
+                "a = b",
+                "a > 7 and 10 > d",
+                "c = d or a = 10");
+        assetEquals("a = b and c + d = e and a > 7 and 10 > d",
+                "a = b",
+                "a > 7 and 10 > d",
+                "c + d = e");
+        assetEquals("a = b and c + d = e or a > 7 and 10 > d",
+                "",
+                "",
+                "a = b and c + d = e or a > 7 and 10 > d");
+    }
+
+    private void assetEquals(String expression,
+            String expectedEqualExpr,
+            String expectedRangeExpr,
+            String expectedResidualExpr) {
+
+        Map<String, Slot> mem = Maps.newHashMap();
+        Expression targetExpr = 
replaceUnboundSlot(PARSER.parseExpression(expression), mem);
+        SplitPredicate splitPredicate = Predicates.splitPredicates(targetExpr);
+
+        if (!StringUtils.isEmpty(expectedEqualExpr)) {
+            Expression equalExpression = 
replaceUnboundSlot(PARSER.parseExpression(expectedEqualExpr), mem);
+            Assertions.assertEquals(equalExpression, 
splitPredicate.getEqualPredicate());
+        } else {
+            Assertions.assertNull(splitPredicate.getEqualPredicate());
+        }
+
+        if (!StringUtils.isEmpty(expectedRangeExpr)) {
+            Expression rangeExpression = 
replaceUnboundSlot(PARSER.parseExpression(expectedRangeExpr), mem);
+            Assertions.assertEquals(rangeExpression, 
splitPredicate.getRangePredicate());
+        } else {
+            Assertions.assertNull(splitPredicate.getRangePredicate());
+        }
+
+        if (!StringUtils.isEmpty(expectedResidualExpr)) {
+            Expression residualExpression = 
replaceUnboundSlot(PARSER.parseExpression(expectedResidualExpr), mem);
+            Assertions.assertEquals(residualExpression, 
splitPredicate.getResidualPredicate());
+        } else {
+            Assertions.assertNull(splitPredicate.getResidualPredicate());
+        }
+    }
+
+    @Override
+    public Expression replaceUnboundSlot(Expression expression, Map<String, 
Slot> mem) {
+        List<Expression> children = Lists.newArrayList();
+        boolean hasNewChildren = false;
+        for (Expression child : expression.children()) {
+            Expression newChild = replaceUnboundSlot(child, mem);
+            if (newChild != child) {
+                hasNewChildren = true;
+            }
+            children.add(newChild);
+        }
+        if (expression instanceof UnboundSlot) {
+            String name = ((UnboundSlot) expression).getName();
+            mem.putIfAbsent(name, SlotReference.fromColumn(
+                    new Column(name, 
getType(name.charAt(0)).toCatalogDataType()),
+                    Lists.newArrayList("table")));
+            return mem.get(name);
+        }
+        return hasNewChildren ? expression.withChildren(children) : expression;
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
index 55f78faa20b..7ab3e8083d1 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/util/ExpressionUtilsTest.java
@@ -19,7 +19,10 @@ package org.apache.doris.nereids.util;
 
 import org.apache.doris.nereids.parser.NereidsParser;
 import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.utframe.TestWithFeService;
 
+import com.google.common.collect.Sets;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 
@@ -28,10 +31,70 @@ import java.util.List;
 /**
  * ExpressionUtils ut.
  */
-public class ExpressionUtilsTest {
+public class ExpressionUtilsTest extends TestWithFeService {
 
     private static final NereidsParser PARSER = new NereidsParser();
 
+    @Override
+    protected void runBeforeAll() throws Exception {
+        createDatabase("expression_test");
+        useDatabase("expression_test");
+
+        createTable("CREATE TABLE IF NOT EXISTS lineitem (\n"
+                + "  L_ORDERKEY    INTEGER NOT NULL,\n"
+                + "  L_PARTKEY     INTEGER NOT NULL,\n"
+                + "  L_SUPPKEY     INTEGER NOT NULL,\n"
+                + "  L_LINENUMBER  INTEGER NOT NULL,\n"
+                + "  L_QUANTITY    DECIMALV3(15,2) NOT NULL,\n"
+                + "  L_EXTENDEDPRICE  DECIMALV3(15,2) NOT NULL,\n"
+                + "  L_DISCOUNT    DECIMALV3(15,2) NOT NULL,\n"
+                + "  L_TAX         DECIMALV3(15,2) NOT NULL,\n"
+                + "  L_RETURNFLAG  CHAR(1) NOT NULL,\n"
+                + "  L_LINESTATUS  CHAR(1) NOT NULL,\n"
+                + "  L_SHIPDATE    DATE NOT NULL,\n"
+                + "  L_COMMITDATE  DATE NOT NULL,\n"
+                + "  L_RECEIPTDATE DATE NOT NULL,\n"
+                + "  L_SHIPINSTRUCT CHAR(25) NOT NULL,\n"
+                + "  L_SHIPMODE     CHAR(10) NOT NULL,\n"
+                + "  L_COMMENT      VARCHAR(44) NOT NULL\n"
+                + ")\n"
+                + "DUPLICATE KEY(L_ORDERKEY, L_PARTKEY, L_SUPPKEY, 
L_LINENUMBER)\n"
+                + "DISTRIBUTED BY HASH(L_ORDERKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+        createTable("CREATE TABLE IF NOT EXISTS orders  (\n"
+                + "  O_ORDERKEY       INTEGER NOT NULL,\n"
+                + "  O_CUSTKEY        INTEGER NOT NULL,\n"
+                + "  O_ORDERSTATUS    CHAR(1) NOT NULL,\n"
+                + "  O_TOTALPRICE     DECIMALV3(15,2) NOT NULL,\n"
+                + "  O_ORDERDATE      DATE NOT NULL,\n"
+                + "  O_ORDERPRIORITY  CHAR(15) NOT NULL,  \n"
+                + "  O_CLERK          CHAR(15) NOT NULL, \n"
+                + "  O_SHIPPRIORITY   INTEGER NOT NULL,\n"
+                + "  O_COMMENT        VARCHAR(79) NOT NULL\n"
+                + ")\n"
+                + "DUPLICATE KEY(O_ORDERKEY, O_CUSTKEY)\n"
+                + "DISTRIBUTED BY HASH(O_ORDERKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+        createTable("CREATE TABLE IF NOT EXISTS partsupp (\n"
+                + "  PS_PARTKEY     INTEGER NOT NULL,\n"
+                + "  PS_SUPPKEY     INTEGER NOT NULL,\n"
+                + "  PS_AVAILQTY    INTEGER NOT NULL,\n"
+                + "  PS_SUPPLYCOST  DECIMALV3(15,2)  NOT NULL,\n"
+                + "  PS_COMMENT     VARCHAR(199) NOT NULL \n"
+                + ")\n"
+                + "DUPLICATE KEY(PS_PARTKEY, PS_SUPPKEY)\n"
+                + "DISTRIBUTED BY HASH(PS_PARTKEY) BUCKETS 3\n"
+                + "PROPERTIES (\n"
+                + "  \"replication_num\" = \"1\"\n"
+                + ")");
+        connectContext.getSessionVariable().enableNereidsTimeout = false;
+
+    }
+
     @Test
     public void extractConjunctionTest() {
         List<Expression> expressions;
@@ -93,4 +156,45 @@ public class ExpressionUtilsTest {
         Assertions.assertEquals(c, expressions.get(1));
         Assertions.assertEquals(eAndf, expressions.get(2));
     }
+
+    @Test
+    public void testShuttleExpressionWithLineage1() {
+        PlanChecker.from(connectContext)
+                .checkExplain("SELECT (o.c1_abs + ps.c2_abs) as add_alias, 
l.L_LINENUMBER, o.O_ORDERSTATUS "
+                                + "FROM "
+                                + "lineitem as l "
+                                + "LEFT JOIN "
+                                + "(SELECT abs(O_TOTALPRICE + 10) as c1_abs, 
O_CUSTKEY, O_ORDERSTATUS, O_ORDERKEY "
+                                + "FROM orders) as o "
+                                + "ON l.L_ORDERKEY = o.O_ORDERKEY "
+                                + "JOIN "
+                                + "(SELECT abs(sqrt(PS_SUPPLYCOST)) as c2_abs, 
PS_AVAILQTY, PS_PARTKEY, PS_SUPPKEY "
+                                + "FROM partsupp) as ps "
+                                + "ON l.L_PARTKEY = ps.PS_PARTKEY and 
l.L_SUPPKEY = ps.PS_SUPPKEY",
+                        nereidsPlanner -> {
+                            Plan rewrittenPlan = 
nereidsPlanner.getRewrittenPlan();
+                            List<? extends Expression> originalExpressions = 
rewrittenPlan.getExpressions();
+                            List<? extends Expression> shuttledExpressions
+                                    = 
ExpressionUtils.shuttleExpressionWithLineage(
+                                    originalExpressions,
+                                    rewrittenPlan,
+                                    Sets.newHashSet(),
+                                    Sets.newHashSet());
+                            assertExpect(originalExpressions, 
shuttledExpressions,
+                                    "(cast(abs((cast(O_TOTALPRICE as 
DECIMALV3(16, 2)) + 10.00)) as "
+                                            + "DOUBLE) + 
abs(sqrt(cast(PS_SUPPLYCOST as DOUBLE))))",
+                                    "L_LINENUMBER",
+                                    "O_ORDERSTATUS");
+                        });
+    }
+
+    private void assertExpect(List<? extends Expression> originalExpressions,
+            List<? extends Expression> shuttledExpressions,
+            String... expectExpressions) {
+        Assertions.assertEquals(originalExpressions.size(), 
shuttledExpressions.size());
+        Assertions.assertEquals(originalExpressions.size(), 
expectExpressions.length);
+        for (int index = 0; index < shuttledExpressions.size(); index++) {
+            Assertions.assertEquals(shuttledExpressions.get(index).toSql(), 
expectExpressions[index]);
+        }
+    }
 }


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

Reply via email to