diff --git a/src/optimizer/OptGroup.cpp b/src/optimizer/OptGroup.cpp
index c4da0182cbdc6f404c33292953763b65a62620e0..d8c4f6e7503781f6955babdae68b97b9cfc5a806 100644
--- a/src/optimizer/OptGroup.cpp
+++ b/src/optimizer/OptGroup.cpp
@@ -31,15 +31,15 @@ OptGroup::OptGroup(QueryContext *qctx) noexcept : qctx_(qctx) {
     DCHECK(qctx != nullptr);
 }
 
-void OptGroup::addGroupExpr(OptGroupExpr *groupExpr) {
-    DCHECK(groupExpr != nullptr);
-    DCHECK(groupExpr->group() == this);
-    groupExprs_.emplace_back(groupExpr);
+void OptGroup::addGroupNode(OptGroupNode *groupNode) {
+    DCHECK(groupNode != nullptr);
+    DCHECK(groupNode->group() == this);
+    groupNodes_.emplace_back(groupNode);
 }
 
-OptGroupExpr *OptGroup::makeGroupExpr(QueryContext *qctx, PlanNode *node) {
-    groupExprs_.emplace_back(OptGroupExpr::create(qctx, node, this));
-    return groupExprs_.back();
+OptGroupNode *OptGroup::makeGroupNode(QueryContext *qctx, PlanNode *node) {
+    groupNodes_.emplace_back(OptGroupNode::create(qctx, node, this));
+    return groupNodes_.back();
 }
 
 Status OptGroup::explore(const OptRule *rule) {
@@ -48,18 +48,18 @@ Status OptGroup::explore(const OptRule *rule) {
     }
     setExplored(rule);
 
-    for (auto iter = groupExprs_.begin(); iter != groupExprs_.end();) {
-        auto groupExpr = *iter;
-        DCHECK(groupExpr != nullptr);
-        if (groupExpr->isExplored(rule)) {
+    for (auto iter = groupNodes_.begin(); iter != groupNodes_.end();) {
+        auto groupNode = *iter;
+        DCHECK(groupNode != nullptr);
+        if (groupNode->isExplored(rule)) {
             ++iter;
             continue;
         }
         // Bottom to up exploration
-        NG_RETURN_IF_ERROR(groupExpr->explore(rule));
+        NG_RETURN_IF_ERROR(groupNode->explore(rule));
 
         // Find more equivalents
-        auto status = rule->match(groupExpr);
+        auto status = rule->match(groupNode);
         if (!status.ok()) {
             ++iter;
             continue;
@@ -69,23 +69,23 @@ Status OptGroup::explore(const OptRule *rule) {
         NG_RETURN_IF_ERROR(resStatus);
         auto result = std::move(resStatus).value();
         if (result.eraseAll) {
-            groupExprs_.clear();
-            for (auto nge : result.newGroupExprs) {
-                addGroupExpr(nge);
+            groupNodes_.clear();
+            for (auto ngn : result.newGroupNodes) {
+                addGroupNode(ngn);
             }
             break;
         }
 
-        if (!result.newGroupExprs.empty()) {
-            for (auto nge : result.newGroupExprs) {
-                addGroupExpr(nge);
+        if (!result.newGroupNodes.empty()) {
+            for (auto ngn : result.newGroupNodes) {
+                addGroupNode(ngn);
             }
 
             setUnexplored(rule);
         }
 
         if (result.eraseCurr) {
-            iter = groupExprs_.erase(iter);
+            iter = groupNodes_.erase(iter);
         } else {
             ++iter;
         }
@@ -107,40 +107,40 @@ Status OptGroup::exploreUntilMaxRound(const OptRule *rule) {
     return Status::OK();
 }
 
-std::pair<double, const OptGroupExpr *> OptGroup::findMinCostGroupExpr() const {
+std::pair<double, const OptGroupNode *> OptGroup::findMinCostGroupNode() const {
     double minCost = std::numeric_limits<double>::max();
-    const OptGroupExpr *minGroupExpr = nullptr;
-    for (auto &groupExpr : groupExprs_) {
-        double cost = groupExpr->getCost();
+    const OptGroupNode *minGroupNode = nullptr;
+    for (auto &groupNode : groupNodes_) {
+        double cost = groupNode->getCost();
         if (minCost > cost) {
             minCost = cost;
-            minGroupExpr = groupExpr;
+            minGroupNode = groupNode;
         }
     }
-    return std::make_pair(minCost, minGroupExpr);
+    return std::make_pair(minCost, minGroupNode);
 }
 
 double OptGroup::getCost() const {
-    return findMinCostGroupExpr().first;
+    return findMinCostGroupNode().first;
 }
 
 const PlanNode *OptGroup::getPlan() const {
-    const OptGroupExpr *minGroupExpr = findMinCostGroupExpr().second;
-    DCHECK(minGroupExpr != nullptr);
-    return minGroupExpr->getPlan();
+    const OptGroupNode *minGroupNode = findMinCostGroupNode().second;
+    DCHECK(minGroupNode != nullptr);
+    return minGroupNode->getPlan();
 }
 
-OptGroupExpr *OptGroupExpr::create(QueryContext *qctx, PlanNode *node, const OptGroup *group) {
-    return qctx->objPool()->add(new OptGroupExpr(node, group));
+OptGroupNode *OptGroupNode::create(QueryContext *qctx, PlanNode *node, const OptGroup *group) {
+    return qctx->objPool()->add(new OptGroupNode(node, group));
 }
 
-OptGroupExpr::OptGroupExpr(PlanNode *node, const OptGroup *group) noexcept
+OptGroupNode::OptGroupNode(PlanNode *node, const OptGroup *group) noexcept
     : node_(node), group_(group) {
     DCHECK(node != nullptr);
     DCHECK(group != nullptr);
 }
 
-Status OptGroupExpr::explore(const OptRule *rule) {
+Status OptGroupNode::explore(const OptRule *rule) {
     if (isExplored(rule)) {
         return Status::OK();
     }
@@ -158,11 +158,11 @@ Status OptGroupExpr::explore(const OptRule *rule) {
     return Status::OK();
 }
 
-double OptGroupExpr::getCost() const {
+double OptGroupNode::getCost() const {
     return node_->cost();
 }
 
-const PlanNode *OptGroupExpr::getPlan() const {
+const PlanNode *OptGroupNode::getPlan() const {
     switch (node_->dependencies().size()) {
         case 0: {
             DCHECK(dependencies_.empty());
diff --git a/src/optimizer/OptGroup.h b/src/optimizer/OptGroup.h
index e1c4c4e359d7911c62ccdf20581eea7ba760045b..e7a15089a0f9d30b63e40f3d6f3d49de7e24d0a1 100644
--- a/src/optimizer/OptGroup.h
+++ b/src/optimizer/OptGroup.h
@@ -20,7 +20,7 @@ class QueryContext;
 
 namespace opt {
 
-class OptGroupExpr;
+class OptGroupNode;
 class OptRule;
 
 class OptGroup final {
@@ -43,10 +43,10 @@ public:
         }
     }
 
-    void addGroupExpr(OptGroupExpr *groupExpr);
-    OptGroupExpr *makeGroupExpr(graph::QueryContext *qctx, graph::PlanNode *node);
-    const std::list<OptGroupExpr *> &groupExprs() const {
-        return groupExprs_;
+    void addGroupNode(OptGroupNode *groupNode);
+    OptGroupNode *makeGroupNode(graph::QueryContext *qctx, graph::PlanNode *node);
+    const std::list<OptGroupNode *> &groupNodes() const {
+        return groupNodes_;
     }
 
     Status explore(const OptRule *rule);
@@ -59,16 +59,16 @@ private:
 
     static constexpr int16_t kMaxExplorationRound = 128;
 
-    std::pair<double, const OptGroupExpr *> findMinCostGroupExpr() const;
+    std::pair<double, const OptGroupNode *> findMinCostGroupNode() const;
 
     graph::QueryContext *qctx_{nullptr};
-    std::list<OptGroupExpr *> groupExprs_;
+    std::list<OptGroupNode *> groupNodes_;
     std::vector<const OptRule *> exploredRules_;
 };
 
-class OptGroupExpr final {
+class OptGroupNode final {
 public:
-    static OptGroupExpr *create(graph::QueryContext *qctx,
+    static OptGroupNode *create(graph::QueryContext *qctx,
                                 graph::PlanNode *node,
                                 const OptGroup *group);
 
@@ -110,7 +110,7 @@ public:
     const graph::PlanNode *getPlan() const;
 
 private:
-    OptGroupExpr(graph::PlanNode *node, const OptGroup *group) noexcept;
+    OptGroupNode(graph::PlanNode *node, const OptGroup *group) noexcept;
 
     graph::PlanNode *node_{nullptr};
     const OptGroup *group_{nullptr};
diff --git a/src/optimizer/OptRule.cpp b/src/optimizer/OptRule.cpp
index e6253fcfc2b8e538907328bc7760736e1c50b822..fed3f0e67387c399cdd5024179cff5b3d8aef4da 100644
--- a/src/optimizer/OptRule.cpp
+++ b/src/optimizer/OptRule.cpp
@@ -21,24 +21,24 @@ Pattern Pattern::create(graph::PlanNode::Kind kind, std::initializer_list<Patter
     return pattern;
 }
 
-StatusOr<MatchedResult> Pattern::match(const OptGroupExpr *groupExpr) const {
-    if (groupExpr->node()->kind() != kind_) {
+StatusOr<MatchedResult> Pattern::match(const OptGroupNode *groupNode) const {
+    if (groupNode->node()->kind() != kind_) {
         return Status::Error();
     }
 
     if (dependencies_.empty()) {
-        return MatchedResult{groupExpr, {}};
+        return MatchedResult{groupNode, {}};
     }
 
-    if (groupExpr->dependencies().size() != dependencies_.size()) {
+    if (groupNode->dependencies().size() != dependencies_.size()) {
         return Status::Error();
     }
 
     MatchedResult result;
-    result.node = groupExpr;
+    result.node = groupNode;
     result.dependencies.reserve(dependencies_.size());
     for (size_t i = 0; i < dependencies_.size(); ++i) {
-        auto group = groupExpr->dependencies()[i];
+        auto group = groupNode->dependencies()[i];
         const auto &pattern = dependencies_[i];
         auto status = pattern.match(group);
         NG_RETURN_IF_ERROR(status);
@@ -48,7 +48,7 @@ StatusOr<MatchedResult> Pattern::match(const OptGroupExpr *groupExpr) const {
 }
 
 StatusOr<MatchedResult> Pattern::match(const OptGroup *group) const {
-    for (auto node : group->groupExprs()) {
+    for (auto node : group->groupNodes()) {
         auto status = match(node);
         if (status.ok()) {
             return status;
@@ -57,9 +57,9 @@ StatusOr<MatchedResult> Pattern::match(const OptGroup *group) const {
     return Status::Error();
 }
 
-StatusOr<MatchedResult> OptRule::match(const OptGroupExpr *groupExpr) const {
+StatusOr<MatchedResult> OptRule::match(const OptGroupNode *groupNode) const {
     const auto &pattern = this->pattern();
-    auto status = pattern.match(groupExpr);
+    auto status = pattern.match(groupNode);
     NG_RETURN_IF_ERROR(status);
     auto matched = std::move(status).value();
     if (!this->match(matched)) {
diff --git a/src/optimizer/OptRule.h b/src/optimizer/OptRule.h
index fe616376dfa9f42162c4107631622b5ed8ce5faa..9041afead0b72f1a53e5f52affc71137d6768ae4 100644
--- a/src/optimizer/OptRule.h
+++ b/src/optimizer/OptRule.h
@@ -22,11 +22,11 @@ class QueryContext;
 
 namespace opt {
 
-class OptGroupExpr;
+class OptGroupNode;
 class OptGroup;
 
 struct MatchedResult {
-    const OptGroupExpr *node{nullptr};
+    const OptGroupNode *node{nullptr};
     std::vector<MatchedResult> dependencies;
 };
 
@@ -34,7 +34,7 @@ class Pattern final {
 public:
     static Pattern create(graph::PlanNode::Kind kind, std::initializer_list<Pattern> patterns = {});
 
-    StatusOr<MatchedResult> match(const OptGroupExpr *groupNode) const;
+    StatusOr<MatchedResult> match(const OptGroupNode *groupNode) const;
 
 private:
     Pattern() = default;
@@ -51,12 +51,13 @@ public:
             static TransformResult kNoTrans{false, false, {}};
             return kNoTrans;
         }
+
         bool eraseCurr{false};
         bool eraseAll{false};
-        std::vector<OptGroupExpr *> newGroupExprs;
+        std::vector<OptGroupNode *> newGroupNodes;
     };
 
-    StatusOr<MatchedResult> match(const OptGroupExpr *groupExpr) const;
+    StatusOr<MatchedResult> match(const OptGroupNode *groupNode) const;
 
     virtual ~OptRule() = default;
 
diff --git a/src/optimizer/Optimizer.cpp b/src/optimizer/Optimizer.cpp
index 34e40b8685f13a6b6e8e650ba811aea841c45c72..0c35446e72009c6adc0fc3e1706bcadcf55011d0 100644
--- a/src/optimizer/Optimizer.cpp
+++ b/src/optimizer/Optimizer.cpp
@@ -60,7 +60,7 @@ OptGroup *Optimizer::convertToGroup(QueryContext *qctx,
     }
 
     auto group = OptGroup::create(qctx);
-    auto groupExpr = group->makeGroupExpr(qctx, node);
+    auto groupNode = group->makeGroupNode(qctx, node);
 
     switch (node->dependencies().size()) {
         case 0: {
@@ -71,29 +71,29 @@ OptGroup *Optimizer::convertToGroup(QueryContext *qctx,
             if (node->kind() == PlanNode::Kind::kSelect) {
                 auto select = static_cast<Select *>(node);
                 auto then = convertToGroup(qctx, const_cast<PlanNode *>(select->then()), visited);
-                groupExpr->addBody(then);
+                groupNode->addBody(then);
                 auto otherNode = const_cast<PlanNode *>(select->otherwise());
                 auto otherwise = convertToGroup(qctx, otherNode, visited);
-                groupExpr->addBody(otherwise);
+                groupNode->addBody(otherwise);
             } else if (node->kind() == PlanNode::Kind::kLoop) {
                 auto loop = static_cast<Loop *>(node);
                 auto body = convertToGroup(qctx, const_cast<PlanNode *>(loop->body()), visited);
-                groupExpr->addBody(body);
+                groupNode->addBody(body);
             }
             auto dep = static_cast<SingleDependencyNode *>(node)->dep();
             DCHECK(dep != nullptr);
             auto depGroup = convertToGroup(qctx, const_cast<graph::PlanNode *>(dep), visited);
-            groupExpr->dependsOn(depGroup);
+            groupNode->dependsOn(depGroup);
             break;
         }
         case 2: {
             auto bNode = static_cast<BiInputNode *>(node);
             auto leftNode = const_cast<graph::PlanNode *>(bNode->left());
             auto leftGroup = convertToGroup(qctx, leftNode, visited);
-            groupExpr->dependsOn(leftGroup);
+            groupNode->dependsOn(leftGroup);
             auto rightNode = const_cast<graph::PlanNode *>(bNode->right());
             auto rightGroup = convertToGroup(qctx, rightNode, visited);
-            groupExpr->dependsOn(rightGroup);
+            groupNode->dependsOn(rightGroup);
             break;
         }
         default: {
diff --git a/src/optimizer/Optimizer.h b/src/optimizer/Optimizer.h
index c250106305c3a907cc53421828ff97dd7b8a4cde..176a7d86bedf80ba0d8bc75ed411c7f2e01a323f 100644
--- a/src/optimizer/Optimizer.h
+++ b/src/optimizer/Optimizer.h
@@ -20,7 +20,7 @@ class QueryContext;
 namespace opt {
 
 class OptGroup;
-class OptGroupExpr;
+class OptGroupNode;
 class RuleSet;
 
 class Optimizer final {
diff --git a/src/optimizer/rule/IndexScanRule.cpp b/src/optimizer/rule/IndexScanRule.cpp
index 136a883e21f70cb5c8db0c94a0e7d5fa7467c6d2..cf250255dcc6d48e38489c8d8534b96afa54dc10 100644
--- a/src/optimizer/rule/IndexScanRule.cpp
+++ b/src/optimizer/rule/IndexScanRule.cpp
@@ -14,6 +14,7 @@ using nebula::graph::IndexScan;
 
 namespace nebula {
 namespace opt {
+
 std::unique_ptr<OptRule> IndexScanRule::kInstance =
     std::unique_ptr<IndexScanRule>(new IndexScanRule());
 
@@ -28,29 +29,27 @@ const Pattern& IndexScanRule::pattern() const {
 
 StatusOr<OptRule::TransformResult> IndexScanRule::transform(graph::QueryContext* qctx,
                                                             const MatchedResult& matched) const {
-    auto groupExpr = matched.node;
-    auto filter = filterExpr(groupExpr);
+    auto groupNode = matched.node;
+    auto filter = filterExpr(groupNode);
     if (filter == nullptr) {
         return Status::SemanticError("WHERE clause error");
     }
     FilterItems items;
     ScanKind kind;
-    auto ret = analyzeExpression(filter.get(), &items, &kind, isEdge(groupExpr));
-    NG_RETURN_IF_ERROR(ret);
+    NG_RETURN_IF_ERROR(analyzeExpression(filter.get(), &items, &kind, isEdge(groupNode)));
 
     IndexQueryCtx iqctx = std::make_unique<std::vector<IndexQueryContext>>();
-    ret = createIndexQueryCtx(iqctx, kind, items, qctx, groupExpr);
-    NG_RETURN_IF_ERROR(ret);
+    NG_RETURN_IF_ERROR(createIndexQueryCtx(iqctx, kind, items, qctx, groupNode));
 
-    auto newIN = static_cast<const IndexScan*>(groupExpr->node())->clone(qctx);
+    auto newIN = static_cast<const IndexScan*>(groupNode->node())->clone(qctx);
     newIN->setIndexQueryContext(std::move(iqctx));
-    auto newGroupExpr = OptGroupExpr::create(qctx, newIN, groupExpr->group());
-    if (groupExpr->dependencies().size() != 1) {
+    auto newGroupNode = OptGroupNode::create(qctx, newIN, groupNode->group());
+    if (groupNode->dependencies().size() != 1) {
         return Status::Error("Plan node dependencies error");
     }
-    newGroupExpr->dependsOn(groupExpr->dependencies()[0]);
+    newGroupNode->dependsOn(groupNode->dependencies()[0]);
     TransformResult result;
-    result.newGroupExprs.emplace_back(newGroupExpr);
+    result.newGroupNodes.emplace_back(newGroupNode);
     result.eraseAll = true;
     return result;
 }
@@ -63,17 +62,17 @@ Status IndexScanRule::createIndexQueryCtx(IndexQueryCtx &iqctx,
                                           ScanKind kind,
                                           const FilterItems& items,
                                           graph::QueryContext *qctx,
-                                          const OptGroupExpr *groupExpr) const {
+                                          const OptGroupNode *groupNode) const {
     return kind.isLogicalAnd()
-           ? createIQCWithLogicAnd(iqctx, items, qctx, groupExpr)
-           : createIQCWithLogicOR(iqctx, items, qctx, groupExpr);
+           ? createIQCWithLogicAnd(iqctx, items, qctx, groupNode)
+           : createIQCWithLogicOR(iqctx, items, qctx, groupNode);
 }
 
 Status IndexScanRule::createIQCWithLogicAnd(IndexQueryCtx &iqctx,
                                             const FilterItems& items,
                                             graph::QueryContext *qctx,
-                                            const OptGroupExpr *groupExpr) const {
-    auto index = findOptimalIndex(qctx, groupExpr, items);
+                                            const OptGroupNode *groupNode) const {
+    auto index = findOptimalIndex(qctx, groupNode, items);
     if (index == nullptr) {
         return Status::IndexNotFound("No valid index found");
     }
@@ -84,9 +83,9 @@ Status IndexScanRule::createIQCWithLogicAnd(IndexQueryCtx &iqctx,
 Status IndexScanRule::createIQCWithLogicOR(IndexQueryCtx &iqctx,
                                            const FilterItems& items,
                                            graph::QueryContext *qctx,
-                                           const OptGroupExpr *groupExpr) const {
+                                           const OptGroupNode *groupNode) const {
     for (auto const& item : items.items) {
-        auto index = findOptimalIndex(qctx, groupExpr, FilterItems({item}));
+        auto index = findOptimalIndex(qctx, groupNode, FilterItems({item}));
         if (index == nullptr) {
             return Status::IndexNotFound("No valid index found");
         }
@@ -121,8 +120,7 @@ Status IndexScanRule::appendIQCtx(const IndexItem& index,
         if (it != filterItems.items.end()) {
             break;
         }
-        auto ret = appendColHint(hints, filterItems, field);
-        NG_RETURN_IF_ERROR(ret);
+        NG_RETURN_IF_ERROR(appendColHint(hints, filterItems, field));
     }
     ctx.set_index_id(index->get_index_id());
     // TODO (sky) : rewrite expr and set filter
@@ -156,8 +154,7 @@ Status IndexScanRule::appendColHint(std::vector<IndexColumnHint>& hints,
             begin = item.value_;
             break;
         }
-        auto ret = boundValue(item, col, begin, end);
-        NG_RETURN_IF_ERROR(ret);
+        NG_RETURN_IF_ERROR(boundValue(item, col, begin, end));
     }
 
     if (isRangeScan) {
@@ -238,24 +235,24 @@ Status IndexScanRule::boundValue(const FilterItem& item,
     return Status::OK();
 }
 
-bool IndexScanRule::isEdge(const OptGroupExpr *groupExpr) const {
-    auto in = static_cast<const IndexScan *>(groupExpr->node());
+bool IndexScanRule::isEdge(const OptGroupNode *groupNode) const {
+    auto in = static_cast<const IndexScan *>(groupNode->node());
     return in->isEdge();
 }
 
-int32_t IndexScanRule::schemaId(const OptGroupExpr *groupExpr) const {
-    auto in = static_cast<const IndexScan *>(groupExpr->node());
+int32_t IndexScanRule::schemaId(const OptGroupNode *groupNode) const {
+    auto in = static_cast<const IndexScan *>(groupNode->node());
     return in->schemaId();
 }
 
-GraphSpaceID IndexScanRule::spaceId(const OptGroupExpr *groupExpr) const {
-    auto in = static_cast<const IndexScan *>(groupExpr->node());
+GraphSpaceID IndexScanRule::spaceId(const OptGroupNode *groupNode) const {
+    auto in = static_cast<const IndexScan *>(groupNode->node());
     return in->space();
 }
 
 std::unique_ptr<Expression>
-IndexScanRule::filterExpr(const OptGroupExpr *groupExpr) const {
-    auto in = static_cast<const IndexScan *>(groupExpr->node());
+IndexScanRule::filterExpr(const OptGroupNode *groupNode) const {
+    auto in = static_cast<const IndexScan *>(groupNode->node());
     auto qct = in->queryContext();
     // The initial IndexScan plan node has only one queryContext.
     // TODO(yee): Move this condition to match interface
@@ -292,10 +289,8 @@ Status IndexScanRule::analyzeExpression(Expression* expr,
                                                    Expression::encode(*expr).c_str());
                 return Status::NotSupported(errorMsg);
             }
-            auto ret = analyzeExpression(lExpr->left(), items, kind, isEdge);
-            NG_RETURN_IF_ERROR(ret);
-            ret = analyzeExpression(lExpr->right(), items, kind, isEdge);
-            NG_RETURN_IF_ERROR(ret);
+            NG_RETURN_IF_ERROR(analyzeExpression(lExpr->left(), items, kind, isEdge));
+            NG_RETURN_IF_ERROR(analyzeExpression(lExpr->right(), items, kind, isEdge));
             break;
         }
         case Expression::Kind::kRelLE:
@@ -365,11 +360,11 @@ Expression::Kind IndexScanRule::reverseRelationalExprKind(Expression::Kind kind)
 }
 
 IndexItem IndexScanRule::findOptimalIndex(graph::QueryContext *qctx,
-                                          const OptGroupExpr *groupExpr,
+                                          const OptGroupNode *groupNode,
                                           const FilterItems& items) const {
     // The rule of priority is '==' --> '< > <= >=' --> '!='
     // Step 1 : find out all valid indexes for where condition.
-    auto validIndexes = findValidIndex(qctx, groupExpr, items);
+    auto validIndexes = findValidIndex(qctx, groupNode, items);
     if (validIndexes.empty()) {
         LOG(ERROR) << "No valid index found";
         return nullptr;
@@ -389,10 +384,10 @@ IndexItem IndexScanRule::findOptimalIndex(graph::QueryContext *qctx,
 
 std::vector<IndexItem>
 IndexScanRule::allIndexesBySchema(graph::QueryContext *qctx,
-                                  const OptGroupExpr *groupExpr) const {
-    auto ret = isEdge(groupExpr)
-               ? qctx->getMetaClient()->getEdgeIndexesFromCache(spaceId(groupExpr))
-               : qctx->getMetaClient()->getTagIndexesFromCache(spaceId(groupExpr));
+                                  const OptGroupNode *groupNode) const {
+    auto ret = isEdge(groupNode)
+               ? qctx->getMetaClient()->getEdgeIndexesFromCache(spaceId(groupNode))
+               : qctx->getMetaClient()->getTagIndexesFromCache(spaceId(groupNode));
     if (!ret.ok()) {
         LOG(ERROR) << "No index was found";
         return {};
@@ -400,10 +395,10 @@ IndexScanRule::allIndexesBySchema(graph::QueryContext *qctx,
     std::vector<IndexItem> indexes;
     for (auto& index : ret.value()) {
         // TODO (sky) : ignore rebuilding indexes
-        auto id = isEdge(groupExpr)
+        auto id = isEdge(groupNode)
                   ? index->get_schema_id().get_edge_type()
                   : index->get_schema_id().get_tag_id();
-        if (id == schemaId(groupExpr)) {
+        if (id == schemaId(groupNode)) {
             indexes.emplace_back(index);
         }
     }
@@ -416,9 +411,9 @@ IndexScanRule::allIndexesBySchema(graph::QueryContext *qctx,
 
 std::vector<IndexItem>
 IndexScanRule::findValidIndex(graph::QueryContext *qctx,
-                              const OptGroupExpr *groupExpr,
+                              const OptGroupNode *groupNode,
                               const FilterItems& items) const {
-    auto indexes = allIndexesBySchema(qctx, groupExpr);
+    auto indexes = allIndexesBySchema(qctx, groupNode);
     if (indexes.empty()) {
         return indexes;
     }
diff --git a/src/optimizer/rule/IndexScanRule.h b/src/optimizer/rule/IndexScanRule.h
index a492342561acb07cacb1ea1cad41143037151a38..8ba09e34ac5521719a5cac76fadcf3140b539934 100644
--- a/src/optimizer/rule/IndexScanRule.h
+++ b/src/optimizer/rule/IndexScanRule.h
@@ -105,17 +105,17 @@ private:
                                ScanKind kind,
                                const FilterItems& items,
                                graph::QueryContext *qctx,
-                               const OptGroupExpr *groupExpr) const;
+                               const OptGroupNode *groupNode) const;
 
     Status createIQCWithLogicAnd(IndexQueryCtx &iqctx,
                                  const FilterItems& items,
                                  graph::QueryContext *qctx,
-                                 const OptGroupExpr *groupExpr) const;
+                                 const OptGroupNode *groupNode) const;
 
     Status createIQCWithLogicOR(IndexQueryCtx &iqctx,
                                 const FilterItems& items,
                                 graph::QueryContext *qctx,
-                                const OptGroupExpr *groupExpr) const;
+                                const OptGroupNode *groupNode) const;
 
     Status appendIQCtx(const IndexItem& index,
                        const FilterItems& items,
@@ -129,13 +129,13 @@ private:
                       const meta::cpp2::ColumnDef& col,
                       Value& begin, Value& end) const;
 
-    bool isEdge(const OptGroupExpr *groupExpr) const;
+    bool isEdge(const OptGroupNode *groupNode) const;
 
-    int32_t schemaId(const OptGroupExpr *groupExpr) const;
+    int32_t schemaId(const OptGroupNode *groupNode) const;
 
-    GraphSpaceID spaceId(const OptGroupExpr *groupExpr) const;
+    GraphSpaceID spaceId(const OptGroupNode *groupNode) const;
 
-    std::unique_ptr<Expression> filterExpr(const OptGroupExpr *groupExpr) const;
+    std::unique_ptr<Expression> filterExpr(const OptGroupNode *groupNode) const;
 
     Status analyzeExpression(Expression* expr, FilterItems* items,
                              ScanKind* kind, bool isEdge) const;
@@ -148,14 +148,14 @@ private:
     Expression::Kind reverseRelationalExprKind(Expression::Kind kind) const;
 
     IndexItem findOptimalIndex(graph::QueryContext *qctx,
-                               const OptGroupExpr *groupExpr,
+                               const OptGroupNode *groupNode,
                                const FilterItems& items) const;
 
     std::vector<IndexItem>
-    allIndexesBySchema(graph::QueryContext *qctx, const OptGroupExpr *groupExpr) const;
+    allIndexesBySchema(graph::QueryContext *qctx, const OptGroupNode *groupNode) const;
 
     std::vector<IndexItem> findValidIndex(graph::QueryContext *qctx,
-                                          const OptGroupExpr *groupExpr,
+                                          const OptGroupNode *groupNode,
                                           const FilterItems& items) const;
 
     std::vector<IndexItem> findIndexForEqualScan(const std::vector<IndexItem>& indexes,
diff --git a/src/optimizer/rule/LimitPushDownRule.cpp b/src/optimizer/rule/LimitPushDownRule.cpp
index e32859223ec6522f72e4d09431f23e9950e0b1f6..dadeae08fa1d7ee84fd0bad920ab921aa11f117a 100644
--- a/src/optimizer/rule/LimitPushDownRule.cpp
+++ b/src/optimizer/rule/LimitPushDownRule.cpp
@@ -44,13 +44,13 @@ const Pattern &LimitPushDownRule::pattern() const {
 StatusOr<OptRule::TransformResult> LimitPushDownRule::transform(
     QueryContext *qctx,
     const MatchedResult &matched) const {
-    auto limitExpr = matched.node;
-    auto projExpr = matched.dependencies.front().node;
-    auto gnExpr = matched.dependencies.front().dependencies.front().node;
+    auto limitGroupNode = matched.node;
+    auto projGroupNode = matched.dependencies.front().node;
+    auto gnGroupNode = matched.dependencies.front().dependencies.front().node;
 
-    const auto limit = static_cast<const Limit *>(limitExpr->node());
-    const auto proj = static_cast<const Project *>(projExpr->node());
-    const auto gn = static_cast<const GetNeighbors *>(gnExpr->node());
+    const auto limit = static_cast<const Limit *>(limitGroupNode->node());
+    const auto proj = static_cast<const Project *>(projGroupNode->node());
+    const auto gn = static_cast<const GetNeighbors *>(gnGroupNode->node());
 
     int64_t limitRows = limit->offset() + limit->count();
     if (limitRows >= gn->limit()) {
@@ -58,27 +58,26 @@ StatusOr<OptRule::TransformResult> LimitPushDownRule::transform(
     }
 
     auto newLimit = limit->clone(qctx);
-    auto newLimitExpr = OptGroupExpr::create(qctx, newLimit, limitExpr->group());
+    auto newLimitGroupNode = OptGroupNode::create(qctx, newLimit, limitGroupNode->group());
 
     auto newProj = proj->clone(qctx);
     auto newProjGroup = OptGroup::create(qctx);
-    auto newProjExpr = newProjGroup->makeGroupExpr(qctx, newProj);
+    auto newProjGroupNode = newProjGroup->makeGroupNode(qctx, newProj);
 
     auto newGn = gn->clone(qctx);
     newGn->setLimit(limitRows);
     auto newGnGroup = OptGroup::create(qctx);
-    auto newGnExpr = newGnGroup->makeGroupExpr(qctx, newGn);
+    auto newGnGroupNode = newGnGroup->makeGroupNode(qctx, newGn);
 
-    newLimitExpr->dependsOn(newProjGroup);
-    newProjExpr->dependsOn(newGnGroup);
-    for (auto dep : gnExpr->dependencies()) {
-        newGnExpr->dependsOn(dep);
+    newLimitGroupNode->dependsOn(newProjGroup);
+    newProjGroupNode->dependsOn(newGnGroup);
+    for (auto dep : gnGroupNode->dependencies()) {
+        newGnGroupNode->dependsOn(dep);
     }
 
     TransformResult result;
     result.eraseAll = true;
-    result.eraseCurr = true;
-    result.newGroupExprs.emplace_back(newLimitExpr);
+    result.newGroupNodes.emplace_back(newLimitGroupNode);
     return result;
 }
 
diff --git a/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp b/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp
index bb6f55298a7c28fd5cc8f51be038d87910374e41..8218afe600dfaace63705b66a95802c8902ea17f 100644
--- a/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp
+++ b/src/optimizer/rule/PushFilterDownGetNbrsRule.cpp
@@ -41,10 +41,10 @@ const Pattern &PushFilterDownGetNbrsRule::pattern() const {
 StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform(
     QueryContext *qctx,
     const MatchedResult &matched) const {
-    auto filterGroupExpr = matched.node;
-    auto gnGroupExpr = matched.dependencies.front().node;
-    auto filter = static_cast<const Filter *>(filterGroupExpr->node());
-    auto gn = static_cast<const GetNeighbors *>(gnGroupExpr->node());
+    auto filterGroupNode = matched.node;
+    auto gnGroupNode = matched.dependencies.front().node;
+    auto filter = static_cast<const Filter *>(filterGroupNode->node());
+    auto gn = static_cast<const GetNeighbors *>(gnGroupNode->node());
 
     auto condition = filter->condition()->clone();
     graph::ExtractFilterExprVisitor visitor;
@@ -55,12 +55,12 @@ StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform(
 
     auto pool = qctx->objPool();
     auto remainedExpr = std::move(visitor).remainedExpr();
-    OptGroupExpr *newFilterGroupExpr = nullptr;
+    OptGroupNode *newFilterGroupNode = nullptr;
     if (remainedExpr != nullptr) {
         auto newFilter = Filter::make(qctx, nullptr, pool->add(remainedExpr.release()));
         newFilter->setOutputVar(filter->outputVar());
         newFilter->setInputVar(filter->inputVar());
-        newFilterGroupExpr = OptGroupExpr::create(qctx, newFilter, filterGroupExpr->group());
+        newFilterGroupNode = OptGroupNode::create(qctx, newFilter, filterGroupNode->group());
     }
 
     auto newGNFilter = condition->encode();
@@ -74,25 +74,25 @@ StatusOr<OptRule::TransformResult> PushFilterDownGetNbrsRule::transform(
     auto newGN = gn->clone(qctx);
     newGN->setFilter(newGNFilter);
 
-    OptGroupExpr *newGnGroupExpr = nullptr;
-    if (newFilterGroupExpr != nullptr) {
-        // Filter(A&&B)->GetNeighbors(C) => Filter(A)->GetNeighbors(B&&C)
+    OptGroupNode *newGnGroupNode = nullptr;
+    if (newFilterGroupNode != nullptr) {
+        // Filter(A&&B)<-GetNeighbors(C) => Filter(A)<-GetNeighbors(B&&C)
         auto newGroup = OptGroup::create(qctx);
-        newGnGroupExpr = newGroup->makeGroupExpr(qctx, newGN);
-        newFilterGroupExpr->dependsOn(newGroup);
+        newGnGroupNode = newGroup->makeGroupNode(qctx, newGN);
+        newFilterGroupNode->dependsOn(newGroup);
     } else {
-        // Filter(A)->GetNeighbors(C) => GetNeighbors(A&&C)
-        newGnGroupExpr = OptGroupExpr::create(qctx, newGN, filterGroupExpr->group());
+        // Filter(A)<-GetNeighbors(C) => GetNeighbors(A&&C)
+        newGnGroupNode = OptGroupNode::create(qctx, newGN, filterGroupNode->group());
         newGN->setOutputVar(filter->outputVar());
     }
 
-    for (auto dep : gnGroupExpr->dependencies()) {
-        newGnGroupExpr->dependsOn(dep);
+    for (auto dep : gnGroupNode->dependencies()) {
+        newGnGroupNode->dependsOn(dep);
     }
 
     TransformResult result;
     result.eraseCurr = true;
-    result.newGroupExprs.emplace_back(newFilterGroupExpr ? newFilterGroupExpr : newGnGroupExpr);
+    result.newGroupNodes.emplace_back(newFilterGroupNode ? newFilterGroupNode : newGnGroupNode);
     return result;
 }
 
diff --git a/src/optimizer/rule/TopNRule.cpp b/src/optimizer/rule/TopNRule.cpp
index 1912d0962b0fa58803c4ec199e5454e5091a0dd0..b7886b0bd958e4d28abc46477a1ac39e0f775991 100644
--- a/src/optimizer/rule/TopNRule.cpp
+++ b/src/optimizer/rule/TopNRule.cpp
@@ -40,10 +40,10 @@ const Pattern &TopNRule::pattern() const {
 
 StatusOr<OptRule::TransformResult> TopNRule::transform(QueryContext *qctx,
                                                        const MatchedResult &matched) const {
-    auto limitExpr = matched.node;
-    auto sortExpr = matched.dependencies.front().node;
-    auto limit = static_cast<const Limit *>(limitExpr->node());
-    auto sort = static_cast<const Sort *>(sortExpr->node());
+    auto limitGroupNode = matched.node;
+    auto sortGroupNode = matched.dependencies.front().node;
+    auto limit = static_cast<const Limit *>(limitGroupNode->node());
+    auto sort = static_cast<const Sort *>(sortGroupNode->node());
 
     // Currently, we cannot know the total amount of input data,
     // so only apply topn rule when offset of limit is 0
@@ -55,15 +55,14 @@ StatusOr<OptRule::TransformResult> TopNRule::transform(QueryContext *qctx,
     topn->setOutputVar(limit->outputVar());
     topn->setInputVar(sort->inputVar());
     topn->setColNames(sort->colNames());
-    auto topnExpr = OptGroupExpr::create(qctx, topn, limitExpr->group());
-    for (auto dep : sortExpr->dependencies()) {
-        topnExpr->dependsOn(dep);
+    auto topnNode = OptGroupNode::create(qctx, topn, limitGroupNode->group());
+    for (auto dep : sortGroupNode->dependencies()) {
+        topnNode->dependsOn(dep);
     }
 
     TransformResult result;
-    result.newGroupExprs.emplace_back(topnExpr);
+    result.newGroupNodes.emplace_back(topnNode);
     result.eraseAll = true;
-    result.eraseCurr = true;
     return result;
 }
 
diff --git a/tests/query/v2/test_optimizer.py b/tests/query/v2/test_optimizer.py
index 580ad093e67d373e7ecde9eebf4647f562b1a4bb..fe4da2163dd6138e07e65ec7f4fb6c921c3a439f 100644
--- a/tests/query/v2/test_optimizer.py
+++ b/tests/query/v2/test_optimizer.py
@@ -218,6 +218,5 @@ class TestOptimizer(NebulaTestSuite):
         ]
         # expected_data = [[1998], [2004], [2009], [2010], [2011], [2014], [2017], [2018]]
         self.check_exec_plan(resp, expected_plan)
-        if resp.data is None:
-            assert False, 'resp.data is None'
-        assert len(resp.data.rows) == 4
\ No newline at end of file
+        assert resp.data is not None, 'resp.data is None'
+        assert len(resp.data.rows) == 4