From dabdeeff2dfee3d858fbfc8ea9ffc52a4ff0757d Mon Sep 17 00:00:00 2001
From: "kyle.cao" <kyle.cao@vesoft.com>
Date: Mon, 11 Jan 2021 10:36:37 +0800
Subject: [PATCH] groupby refactoring (#503)

* refactor groupBy impl

tmp commit

modify parser

modify subgraph

graphd compiled

complied

fix empty collect

fix UT

add bug

fix count

add visitors

add projCol

compiled

add parser ut

fix ut

plan validated

add pushUp flag

small change

fix ut memory leak

fix memory leak

handle yield validator

implicit groupby impl

add check

fix memory leak

fix memory leak

small change

fix memory leak

add agg check

* implicit groupBy refactoring

bak

add tck test cases

fix ut

implicit agg refactoring

fix agg Project

* small change

* fix memory leak

* add agg checks

fix agg check

small change

add test cases

* add tck test cases

add tck test cases

add group by tck test

small change

* fix test

modify visitors

small change

small change

fix avg

small change

small change

small change

* review

* delete AggFun cmake

* small change

* small change

* add multiVar check

* fix conflict

refactor implicit groupBy

fix

* add implicit groupBy where test cases

* fix ci

fix ci

* add case

* refactor GroupByValidator

fix case

small change

small change

small change

small change

small change

fix memory leak

fmt

small change

* review

fix ut

small change

* modify group by list check

* move test_groupby.py cases to tck

* add multiple user var check

multiple user var check

Co-authored-by: Yee <2520865+yixinglu@users.noreply.github.com>
---
 .gitignore                                    |   2 +
 src/context/test/CMakeLists.txt               |   1 -
 src/daemons/CMakeLists.txt                    |   1 -
 src/executor/admin/SubmitJobExecutor.cpp      |   2 +-
 src/executor/query/AggregateExecutor.cpp      |  33 +-
 src/executor/test/AggregateTest.cpp           | 220 +++----
 src/executor/test/CMakeLists.txt              |   1 -
 src/optimizer/test/CMakeLists.txt             |   1 -
 src/parser/AdminSentences.cpp                 |   2 +-
 src/parser/Clauses.cpp                        |  13 +-
 src/parser/Clauses.h                          |  35 +-
 src/parser/TraverseSentences.cpp              |  12 +-
 src/parser/TraverseSentences.h                |  19 +-
 src/parser/parser.yy                          |  46 +-
 src/parser/test/CMakeLists.txt                |   1 -
 src/parser/test/ParserTest.cpp                |  62 +-
 src/planner/Query.cpp                         |   6 +-
 src/planner/Query.h                           |  18 +-
 src/planner/match/StartVidFinder.cpp          |   2 +-
 src/util/ExpressionUtils.h                    |   1 +
 src/util/test/CMakeLists.txt                  |   1 -
 src/validator/GetSubgraphValidator.cpp        |  31 +-
 src/validator/GoValidator.cpp                 |   9 +-
 src/validator/GroupByValidator.cpp            | 220 +++++--
 src/validator/GroupByValidator.h              |  17 +-
 src/validator/Validator.h                     |  54 +-
 src/validator/YieldValidator.cpp              | 128 ++--
 src/validator/YieldValidator.h                |   8 +-
 src/validator/test/CMakeLists.txt             |   1 -
 src/validator/test/GroupByValidatorTest.cpp   | 104 +++-
 src/validator/test/QueryValidatorTest.cpp     |   5 +-
 src/validator/test/YieldValidatorTest.cpp     |  26 +-
 src/visitor/CMakeLists.txt                    |   2 +
 src/visitor/CollectAllExprsVisitor.cpp        |   5 +
 src/visitor/CollectAllExprsVisitor.h          |   1 +
 src/visitor/DeduceTypeVisitor.cpp             |  63 +-
 src/visitor/DeduceTypeVisitor.h               |   1 +
 src/visitor/ExprVisitorImpl.cpp               |   5 +
 src/visitor/ExprVisitorImpl.h                 |   1 +
 src/visitor/FindAnyExprVisitor.cpp            |   6 +
 src/visitor/FindAnyExprVisitor.h              |   1 +
 src/visitor/FindAnySubExprVisitor.cpp         | 206 +++++++
 src/visitor/FindAnySubExprVisitor.h           |  83 +++
 src/visitor/FoldConstantExprVisitor.cpp       |  10 +
 src/visitor/FoldConstantExprVisitor.h         |   1 +
 src/visitor/RewriteAggExprVisitor.cpp         |  71 +++
 src/visitor/RewriteAggExprVisitor.h           |  70 +++
 src/visitor/RewriteInputPropVisitor.cpp       |   8 +
 src/visitor/RewriteInputPropVisitor.h         |   1 +
 src/visitor/RewriteSymExprVisitor.cpp         |   8 +
 src/visitor/RewriteSymExprVisitor.h           |   1 +
 src/visitor/test/CMakeLists.txt               |   1 -
 tests/query/v1/test_yield.py                  |  16 +-
 tests/tck/features/agg/Agg.feature            | 559 ++++++++++++++++++
 tests/tck/features/go/GO.feature              |   3 +-
 .../features/go/GroupbyLimit.IntVid.feature   |   8 +-
 tests/tck/features/go/GroupbyLimit.feature    |   8 +-
 57 files changed, 1813 insertions(+), 408 deletions(-)
 create mode 100644 src/visitor/FindAnySubExprVisitor.cpp
 create mode 100644 src/visitor/FindAnySubExprVisitor.h
 create mode 100644 src/visitor/RewriteAggExprVisitor.cpp
 create mode 100644 src/visitor/RewriteAggExprVisitor.h
 create mode 100644 tests/tck/features/agg/Agg.feature

diff --git a/.gitignore b/.gitignore
index 01f7ec21..c3914e63 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,8 @@ nebula-python
 .classpath
 cmake-build*/
 .vscode/
+core.*
+workspace.*
 
 #py
 *.egg-info
diff --git a/src/context/test/CMakeLists.txt b/src/context/test/CMakeLists.txt
index 3c730164..ab775c1d 100644
--- a/src/context/test/CMakeLists.txt
+++ b/src/context/test/CMakeLists.txt
@@ -4,7 +4,6 @@
 # attached with Common Clause Condition 1.0, found in the LICENSES directory.
 
 SET(CONTEXT_TEST_LIBS
-    $<TARGET_OBJECTS:common_agg_function_obj>
     $<TARGET_OBJECTS:common_charset_obj>
     $<TARGET_OBJECTS:common_datatypes_obj>
     $<TARGET_OBJECTS:common_encryption_obj>
diff --git a/src/daemons/CMakeLists.txt b/src/daemons/CMakeLists.txt
index 201505bc..fb69079a 100644
--- a/src/daemons/CMakeLists.txt
+++ b/src/daemons/CMakeLists.txt
@@ -54,7 +54,6 @@ nebula_add_executable(
         $<TARGET_OBJECTS:common_charset_obj>
         $<TARGET_OBJECTS:common_encryption_obj>
         $<TARGET_OBJECTS:common_function_manager_obj>
-        $<TARGET_OBJECTS:common_agg_function_obj>
         $<TARGET_OBJECTS:common_conf_obj>
         $<TARGET_OBJECTS:common_time_utils_obj>
         $<TARGET_OBJECTS:common_graph_obj>
diff --git a/src/executor/admin/SubmitJobExecutor.cpp b/src/executor/admin/SubmitJobExecutor.cpp
index 84ba6191..3e3e5902 100644
--- a/src/executor/admin/SubmitJobExecutor.cpp
+++ b/src/executor/admin/SubmitJobExecutor.cpp
@@ -112,7 +112,7 @@ folly::Future<Status> SubmitJobExecutor::execute() {
             // no default so the compiler will warning when lack
             }
             DLOG(FATAL) << "Unknown job operation " << static_cast<int>(jobOp);
-            return Status::Error("Unkown job job operation %d.", static_cast<int>(jobOp));
+            return Status::Error("Unknown job job operation %d.", static_cast<int>(jobOp));
         });
 }
 
diff --git a/src/executor/query/AggregateExecutor.cpp b/src/executor/query/AggregateExecutor.cpp
index d1e8316e..4e0f8841 100644
--- a/src/executor/query/AggregateExecutor.cpp
+++ b/src/executor/query/AggregateExecutor.cpp
@@ -7,7 +7,7 @@
 #include "executor/query/AggregateExecutor.h"
 
 #include "common/datatypes/List.h"
-#include "common/function/AggregateFunction.h"
+#include "common/expression/AggregateExpression.h"
 #include "context/QueryExpressionContext.h"
 #include "context/Result.h"
 #include "planner/PlanNode.h"
@@ -26,7 +26,7 @@ folly::Future<Status> AggregateExecutor::execute() {
     DCHECK(!!iter);
     QueryExpressionContext ctx(ectx_);
 
-    std::unordered_map<List, std::vector<std::unique_ptr<AggFun>>> result;
+    std::unordered_map<List, std::vector<std::unique_ptr<AggData>>, std::hash<nebula::List>> result;
     for (; iter->valid(); iter->next()) {
         List list;
         for (auto& key : groupKeys) {
@@ -35,19 +35,22 @@ folly::Future<Status> AggregateExecutor::execute() {
 
         auto it = result.find(list);
         if (it == result.end()) {
-            std::vector<std::unique_ptr<AggFun>> funs;
-            for (auto& item : groupItems) {
-                auto fun = AggFun::aggFunMap_[item.func](item.distinct);
-                auto& v = item.expr->eval(ctx(iter.get()));
-                fun->apply(v);
-                funs.emplace_back(std::move(fun));
-            }
-            result.emplace(std::make_pair(std::move(list), std::move(funs)));
+           std::vector<std::unique_ptr<AggData>> cols;
+           for (size_t i = 0; i < groupItems.size(); ++i) {
+               cols.emplace_back(new AggData());
+           }
+           result.emplace(std::make_pair(list, std::move(cols)));
         } else {
             DCHECK_EQ(it->second.size(), groupItems.size());
-            for (size_t i = 0; i < groupItems.size(); ++i) {
-                auto& v = groupItems[i].expr->eval(ctx(iter.get()));
-                it->second[i]->apply(v);
+        }
+
+        for (size_t i = 0; i < groupItems.size(); ++i) {
+            auto* item = groupItems[i];
+            if (item->kind() == Expression::Kind::kAggregate) {
+                static_cast<AggregateExpression*>(item)->setAggData(result[list][i].get());
+                item->eval(ctx(iter.get()));
+            } else {
+                result[list][i]->setResult(item->eval(ctx(iter.get())));
             }
         }
     }
@@ -57,8 +60,8 @@ folly::Future<Status> AggregateExecutor::execute() {
     ds.rows.reserve(result.size());
     for (auto& kv : result) {
         Row row;
-        for (auto& f : kv.second) {
-            row.values.emplace_back(f->getResult());
+        for (auto& v : kv.second) {
+            row.values.emplace_back(v->result());
         }
         ds.rows.emplace_back(std::move(row));
     }
diff --git a/src/executor/test/AggregateTest.cpp b/src/executor/test/AggregateTest.cpp
index 30b4309a..bb12ce0c 100644
--- a/src/executor/test/AggregateTest.cpp
+++ b/src/executor/test/AggregateTest.cpp
@@ -85,11 +85,11 @@ struct RowCmp {
 
 #define TEST_AGG_1(FUN, COL, DISTINCT)                                      \
     std::vector<Expression*> groupKeys;                                     \
-    std::vector<Aggregate::GroupItem> groupItems;                           \
+    std::vector<Expression*> groupItems;                                    \
     auto expr =                                                             \
         std::make_unique<InputPropertyExpression>(new std::string("col1")); \
-    Aggregate::GroupItem item(expr.get(), FUN, DISTINCT);                   \
-    groupItems.emplace_back(std::move(item));                               \
+    AggregateExpression item(new std::string(FUN), expr.release(), DISTINCT); \
+    groupItems.emplace_back(&item);                                         \
     auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
                                 std::move(groupItems));                     \
     agg->setInputVar(*input_);                                              \
@@ -105,12 +105,13 @@ struct RowCmp {
 
 #define TEST_AGG_2(FUN, COL, DISTINCT)                                      \
     std::vector<Expression*> groupKeys;                                     \
-    std::vector<Aggregate::GroupItem> groupItems;                           \
-    auto expr =                                                             \
+    std::vector<Expression*> groupItems;                                    \
+    auto expr1 =                                                            \
         std::make_unique<InputPropertyExpression>(new std::string("col2")); \
-    groupKeys.emplace_back(expr.get());                                     \
-    Aggregate::GroupItem item(expr.get(), FUN, DISTINCT);                   \
-    groupItems.emplace_back(std::move(item));                               \
+    auto expr2 = expr1->clone();                                            \
+    groupKeys.emplace_back(expr1.get());                                    \
+    AggregateExpression item(new std::string(FUN), expr2.release(), DISTINCT); \
+    groupItems.emplace_back(&item);                                         \
     auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
                                 std::move(groupItems));                     \
     agg->setInputVar(*input_);                                              \
@@ -128,17 +129,18 @@ struct RowCmp {
 
 #define TEST_AGG_3(FUN, COL, DISTINCT)                                      \
     std::vector<Expression*> groupKeys;                                     \
-    std::vector<Aggregate::GroupItem> groupItems;                           \
-    auto expr =                                                             \
-        std::make_unique<InputPropertyExpression>(new std::string("col2")); \
-    groupKeys.emplace_back(expr.get());                                     \
-    Aggregate::GroupItem item(expr.get(), AggFun::Function::kNone, false);  \
-    groupItems.emplace_back(std::move(item));                               \
+    std::vector<Expression*> groupItems;                                    \
     auto expr1 =                                                            \
-        std::make_unique<InputPropertyExpression>(new std::string("col3")); \
+        std::make_unique<InputPropertyExpression>(new std::string("col2")); \
+    auto expr2 = expr1->clone(); \
     groupKeys.emplace_back(expr1.get());                                    \
-    Aggregate::GroupItem item1(expr1.get(), FUN, DISTINCT);                 \
-    groupItems.emplace_back(std::move(item1));                              \
+    AggregateExpression item(new std::string(""), expr2.release(), false);  \
+    groupItems.emplace_back(&item);                                         \
+    auto expr3 =                                                            \
+        std::make_unique<InputPropertyExpression>(new std::string("col3")); \
+    groupKeys.emplace_back(expr3.get());                                    \
+    AggregateExpression item1(new std::string(FUN), expr3.release(), DISTINCT); \
+    groupItems.emplace_back(&item1);                                        \
     auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
                                 std::move(groupItems));                     \
     agg->setInputVar(*input_);                                              \
@@ -156,10 +158,10 @@ struct RowCmp {
 
 #define TEST_AGG_4(FUN, COL, DISTINCT)                                      \
     std::vector<Expression*> groupKeys;                                     \
-    std::vector<Aggregate::GroupItem> groupItems;                           \
+    std::vector<Expression*> groupItems;                                    \
     auto expr = std::make_unique<ConstantExpression>(1);                    \
-    Aggregate::GroupItem item(expr.get(), FUN, DISTINCT);                   \
-    groupItems.emplace_back(std::move(item));                               \
+    AggregateExpression item(new std::string(FUN), expr.release(), DISTINCT); \
+    groupItems.emplace_back(&item);                                         \
     auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys), \
                                 std::move(groupItems));                     \
     agg->setInputVar(*input_);                                              \
@@ -204,7 +206,7 @@ TEST_F(AggregateTest, Group) {
 
         // key = col2
         // items = col2
-        TEST_AGG_2(AggFun::Function::kNone, "col2", false)
+        TEST_AGG_2("", "col2", false)
     }
     {
         // ===============
@@ -237,7 +239,7 @@ TEST_F(AggregateTest, Group) {
 
         // key = col2, col3
         // items = col2, col3
-        TEST_AGG_3(AggFun::Function::kNone, "col3", false)
+        TEST_AGG_3("", "col3", false)
     }
 }
 
@@ -260,7 +262,7 @@ TEST_F(AggregateTest, Collect) {
 
         // key =
         // items = collect(col1)
-        TEST_AGG_1(AggFun::Function::kCollect, "list", false)
+        TEST_AGG_1("COLLECT", "list", false)
     }
     {
         // ========
@@ -301,12 +303,13 @@ TEST_F(AggregateTest, Collect) {
         // key = col1
         // items = collect(col1)
         std::vector<Expression*> groupKeys;
-        std::vector<Aggregate::GroupItem> groupItems;
-        auto expr =
+        std::vector<Expression*> groupItems;
+        auto expr1 =
             std::make_unique<InputPropertyExpression>(new std::string("col1"));
-        groupKeys.emplace_back(expr.get());
-        Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, false);
-        groupItems.emplace_back(std::move(item));
+        auto expr2 = expr1->clone();
+        groupKeys.emplace_back(expr1.get());
+        AggregateExpression item(new std::string("COLLECT"), expr2.release(), false);
+        groupItems.emplace_back(std::move(&item));
         auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
                                     std::move(groupItems));
         agg->setInputVar(*input_);
@@ -367,7 +370,7 @@ TEST_F(AggregateTest, Collect) {
 
         // key = col2, col3
         // items = col2, collect(col3)
-        TEST_AGG_3(AggFun::Function::kCollect, "list", false)
+        TEST_AGG_3("COLLECT", "list", false)
     }
     {
         // ====================================
@@ -387,7 +390,7 @@ TEST_F(AggregateTest, Collect) {
 
         // key =
         // items = collect(1)
-        TEST_AGG_4(AggFun::Function::kCollect, "list", false)
+        TEST_AGG_4("COLLECT", "list", false)
     }
     {
         // ====================================
@@ -408,11 +411,11 @@ TEST_F(AggregateTest, Collect) {
         // key =
         // items = collect(distinct col1)
         std::vector<Expression*> groupKeys;
-        std::vector<Aggregate::GroupItem> groupItems;
+        std::vector<Expression*> groupItems;
         auto expr =
             std::make_unique<InputPropertyExpression>(new std::string("col1"));
-        Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, true);
-        groupItems.emplace_back(std::move(item));
+        AggregateExpression item(new std::string("COLLECT"), expr.release(), true);
+        groupItems.emplace_back(std::move(&item));
         auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
                                     std::move(groupItems));
         agg->setInputVar(*input_);
@@ -469,12 +472,12 @@ TEST_F(AggregateTest, Collect) {
         // key = col1
         // items = collect(distinct col1)
         std::vector<Expression*> groupKeys;
-        std::vector<Aggregate::GroupItem> groupItems;
-        auto expr =
+        std::vector<Expression*> groupItems;
+        auto expr1 =
             std::make_unique<InputPropertyExpression>(new std::string("col1"));
-        groupKeys.emplace_back(expr.get());
-        Aggregate::GroupItem item(expr.get(), AggFun::Function::kCollect, true);
-        groupItems.emplace_back(std::move(item));
+        auto expr2 = expr1->clone();
+        AggregateExpression item(new std::string("COLLECT"), expr2.release(), true);
+        groupItems.emplace_back(std::move(&item));
         auto* agg = Aggregate::make(qctx_.get(), nullptr, std::move(groupKeys),
                                     std::move(groupItems));
         agg->setInputVar(*input_);
@@ -535,7 +538,8 @@ TEST_F(AggregateTest, Collect) {
 
         // key = col2, col3
         // items = col2, collect(distinct col3)
-        TEST_AGG_3(AggFun::Function::kCollect, "list", true)
+
+       TEST_AGG_3("COLLECT", "list", true)
     }
     {
         // ====================================
@@ -553,7 +557,7 @@ TEST_F(AggregateTest, Collect) {
 
         // key =
         // items = collect(distinct 1)
-        TEST_AGG_4(AggFun::Function::kCollect, "list", true)
+        TEST_AGG_4("COLLECT", "list", true)
     }
 }
 
@@ -572,7 +576,7 @@ TEST_F(AggregateTest, Count) {
 
         // key =
         // items = count(col1)
-        TEST_AGG_1(AggFun::Function::kCount, "count", false)
+        TEST_AGG_1("COUNT", "count", false)
     }
     {
         // ========
@@ -605,7 +609,7 @@ TEST_F(AggregateTest, Count) {
 
         // key = col2
         // items = count(col2)
-        TEST_AGG_2(AggFun::Function::kCount, "count", false)
+        TEST_AGG_2("COUNT", "count", false)
     }
     {
         // ================
@@ -638,7 +642,7 @@ TEST_F(AggregateTest, Count) {
 
         // key = col2, col3
         // items = col2, count(col3)
-        TEST_AGG_3(AggFun::Function::kCount, "count", false)
+        TEST_AGG_3("COUNT", "count", false)
     }
     {
         // ========
@@ -653,7 +657,7 @@ TEST_F(AggregateTest, Count) {
 
         // key =
         // items = count(1)
-        TEST_AGG_4(AggFun::Function::kCount, "count", false)
+        TEST_AGG_4("COUNT", "count", false)
     }
     {
         // ========
@@ -669,7 +673,7 @@ TEST_F(AggregateTest, Count) {
 
         // key =
         // items = count(distinct col1)
-        TEST_AGG_1(AggFun::Function::kCount, "count", true)
+        TEST_AGG_1("COUNT", "count", true)
     }
     {
         // ========
@@ -702,7 +706,7 @@ TEST_F(AggregateTest, Count) {
 
         // key = col2
         // items = count(distinct col2)
-        TEST_AGG_2(AggFun::Function::kCount, "count", true)
+        TEST_AGG_2("COUNT", "count", true)
     }
     {
         // ================
@@ -735,7 +739,7 @@ TEST_F(AggregateTest, Count) {
 
         // key = col2, col3
         // items = col2, count(distinct col3)
-        TEST_AGG_3(AggFun::Function::kCount, "count", true)
+        TEST_AGG_3("COUNT", "count", true)
     }
     {
         // ========
@@ -750,7 +754,7 @@ TEST_F(AggregateTest, Count) {
 
         // key =
         // items = count(distinct 1)
-        TEST_AGG_4(AggFun::Function::kCount, "count", true)
+        TEST_AGG_4("COUNT", "count", true)
     }
 }
 
@@ -769,7 +773,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key =
         // items = sum(col1)
-        TEST_AGG_1(AggFun::Function::kSum, "sum", false)
+        TEST_AGG_1("SUM", "sum", false)
     }
     {
         // ========
@@ -800,7 +804,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key = col2
         // items = sum(col2)
-        TEST_AGG_2(AggFun::Function::kSum, "sum", false)
+       TEST_AGG_2("SUM", "sum", false)
     }
     {
         // ================
@@ -833,7 +837,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key = col2, col3
         // items = col2, sum(col3)
-        TEST_AGG_3(AggFun::Function::kSum, "sum", false)
+        TEST_AGG_3("SUM", "sum", false)
     }
     {
         // ========
@@ -849,7 +853,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key =
         // items = sum(1)
-        TEST_AGG_4(AggFun::Function::kSum, "sum", false)
+        TEST_AGG_4("SUM", "sum", false)
     }
     {
         // ========
@@ -865,7 +869,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key =
         // items = sum(distinct col1)
-        TEST_AGG_1(AggFun::Function::kSum, "sum", true)
+        TEST_AGG_1("SUM", "sum", true)
     }
     {
         // ========
@@ -896,7 +900,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key = col2
         // items = sum(distinct col2)
-        TEST_AGG_2(AggFun::Function::kSum, "sum", true)
+        TEST_AGG_2("SUM", "sum", true)
     }
     {
         // ================
@@ -929,7 +933,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key = col2, col3
         // items = col2, sum(distinct col3)
-        TEST_AGG_3(AggFun::Function::kSum, "sum", true)
+        TEST_AGG_3("SUM", "sum", true)
     }
     {
         // ========
@@ -945,7 +949,7 @@ TEST_F(AggregateTest, Sum) {
 
         // key =
         // items = sum(distinct 1)
-        TEST_AGG_4(AggFun::Function::kSum, "sum", true)
+        TEST_AGG_4("SUM", "sum", true)
     }
 }
 
@@ -964,7 +968,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key =
         // items = avg(col1)
-        TEST_AGG_1(AggFun::Function::kAvg, "avg", false)
+        TEST_AGG_1("AVG", "avg", false)
     }
     {
         // ========
@@ -995,7 +999,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key = col2
         // items = avg(col2)
-        TEST_AGG_2(AggFun::Function::kAvg, "avg", false)
+        TEST_AGG_2("AVG", "avg", false)
     }
     {
         // ================
@@ -1028,7 +1032,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key = col2, col3
         // items = col2, sum(col3)
-        TEST_AGG_3(AggFun::Function::kAvg, "avg", false)
+        TEST_AGG_3("AVG", "avg", false)
     }
     {
         // ========
@@ -1044,7 +1048,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key =
         // items = avg(1)
-        TEST_AGG_4(AggFun::Function::kAvg, "avg", false)
+        TEST_AGG_4("AVG", "avg", false)
     }
     {
         // ========
@@ -1060,7 +1064,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key =
         // items = avg(col1)
-        TEST_AGG_1(AggFun::Function::kAvg, "avg", true)
+        TEST_AGG_1("AVG", "avg", true)
     }
     {
         // ========
@@ -1091,7 +1095,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key = col2
         // items = avg(col2)
-        TEST_AGG_2(AggFun::Function::kAvg, "avg", true)
+        TEST_AGG_2("AVG", "avg", true)
     }
     {
         // ================
@@ -1124,7 +1128,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key = col2, col3
         // items = col2, sum(col3)
-        TEST_AGG_3(AggFun::Function::kAvg, "avg", true)
+        TEST_AGG_3("AVG", "avg", true)
     }
     {
         // ========
@@ -1140,7 +1144,7 @@ TEST_F(AggregateTest, Avg) {
 
         // key =
         // items = avg(1)
-        TEST_AGG_4(AggFun::Function::kAvg, "avg", true)
+        TEST_AGG_4("AVG", "avg", true)
     }
 }
 
@@ -1159,7 +1163,7 @@ TEST_F(AggregateTest, Max) {
 
         // key =
         // items = max(col1)
-        TEST_AGG_1(AggFun::Function::kMax, "max", false)
+        TEST_AGG_1("MAX", "max", false)
     }
     {
         // ================
@@ -1192,7 +1196,7 @@ TEST_F(AggregateTest, Max) {
 
         // key = col2, col3
         // items = col2, max(col3)
-        TEST_AGG_3(AggFun::Function::kMax, "max", false)
+        TEST_AGG_3("MAX", "max", false)
     }
     {
         // ========
@@ -1208,7 +1212,7 @@ TEST_F(AggregateTest, Max) {
 
         // key =
         // items = max(1)
-        TEST_AGG_4(AggFun::Function::kMax, "max", false)
+        TEST_AGG_4("MAX", "max", false)
     }
     {
         // ========
@@ -1224,7 +1228,7 @@ TEST_F(AggregateTest, Max) {
 
         // key =
         // items = max(col1)
-        TEST_AGG_1(AggFun::Function::kMax, "max", true)
+        TEST_AGG_1("MAX", "max", true)
     }
     {
         // ================
@@ -1257,7 +1261,7 @@ TEST_F(AggregateTest, Max) {
 
         // key = col2, col3
         // items = col2, max(col3)
-        TEST_AGG_3(AggFun::Function::kMax, "max", true)
+        TEST_AGG_3("MAX", "max", true)
     }
     {
         // ========
@@ -1273,7 +1277,7 @@ TEST_F(AggregateTest, Max) {
 
         // key =
         // items = max(1)
-        TEST_AGG_4(AggFun::Function::kMax, "max", true)
+        TEST_AGG_4("MAX", "max", true)
     }
 }
 
@@ -1292,7 +1296,7 @@ TEST_F(AggregateTest, Min) {
 
         // key =
         // items = min(col1)
-        TEST_AGG_1(AggFun::Function::kMin, "min", false)
+        TEST_AGG_1("MIN", "min", false)
     }
     {
         // ================
@@ -1325,7 +1329,7 @@ TEST_F(AggregateTest, Min) {
 
         // key = col2, col3
         // items = col2, min(col3)
-        TEST_AGG_3(AggFun::Function::kMin, "min", false)
+        TEST_AGG_3("MIN", "min", false)
     }
     {
         // ========
@@ -1341,7 +1345,7 @@ TEST_F(AggregateTest, Min) {
 
         // key =
         // items = min(1)
-        TEST_AGG_4(AggFun::Function::kMin, "min", false)
+        TEST_AGG_4("MIN", "min", false)
     }
     {
         // ========
@@ -1357,7 +1361,7 @@ TEST_F(AggregateTest, Min) {
 
         // key =
         // items = min(distinct col1)
-        TEST_AGG_1(AggFun::Function::kMin, "min", true)
+        TEST_AGG_1("MIN", "min", true)
     }
     {
         // ================
@@ -1390,7 +1394,7 @@ TEST_F(AggregateTest, Min) {
 
         // key = col2, col3
         // items = col2, min(distinct col3)
-        TEST_AGG_3(AggFun::Function::kMin, "min", true)
+        TEST_AGG_3("MIN", "min", true)
     }
     {
         // ========
@@ -1406,7 +1410,7 @@ TEST_F(AggregateTest, Min) {
 
         // key =
         // items = min(distinct 1)
-        TEST_AGG_4(AggFun::Function::kMin, "min", true)
+        TEST_AGG_4("MIN", "min", true)
     }
 }
 
@@ -1425,7 +1429,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key =
         // items = stdev(col1)
-        TEST_AGG_1(AggFun::Function::kStdev, "stdev", false)
+        TEST_AGG_1("STD", "stdev", false)
     }
     {
         // ===============
@@ -1455,7 +1459,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key = col2
         // items = stdev(col2)
-        TEST_AGG_2(AggFun::Function::kStdev, "stdev", false)
+        TEST_AGG_2("STD", "stdev", false)
     }
     {
         // =======================
@@ -1488,7 +1492,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key = col2, col3
         // items = col2, stdev(col3)
-        TEST_AGG_3(AggFun::Function::kStdev, "stdev", false)
+        TEST_AGG_3("STD", "stdev", false)
     }
     {
         // ===============
@@ -1503,7 +1507,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key =
         // items = stdev(1)
-        TEST_AGG_4(AggFun::Function::kStdev, "stdev", false)
+        TEST_AGG_4("STD", "stdev", false)
     }
     {
         // ===============
@@ -1519,7 +1523,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key =
         // items = stdev(distinct col1)
-        TEST_AGG_1(AggFun::Function::kStdev, "stdev", true)
+        TEST_AGG_1("STD", "stdev", true)
     }
     {
         // ===============
@@ -1549,7 +1553,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key = col2
         // items = stdev(distinct col2)
-        TEST_AGG_2(AggFun::Function::kStdev, "stdev", true)
+        TEST_AGG_2("STD", "stdev", true)
     }
     {
         // =======================
@@ -1582,7 +1586,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key = col2, col3
         // items = col2, stdev(distinct col3)
-        TEST_AGG_3(AggFun::Function::kStdev, "stdev", true)
+        TEST_AGG_3("STD", "stdev", true)
     }
     {
         // ===============
@@ -1598,7 +1602,7 @@ TEST_F(AggregateTest, Stdev) {
 
         // key =
         // items = stdev(distinct 1)
-        TEST_AGG_4(AggFun::Function::kStdev, "stdev", true)
+        TEST_AGG_4("STD", "stdev", true)
     }
 }
 
@@ -1617,7 +1621,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key =
         // items = bit_and(col1)
-        TEST_AGG_1(AggFun::Function::kBitAnd, "bit_and", false)
+        TEST_AGG_1("BIT_AND", "bit_and", false)
     }
     {
         // ===============
@@ -1648,7 +1652,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key = col2
         // items = bit_and(col2)
-        TEST_AGG_2(AggFun::Function::kBitAnd, "bit_and", false)
+        TEST_AGG_2("BIT_AND", "bit_and", false)
     }
     {
         // =======================
@@ -1681,7 +1685,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key = col2, col3
         // items = col2, bit_and(col3)
-        TEST_AGG_3(AggFun::Function::kBitAnd, "bit_and", false)
+        TEST_AGG_3("BIT_AND", "bit_and", false)
     }
     {
         // ===============
@@ -1696,7 +1700,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key =
         // items = bit_and(1)
-        TEST_AGG_4(AggFun::Function::kBitAnd, "bit_and", false)
+        TEST_AGG_4("BIT_AND", "bit_and", false)
     }
     {
         // ===============
@@ -1712,7 +1716,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key =
         // items = bit_and(distinct col1)
-        TEST_AGG_1(AggFun::Function::kBitAnd, "bit_and", true)
+        TEST_AGG_1("BIT_AND", "bit_and", true)
     }
     {
         // ===============
@@ -1743,7 +1747,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key = col2
         // items = bit_and(col2)
-        TEST_AGG_2(AggFun::Function::kBitAnd, "bit_and", true)
+        TEST_AGG_2("BIT_AND", "bit_and", true)
     }
     {
         // =======================
@@ -1776,7 +1780,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key = col2, col3
         // items = col2, bit_and(col3)
-        TEST_AGG_3(AggFun::Function::kBitAnd, "bit_and", true)
+        TEST_AGG_3("BIT_AND", "bit_and", true)
     }
     {
         // ===============
@@ -1792,7 +1796,7 @@ TEST_F(AggregateTest, BitAnd) {
 
         // key =
         // items = bit_and(1)
-        TEST_AGG_4(AggFun::Function::kBitAnd, "bit_and", true)
+        TEST_AGG_4("BIT_AND", "bit_and", true)
     }
 }
 
@@ -1811,7 +1815,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key =
         // items = bit_or(col1)
-        TEST_AGG_1(AggFun::Function::kBitOr, "bit_or", false)
+        TEST_AGG_1("BIT_OR", "bit_or", false)
     }
     {
         // ===============
@@ -1842,7 +1846,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key = col2
         // items = bit_or(col2)
-        TEST_AGG_2(AggFun::Function::kBitOr, "bit_or", false)
+        TEST_AGG_2("BIT_OR", "bit_or", false)
     }
     {
         // =======================
@@ -1875,7 +1879,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key = col2, col3
         // items = col2, bit_or(col3)
-        TEST_AGG_3(AggFun::Function::kBitOr, "bit_or", false)
+        TEST_AGG_3("BIT_OR", "bit_or", false)
     }
     {
         // ===============
@@ -1891,7 +1895,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key =
         // items = bit_or(1)
-        TEST_AGG_4(AggFun::Function::kBitOr, "bit_or", false)
+        TEST_AGG_4("BIT_OR", "bit_or", false)
     }
     {
         // ===============
@@ -1907,7 +1911,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key =
         // items = bit_or(distinct col1)
-        TEST_AGG_1(AggFun::Function::kBitOr, "bit_or", true)
+        TEST_AGG_1("BIT_OR", "bit_or", true)
     }
     {
         // ===============
@@ -1938,7 +1942,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key = col2
         // items = bit_or(distinct col2)
-        TEST_AGG_2(AggFun::Function::kBitOr, "bit_or", true)
+        TEST_AGG_2("BIT_OR", "bit_or", true)
     }
     {
         // =======================
@@ -1971,7 +1975,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key = col2, col3
         // items = col2, bit_or(distinct col3)
-        TEST_AGG_3(AggFun::Function::kBitOr, "bit_or", true)
+        TEST_AGG_3("BIT_OR", "bit_or", true)
     }
     {
         // ===============
@@ -1987,7 +1991,7 @@ TEST_F(AggregateTest, BitOr) {
 
         // key =
         // items = bit_or(distinct 1)
-        TEST_AGG_4(AggFun::Function::kBitOr, "bit_or", true)
+        TEST_AGG_4("BIT_OR", "bit_or", true)
     }
 }
 
@@ -2006,7 +2010,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key =
         // items = bit_xor(col1)
-        TEST_AGG_1(AggFun::Function::kBitXor, "bit_xor", false)
+        TEST_AGG_1("BIT_XOR", "bit_xor", false)
     }
     {
         // ===============
@@ -2037,7 +2041,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key = col2
         // items = bit_xor(col2)
-        TEST_AGG_2(AggFun::Function::kBitXor, "bit_xor", false)
+        TEST_AGG_2("BIT_XOR", "bit_xor", false)
     }
     {
         // =======================
@@ -2070,7 +2074,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key = col2, col3
         // items = col2, bit_xor(col3)
-        TEST_AGG_3(AggFun::Function::kBitXor, "bit_xor", false)
+        TEST_AGG_3("BIT_XOR", "bit_xor", false)
     }
     {
         // ===============
@@ -2086,7 +2090,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key =
         // items = bit_xor(1)
-        TEST_AGG_4(AggFun::Function::kBitXor, "bit_xor", false)
+        TEST_AGG_4("BIT_XOR", "bit_xor", false)
     }
     {
         // ===============
@@ -2102,7 +2106,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key =
         // items = bit_xor(distinct col1)
-        TEST_AGG_1(AggFun::Function::kBitXor, "bit_xor", true)
+        TEST_AGG_1("BIT_XOR", "bit_xor", true)
     }
     {
         // ===============
@@ -2133,7 +2137,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key = col2
         // items = bit_xor(distinct col2)
-        TEST_AGG_2(AggFun::Function::kBitXor, "bit_xor", true)
+        TEST_AGG_2("BIT_XOR", "bit_xor", true)
     }
     {
         // =======================
@@ -2166,7 +2170,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key = col2, col3
         // items = col2, bit_xor(distinct col3)
-        TEST_AGG_3(AggFun::Function::kBitXor, "bit_xor", true)
+        TEST_AGG_3("BIT_XOR", "bit_xor", true)
     }
     {
         // ===============
@@ -2182,7 +2186,7 @@ TEST_F(AggregateTest, BitXor) {
 
         // key =
         // items = bit_xor(distinct 1)
-        TEST_AGG_4(AggFun::Function::kBitXor, "bit_xor", true)
+        TEST_AGG_4("BIT_XOR", "bit_xor", true)
     }
 }
 }  // namespace graph
diff --git a/src/executor/test/CMakeLists.txt b/src/executor/test/CMakeLists.txt
index a814808d..f2b17693 100644
--- a/src/executor/test/CMakeLists.txt
+++ b/src/executor/test/CMakeLists.txt
@@ -27,7 +27,6 @@ SET(EXEC_QUERY_TEST_OBJS
     $<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
     $<TARGET_OBJECTS:common_charset_obj>
     $<TARGET_OBJECTS:common_function_manager_obj>
-    $<TARGET_OBJECTS:common_agg_function_obj>
     $<TARGET_OBJECTS:common_encryption_obj>
     $<TARGET_OBJECTS:common_http_client_obj>
     $<TARGET_OBJECTS:common_time_utils_obj>
diff --git a/src/optimizer/test/CMakeLists.txt b/src/optimizer/test/CMakeLists.txt
index 14fe3950..ba3c961f 100644
--- a/src/optimizer/test/CMakeLists.txt
+++ b/src/optimizer/test/CMakeLists.txt
@@ -26,7 +26,6 @@ set(OPTIMIZER_TEST_LIB
     $<TARGET_OBJECTS:common_encryption_obj>
     $<TARGET_OBJECTS:common_http_client_obj>
     $<TARGET_OBJECTS:common_process_obj>
-    $<TARGET_OBJECTS:common_agg_function_obj>
     $<TARGET_OBJECTS:common_time_utils_obj>
     $<TARGET_OBJECTS:common_graph_obj>
     $<TARGET_OBJECTS:common_ft_es_graph_adapter_obj>
diff --git a/src/parser/AdminSentences.cpp b/src/parser/AdminSentences.cpp
index bd036555..d00285ae 100644
--- a/src/parser/AdminSentences.cpp
+++ b/src/parser/AdminSentences.cpp
@@ -185,7 +185,7 @@ std::string AdminJobSentence::toString() const {
     case meta::cpp2::AdminJobOp::RECOVER:
         return "recover job";
     }
-    LOG(FATAL) << "Unkown job operation " << static_cast<uint8_t>(op_);
+    LOG(FATAL) << "Unknown job operation " << static_cast<uint8_t>(op_);
 }
 
 meta::cpp2::AdminJobOp AdminJobSentence::getOp() const {
diff --git a/src/parser/Clauses.cpp b/src/parser/Clauses.cpp
index 9044188c..73104504 100644
--- a/src/parser/Clauses.cpp
+++ b/src/parser/Clauses.cpp
@@ -115,14 +115,7 @@ std::string WhereClause::toString() const {
 std::string YieldColumn::toString() const {
     std::string buf;
     buf.reserve(256);
-    if (aggFunName_ != nullptr) {
-        buf += *aggFunName_;
-        buf += "(";
-        buf += expr_->toString();
-        buf += ")";
-    } else {
-        buf += expr_->toString();
-    }
+    buf += expr_->toString();
 
     if (alias_ != nullptr) {
         buf += " AS ";
@@ -162,10 +155,6 @@ bool operator==(const YieldColumn &l, const YieldColumn &r) {
         return false;
     }
 
-    if (l.getAggFunName() != r.getAggFunName()) {
-        return false;
-    }
-
     return true;
 }
 
diff --git a/src/parser/Clauses.h b/src/parser/Clauses.h
index f20b7fb8..856f6ca5 100644
--- a/src/parser/Clauses.h
+++ b/src/parser/Clauses.h
@@ -203,6 +203,10 @@ public:
         filter_.reset(expr);
     }
 
+    std::unique_ptr<WhereClause> clone() const {
+        return std::make_unique<WhereClause>(filter_->clone().release());
+    }
+
     std::string toString() const;
 
 private:
@@ -223,9 +227,6 @@ public:
         if (alias_ != nullptr) {
             col->setAlias(new std::string(*alias_));
         }
-        if (aggFunName_ != nullptr) {
-            col->setAggFunction(new std::string(*aggFunName_));
-        }
         return col;
     }
 
@@ -245,26 +246,11 @@ public:
         return alias_.get();
     }
 
-    void setAggFunction(std::string* fun = nullptr) {
-        if (fun == nullptr) {
-            return;
-        }
-        aggFunName_.reset(fun);
-    }
-
-    std::string getAggFunName() const {
-        if (aggFunName_ == nullptr) {
-            return "";
-        }
-        return *aggFunName_;
-    }
-
     std::string toString() const;
 
 private:
     std::unique_ptr<Expression>                 expr_;
     std::unique_ptr<std::string>                alias_;
-    std::unique_ptr<std::string>                aggFunName_{nullptr};
 };
 
 bool operator==(const YieldColumn &l, const YieldColumn &r);
@@ -294,6 +280,14 @@ public:
         return columns_.empty();
     }
 
+    std::unique_ptr<YieldColumns> clone() const {
+        auto cols = std::make_unique<YieldColumns>();
+        for (auto& col : columns_) {
+            cols->addColumn(col->clone().release());
+        }
+        return cols;
+    }
+
     std::string toString() const;
 
     const YieldColumn* back() const {
@@ -328,6 +322,11 @@ public:
         return distinct_;
     }
 
+    std::unique_ptr<YieldClause> clone() const {
+        auto cols = yieldColumns_->clone();
+        return std::make_unique<YieldClause>(cols.release(), distinct_);
+    }
+
     std::string toString() const;
 
 private:
diff --git a/src/parser/TraverseSentences.cpp b/src/parser/TraverseSentences.cpp
index 9c689d63..c4312ddc 100644
--- a/src/parser/TraverseSentences.cpp
+++ b/src/parser/TraverseSentences.cpp
@@ -6,6 +6,7 @@
 
 #include "common/base/Base.h"
 #include "parser/TraverseSentences.h"
+#include "util/ExpressionUtils.h"
 
 namespace nebula {
 
@@ -104,7 +105,7 @@ std::string OrderFactor::toString() const {
         case DESCEND:
             return folly::stringPrintf("%s DESC,", expr_->toString().c_str());
         default:
-            LOG(FATAL) << "Unkown Order Type: " << orderType_;
+            LOG(FATAL) << "Unknown Order Type: " << orderType_;
     }
 }
 
@@ -214,6 +215,15 @@ std::string LimitSentence::toString() const {
     return folly::stringPrintf("LIMIT %ld,%ld", offset_, count_);
 }
 
+bool YieldSentence::hasAgg() const {
+    for (auto* col : columns()) {
+        if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})) {
+            return true;
+        }
+    }
+    return false;
+}
+
 std::string YieldSentence::toString() const {
     std::string buf;
     buf.reserve(256);
diff --git a/src/parser/TraverseSentences.h b/src/parser/TraverseSentences.h
index f3baece9..e23d4797 100644
--- a/src/parser/TraverseSentences.h
+++ b/src/parser/TraverseSentences.h
@@ -554,6 +554,7 @@ public:
         return yieldClause_.get();
     }
 
+    bool hasAgg() const;
     std::string toString() const override;
 
 private:
@@ -563,14 +564,23 @@ private:
 
 class GroupBySentence final : public Sentence {
 public:
-    GroupBySentence() {
+    GroupBySentence(YieldClause* yield = nullptr,
+                    WhereClause* having = nullptr,
+                    GroupClause* groupby = nullptr) {
         kind_ = Kind::kGroupBy;
+        yieldClause_.reset(yield);
+        havingClause_.reset(having);
+        groupClause_.reset(groupby);
     }
 
     void setGroupClause(GroupClause *clause) {
         groupClause_.reset(clause);
     }
 
+    void setHavingClause(WhereClause *clause) {
+        havingClause_.reset(clause);
+    }
+
     void setYieldClause(YieldClause *clause) {
         yieldClause_.reset(clause);
     }
@@ -579,6 +589,10 @@ public:
         return groupClause_.get();
     }
 
+    const WhereClause* havingClause() const {
+        return havingClause_.get();
+    }
+
     const YieldClause* yieldClause() const {
         return yieldClause_.get();
     }
@@ -586,8 +600,9 @@ public:
     std::string toString() const override;
 
 private:
-    std::unique_ptr<GroupClause>   groupClause_;
     std::unique_ptr<YieldClause>   yieldClause_;
+    std::unique_ptr<WhereClause>   havingClause_;
+    std::unique_ptr<GroupClause>   groupClause_;
 };
 
 class GetSubgraphSentence final : public Sentence {
diff --git a/src/parser/parser.yy b/src/parser/parser.yy
index 814adbdb..6f1c2833 100644
--- a/src/parser/parser.yy
+++ b/src/parser/parser.yy
@@ -24,6 +24,8 @@
 #include "common/expression/CaseExpression.h"
 #include "common/expression/TextSearchExpression.h"
 #include "common/expression/ListComprehensionExpression.h"
+#include "common/expression/AggregateExpression.h"
+
 #include "util/SchemaUtil.h"
 #include "util/ParserUtil.h"
 #include "context/QueryContext.h"
@@ -213,6 +215,7 @@ static constexpr size_t MAX_ABS_INTEGER = 9223372036854775808ULL;
 %type <expr> case_expression
 %type <expr> list_comprehension_expression
 %type <expr> compound_expression
+%type <expr> aggregate_expression
 %type <expr> text_search_expression
 %type <argument_list> argument_list opt_argument_list
 %type <type> type_spec
@@ -490,7 +493,6 @@ unreserved_keyword
 
 agg_function
     : KW_COUNT              { $$ = new std::string("COUNT"); }
-    | KW_COUNT_DISTINCT     { $$ = new std::string("COUNT_DISTINCT"); }
     | KW_SUM                { $$ = new std::string("SUM"); }
     | KW_AVG                { $$ = new std::string("AVG"); }
     | KW_MAX                { $$ = new std::string("MAX"); }
@@ -527,6 +529,9 @@ expression
     | compound_expression {
         $$ = $1;
     }
+    | aggregate_expression {
+        $$ = $1;
+    }
     | MINUS {
         scanner.setUnaryMinus(true);
     } expression %prec UNARY_MINUS {
@@ -834,6 +839,23 @@ function_call_expression
     }
     ;
 
+aggregate_expression
+    : agg_function L_PAREN expression R_PAREN {
+        $$ = new AggregateExpression($1, $3, false/*distinct*/);
+    }
+    | agg_function L_PAREN KW_DISTINCT expression R_PAREN {
+        $$ = new AggregateExpression($1, $4, true/*distinct*/);
+    }
+    | agg_function L_PAREN STAR R_PAREN {
+        auto star = new ConstantExpression(std::string("*"));
+        $$ = new AggregateExpression($1, star, false/*distinct*/);
+    }
+    | agg_function L_PAREN KW_DISTINCT STAR R_PAREN {
+        auto star = new ConstantExpression(std::string("*"));
+        $$ = new AggregateExpression($1, star, true/*distinct*/);
+    }
+    ;
+
 uuid_expression
     : KW_UUID L_PAREN STRING R_PAREN {
         $$ = new UUIDExpression($3);
@@ -1148,28 +1170,6 @@ yield_column
     | expression KW_AS name_label {
         $$ = new YieldColumn($1, $3);
     }
-    | agg_function L_PAREN expression R_PAREN {
-        auto yield = new YieldColumn($3);
-        yield->setAggFunction($1);
-        $$ = yield;
-    }
-    | agg_function L_PAREN expression R_PAREN KW_AS name_label {
-        auto yield = new YieldColumn($3, $6);
-        yield->setAggFunction($1);
-        $$ = yield;
-    }
-    | agg_function L_PAREN STAR R_PAREN {
-        auto expr = new ConstantExpression(std::string("*"));
-        auto yield = new YieldColumn(expr);
-        yield->setAggFunction($1);
-        $$ = yield;
-    }
-    | agg_function L_PAREN STAR R_PAREN KW_AS name_label {
-        auto expr = new ConstantExpression(std::string("*"));
-        auto yield = new YieldColumn(expr, $6);
-        yield->setAggFunction($1);
-        $$ = yield;
-    }
     ;
 
 group_clause
diff --git a/src/parser/test/CMakeLists.txt b/src/parser/test/CMakeLists.txt
index 7d64edd4..7eba4edd 100644
--- a/src/parser/test/CMakeLists.txt
+++ b/src/parser/test/CMakeLists.txt
@@ -17,7 +17,6 @@ set(PARSER_TEST_LIBS
     $<TARGET_OBJECTS:common_datatypes_obj>
     $<TARGET_OBJECTS:common_base_obj>
     $<TARGET_OBJECTS:common_function_manager_obj>
-    $<TARGET_OBJECTS:common_agg_function_obj>
     $<TARGET_OBJECTS:common_meta_thrift_obj>
     $<TARGET_OBJECTS:common_graph_thrift_obj>
     $<TARGET_OBJECTS:common_http_client_obj>
diff --git a/src/parser/test/ParserTest.cpp b/src/parser/test/ParserTest.cpp
index 49da92d4..5fd931e9 100644
--- a/src/parser/test/ParserTest.cpp
+++ b/src/parser/test/ParserTest.cpp
@@ -2061,15 +2061,67 @@ TEST(Parser, GroupBy) {
     {
         GQLParser parser;
         std::string query = "GO FROM \"1\" OVER work "
-                            "YIELD $$.company.name, $^.person.name "
-                            "| GROUP BY $$.company.name "
-                            "YIELD $$.company.name as name, "
+                            "YIELD $$.company.name, $^.person.name AS name "
+                            "| GROUP BY $$.company.name , abs($$.company.age+1)"
+                            "YIELD $$.company.name AS name, "
+                            "COUNT($^.person.name ), "
+                            "SUM($^.person.name ), "
+                            "AVG($^.person.name ), "
+                            "(INT)abs($$.company.age+1), "
+                            "(INT)COUNT(DISTINCT $^.person.name ), "
+                            "COUNT(DISTINCT $-.name )+1, "
+                            "abs(SUM(DISTINCT $$.person.name )), "
+                            "AVG(DISTINCT $^.person.name ), "
+                            "AVG(DISTINCT $-.name ), "
+                            "COUNT(*), "
+                            "COUNT(DISTINCT *), "
+                            "MAX($^.person.name ), "
+                            "MIN($$.person.name ), "
+                            "STD($^.person.name ), "
+                            "BIT_AND($^.person.name ), "
+                            "BIT_OR($$.person.name ), "
+                            "BIT_XOR($^.person.name ),"
+                            "STD(DISTINCT $^.person.name ), "
+                            "BIT_AND(DISTINCT $$.person.name ), "
+                            "BIT_OR(DISTINCT $^.person.name ), "
+                            "BIT_XOR(DISTINCT $$.person.name ),"
+                            "BIT_XOR(DISTINCT $-.name ),"
+                            "F_STD($^.person.name ), "
+                            "F_BIT_AND($^.person.name ), "
+                            "F_BIT_OR($^.person.name ), "
+                            "F_BIT_XOR($^.person.name )";
+
+        auto result = parser.parse(query);
+        ASSERT_TRUE(result.ok()) << result.status();
+    }
+    {
+        GQLParser parser;
+        std::string query = "GO FROM \"1\" OVER work "
+                            "YIELD $$.company.name, $^.person.name AS name "
+                            "| YIELD $$.company.name AS name, "
+                            " abs($$.company.age+1), "
                             "COUNT($^.person.name ), "
-                            "COUNT_DISTINCT($^.person.name ), "
                             "SUM($^.person.name ), "
                             "AVG($^.person.name ), "
+                            "(INT)abs($$.company.age+1), "
+                            "(INT)COUNT(DISTINCT $^.person.name ), "
+                            "COUNT(DISTINCT $-.name )+1, "
+                            "abs(SUM(DISTINCT $$.person.name )), "
+                            "AVG(DISTINCT $^.person.name ), "
+                            "AVG(DISTINCT $-.name ), "
+                            "COUNT(*), "
+                            "COUNT(DISTINCT *), "
                             "MAX($^.person.name ), "
-                            "MIN($^.person.name ), "
+                            "MIN($$.person.name ), "
+                            "STD($^.person.name ), "
+                            "BIT_AND($^.person.name ), "
+                            "BIT_OR($$.person.name ), "
+                            "BIT_XOR($^.person.name ),"
+                            "STD(DISTINCT $^.person.name ), "
+                            "BIT_AND(DISTINCT $$.person.name ), "
+                            "BIT_OR(DISTINCT $^.person.name ), "
+                            "BIT_XOR(DISTINCT $$.person.name ),"
+                            "BIT_XOR(DISTINCT $-.name ),"
                             "F_STD($^.person.name ), "
                             "F_BIT_AND($^.person.name ), "
                             "F_BIT_OR($^.person.name ), "
diff --git a/src/planner/Query.cpp b/src/planner/Query.cpp
index c9411c7f..713d752d 100644
--- a/src/planner/Query.cpp
+++ b/src/planner/Query.cpp
@@ -200,11 +200,9 @@ std::unique_ptr<PlanNodeDescription> Aggregate::explain() const {
     auto desc = SingleInputNode::explain();
     addDescription("groupKeys", folly::toJson(util::toJson(groupKeys_)), desc.get());
     folly::dynamic itemArr = folly::dynamic::array();
-    for (const auto& item : groupItems_) {
+    for (auto* item : groupItems_) {
         folly::dynamic itemObj = folly::dynamic::object();
-        itemObj.insert("distinct", util::toJson(item.distinct));
-        itemObj.insert("funcType", static_cast<uint8_t>(item.func));
-        itemObj.insert("expr", item.expr ? item.expr->toString() : "");
+        itemObj.insert("expr", item? item->toString() : "");
         itemArr.push_back(itemObj);
     }
     addDescription("groupItems", folly::toJson(itemArr), desc.get());
diff --git a/src/planner/Query.h b/src/planner/Query.h
index 223ea642..ab6751c0 100644
--- a/src/planner/Query.h
+++ b/src/planner/Query.h
@@ -8,7 +8,7 @@
 #define PLANNER_QUERY_H_
 
 #include "common/base/Base.h"
-#include "common/function/AggregateFunction.h"
+#include "common/expression/AggregateExpression.h"
 #include "common/interface/gen-cpp2/storage_types.h"
 #include "context/QueryContext.h"
 #include "parser/Clauses.h"
@@ -804,18 +804,10 @@ private:
  */
 class Aggregate final : public SingleInputNode {
 public:
-    struct GroupItem {
-        GroupItem(Expression* e, AggFun::Function f, bool d)
-            : expr(e), func(f), distinct(d) {}
-        Expression* expr;
-        AggFun::Function func;
-        bool distinct = false;
-    };
-
     static Aggregate* make(QueryContext* qctx,
                            PlanNode* input,
                            std::vector<Expression*>&& groupKeys,
-                           std::vector<GroupItem>&& groupItems) {
+                           std::vector<Expression*>&& groupItems) {
         return qctx->objPool()->add(
             new Aggregate(qctx, input, std::move(groupKeys), std::move(groupItems)));
     }
@@ -824,7 +816,7 @@ public:
         return groupKeys_;
     }
 
-    const std::vector<GroupItem>& groupItems() const {
+    const std::vector<Expression*>& groupItems() const {
         return groupItems_;
     }
 
@@ -834,7 +826,7 @@ private:
     Aggregate(QueryContext* qctx,
               PlanNode* input,
               std::vector<Expression*>&& groupKeys,
-              std::vector<GroupItem>&& groupItems)
+              std::vector<Expression*>&& groupItems)
         : SingleInputNode(qctx, Kind::kAggregate, input) {
         groupKeys_ = std::move(groupKeys);
         groupItems_ = std::move(groupItems);
@@ -842,7 +834,7 @@ private:
 
 private:
     std::vector<Expression*>    groupKeys_;
-    std::vector<GroupItem>      groupItems_;
+    std::vector<Expression*>    groupItems_;
 };
 
 class SwitchSpace final : public SingleInputNode {
diff --git a/src/planner/match/StartVidFinder.cpp b/src/planner/match/StartVidFinder.cpp
index a8db5e6d..eeba83d0 100644
--- a/src/planner/match/StartVidFinder.cpp
+++ b/src/planner/match/StartVidFinder.cpp
@@ -33,7 +33,7 @@ StatusOr<SubPlan> StartVidFinder::transform(PatternContext* patternCtx) {
         return transformEdge(edgeCtx);
     }
 
-    return Status::Error("Unkown pattern kind.");
+    return Status::Error("Unknown pattern kind.");
 }
 
 }  // namespace graph
diff --git a/src/util/ExpressionUtils.h b/src/util/ExpressionUtils.h
index 9e549863..2523024c 100644
--- a/src/util/ExpressionUtils.h
+++ b/src/util/ExpressionUtils.h
@@ -10,6 +10,7 @@
 #include "common/expression/BinaryExpression.h"
 #include "common/expression/Expression.h"
 #include "common/expression/FunctionCallExpression.h"
+#include "common/expression/AggregateExpression.h"
 #include "common/expression/LabelExpression.h"
 #include "common/expression/PropertyExpression.h"
 #include "common/expression/TypeCastingExpression.h"
diff --git a/src/util/test/CMakeLists.txt b/src/util/test/CMakeLists.txt
index 98ae1729..d71c6c17 100644
--- a/src/util/test/CMakeLists.txt
+++ b/src/util/test/CMakeLists.txt
@@ -26,7 +26,6 @@ nebula_add_test(
         $<TARGET_OBJECTS:common_encryption_obj>
         $<TARGET_OBJECTS:common_http_client_obj>
         $<TARGET_OBJECTS:common_process_obj>
-        $<TARGET_OBJECTS:common_agg_function_obj>
         $<TARGET_OBJECTS:common_time_utils_obj>
         $<TARGET_OBJECTS:common_graph_obj>
         $<TARGET_OBJECTS:common_ft_es_graph_adapter_obj>
diff --git a/src/validator/GetSubgraphValidator.cpp b/src/validator/GetSubgraphValidator.cpp
index ea176d82..d45746db 100644
--- a/src/validator/GetSubgraphValidator.cpp
+++ b/src/validator/GetSubgraphValidator.cpp
@@ -225,15 +225,14 @@ Status GetSubgraphValidator::zeroStep(PlanNode* depend, const std::string& input
     getVertex->setInputVar(inputVar);
 
     auto var = vctx_->anonVarGen()->getVar();
-    auto* column = new YieldColumn(new VertexExpression(), new std::string("_vertices"));
-    qctx_->objPool()->add(column);
-    column->setAggFunction(new std::string("COLLECT"));
-    auto fun = column->getAggFunName();
+    auto* column = new VertexExpression();
+    auto* func = new AggregateExpression(new std::string("COLLECT"), column, false);
+    qctx_->objPool()->add(func);
     auto* collectVertex =
         Aggregate::make(qctx_,
                         getVertex,
                         {},
-                        {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], false)});
+                        {func});
     collectVertex->setInputVar(getVertex->outputVar());
     collectVertex->setColNames({"_vertices"});
 
@@ -259,17 +258,14 @@ Status GetSubgraphValidator::toPlan() {
         startVidsVar = dedupStartVid->outputVar();
         // collect runtime startVids
         auto var = vctx_->anonVarGen()->getVar();
-        auto* column = new YieldColumn(
-            new VariablePropertyExpression(new std::string(var), new std::string(kVid)),
-            new std::string(kVid));
-        qctx_->objPool()->add(column);
-        column->setAggFunction(new std::string("COLLECT_SET"));
-        auto fun = column->getAggFunName();
+        auto* column = new VariablePropertyExpression(new std::string(var), new std::string(kVid));
+        auto* func = new AggregateExpression(new std::string("COLLECT_SET"), column, true);
+        qctx_->objPool()->add(func);
         collectRunTimeStartVids =
             Aggregate::make(qctx_,
                             dedupStartVid,
                             {},
-                            {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], true)});
+                            {func});
         collectRunTimeStartVids->setInputVar(dedupStartVid->outputVar());
         collectRunTimeStartVids->setColNames({kVid});
         runtimeStartVar_ = collectRunTimeStartVids->outputVar();
@@ -298,17 +294,14 @@ Status GetSubgraphValidator::toPlan() {
     auto* projectVids = projectDstVidsFromGN(gn, startVidsVar);
 
     auto var = vctx_->anonVarGen()->getVar();
-    auto* column =
-        new YieldColumn(new VariablePropertyExpression(new std::string(var), new std::string(kVid)),
-                        new std::string(kVid));
-    qctx_->objPool()->add(column);
-    column->setAggFunction(new std::string("COLLECT_SET"));
-    auto fun = column->getAggFunName();
+    auto* column = new VariablePropertyExpression(new std::string(var), new std::string(kVid));
+    auto* func = new AggregateExpression(new std::string("COLLECT_SET"), column, true);
+    qctx_->objPool()->add(func);
     auto* collect =
         Aggregate::make(qctx_,
                         projectVids,
                         {},
-                        {Aggregate::GroupItem(column->expr(), AggFun::nameIdMap_[fun], true)});
+                        {func});
     collect->setInputVar(projectVids->outputVar());
     collect->setColNames({kVid});
     collectVar_ = collect->outputVar();
diff --git a/src/validator/GoValidator.cpp b/src/validator/GoValidator.cpp
index 6f241a89..ae0ffdc6 100644
--- a/src/validator/GoValidator.cpp
+++ b/src/validator/GoValidator.cpp
@@ -51,7 +51,11 @@ Status GoValidator::validateWhere(WhereClause* where) {
     }
 
     filter_ = where->filter();
-
+    if (graph::ExpressionUtils::findAny(filter_, {Expression::Kind::kAggregate})) {
+        return Status::SemanticError(
+            "`%s', not support aggregate function in where sentence.",
+            filter_->toString().c_str());
+    }
     if (filter_->kind() == Expression::Kind::kLabelAttribute) {
         auto laExpr = static_cast<LabelAttributeExpression*>(filter_);
         where->setFilter(ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(laExpr));
@@ -107,8 +111,7 @@ Status GoValidator::validateYield(YieldClause* yield) {
             } else {
                 ExpressionUtils::rewriteLabelAttribute<EdgePropertyExpression>(col->expr());
             }
-
-            if (!col->getAggFunName().empty()) {
+            if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})) {
                 return Status::SemanticError(
                     "`%s', not support aggregate function in go sentence.",
                     col->toString().c_str());
diff --git a/src/validator/GroupByValidator.cpp b/src/validator/GroupByValidator.cpp
index 9885a465..b5928116 100644
--- a/src/validator/GroupByValidator.cpp
+++ b/src/validator/GroupByValidator.cpp
@@ -6,6 +6,11 @@
 
 #include "validator/GroupByValidator.h"
 #include "planner/Query.h"
+#include "util/ExpressionUtils.h"
+#include "util/AnonColGenerator.h"
+#include "util/AnonVarGenerator.h"
+#include "visitor/RewriteAggExprVisitor.h"
+#include "visitor/FindAnySubExprVisitor.h"
 
 
 namespace nebula {
@@ -15,13 +20,8 @@ Status GroupByValidator::validateImpl() {
     auto *groupBySentence = static_cast<GroupBySentence*>(sentence_);
     NG_RETURN_IF_ERROR(validateGroup(groupBySentence->groupClause()));
     NG_RETURN_IF_ERROR(validateYield(groupBySentence->yieldClause()));
+    NG_RETURN_IF_ERROR(groupClauseSemanticCheck());
 
-    if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty()) {
-        return Status::SemanticError("Only support input and variable in GroupBy sentence.");
-    }
-    if (!exprProps_.inputProps().empty() && !exprProps_.varProps().empty()) {
-        return Status::SemanticError("Not support both input and variable in GroupBy sentence.");
-    }
     return Status::OK();
 }
 
@@ -34,80 +34,87 @@ Status GroupByValidator::validateYield(const YieldClause *yieldClause) {
         return Status::SemanticError("Yield cols is Empty");
     }
 
+    projCols_ = qctx_->objPool()->add(new YieldColumns);
     for (auto* col : columns) {
-        auto fun = col->getAggFunName();
-        if (!fun.empty()) {
-            auto iter = AggFun::nameIdMap_.find(fun);
-            if (iter == AggFun::nameIdMap_.end()) {
-                return Status::SemanticError("Unkown aggregate function `%s`", fun.c_str());
-            }
-            if (iter->second != AggFun::Function::kCount && col->expr()->toString() == "*") {
-                return Status::SemanticError("`%s` invaild, * valid in count.",
-                                             col->toString().c_str());
-            }
+        auto rewrited = false;
+        auto colOldName = deduceColName(col);
+        if (col->expr()->kind() != Expression::Kind::kAggregate) {
+            NG_RETURN_IF_ERROR(rewriteInnerAggExpr(col, rewrited));
+        }
+        auto colExpr = col->expr();
+        // collect exprs for check later
+        if (colExpr->kind() == Expression::Kind::kAggregate) {
+            auto* aggExpr = static_cast<AggregateExpression*>(colExpr);
+            NG_RETURN_IF_ERROR(checkAggExpr(aggExpr));
+            yieldAggs_.emplace_back(colExpr);
+        } else {
+            yieldCols_.emplace_back(colExpr);
         }
 
-        // todo(jmq) count(distinct)
-        groupItems_.emplace_back(Aggregate::GroupItem{col->expr(), AggFun::nameIdMap_[fun], false});
-
-        auto status = deduceExprType(col->expr());
+        groupItems_.emplace_back(colExpr);
+        auto status = deduceExprType(colExpr);
         NG_RETURN_IF_ERROR(status);
         auto type = std::move(status).value();
-        auto name = deduceColName(col);
+        std::string name;
+        if (!rewrited) {
+            name = deduceColName(col);
+            projCols_->addColumn(new YieldColumn(
+                new VariablePropertyExpression(new std::string(),
+                                           new std::string(name)),
+                new std::string(colOldName)));
+        } else {
+            name = colExpr->toString();
+        }
+        projOutputColumnNames_.emplace_back(colOldName);
         outputs_.emplace_back(name, type);
-        outputColumnNames_.emplace_back(std::move(name));
-        // todo(jmq) extend $-.*
+        outputColumnNames_.emplace_back(name);
 
-        yieldCols_.emplace_back(col);
-        if (col->alias() != nullptr) {
+        if (col->alias() != nullptr && !rewrited) {
             aliases_.emplace(*col->alias(), col);
         }
 
-        // check input yield filed without agg function and not in group cols
         ExpressionProps yieldProps;
-        NG_RETURN_IF_ERROR(deduceProps(col->expr(), yieldProps));
-        if (col->getAggFunName().empty()) {
-            if (!yieldProps.inputProps().empty()) {
-                if (!exprProps_.isSubsetOfInput(yieldProps.inputProps())) {
-                    return Status::SemanticError("Yield `%s` isn't in output fields",
-                                                 col->toString().c_str());
-                }
-            } else if (!yieldProps.varProps().empty()) {
-                if (!exprProps_.isSubsetOfVar(yieldProps.varProps())) {
-                    return Status::SemanticError("Yield `%s` isn't in output fields",
-                                                 col->toString().c_str());
-                }
-            }
-        }
+        NG_RETURN_IF_ERROR(deduceProps(colExpr, yieldProps));
         exprProps_.unionProps(std::move(yieldProps));
     }
+
     return Status::OK();
 }
 
-
 Status GroupByValidator::validateGroup(const GroupClause *groupClause) {
+    if (!groupClause) return Status::OK();
     std::vector<YieldColumn*> columns;
     if (groupClause != nullptr) {
         columns = groupClause->columns();
     }
 
-    if (columns.empty()) {
-        return Status::SemanticError("Group cols is Empty");
-    }
+    auto groupByValid = [](Expression::Kind kind)->bool {
+         return std::unordered_set<Expression::Kind>{
+                Expression::Kind::kAdd ,
+                Expression::Kind::kMinus,
+                Expression::Kind::kMultiply,
+                Expression::Kind::kDivision,
+                Expression::Kind::kMod,
+                Expression::Kind::kTypeCasting,
+                Expression::Kind::kFunctionCall,
+                Expression::Kind::kInputProperty,
+                Expression::Kind::kVarProperty,
+                Expression::Kind::kCase }
+                .count(kind);};
     for (auto* col : columns) {
-        if (col->expr()->kind() != Expression::Kind::kInputProperty &&
-            col->expr()->kind() != Expression::Kind::kVarProperty &&
-            col->expr()->kind() != Expression::Kind::kFunctionCall) {
-            return Status::SemanticError("Group `%s` invalid", col->expr()->toString().c_str());
+        if (graph::ExpressionUtils::findAny(col->expr(), {Expression::Kind::kAggregate})
+            || !graph::ExpressionUtils::findAny(col->expr(),
+                                               {Expression::Kind::kInputProperty,
+                                                Expression::Kind::kVarProperty})) {
+            return Status::SemanticError("Group `%s' invalid", col->expr()->toString().c_str());
         }
-        if (!col->getAggFunName().empty()) {
-            return Status::SemanticError("Use invalid group function `%s`",
-                                         col->getAggFunName().c_str());
+        if (!groupByValid(col->expr()->kind())) {
+            return Status::SemanticError("Group `%s' invalid", col->expr()->toString().c_str());
         }
+
         NG_RETURN_IF_ERROR(deduceExprType(col->expr()));
         NG_RETURN_IF_ERROR(deduceProps(col->expr(), exprProps_));
 
-        groupCols_.emplace_back(col);
         groupKeys_.emplace_back(col->expr());
     }
     return Status::OK();
@@ -116,10 +123,117 @@ Status GroupByValidator::validateGroup(const GroupClause *groupClause) {
 Status GroupByValidator::toPlan() {
     auto *groupBy = Aggregate::make(qctx_, nullptr, std::move(groupKeys_), std::move(groupItems_));
     groupBy->setColNames(std::vector<std::string>(outputColumnNames_));
-    root_ = groupBy;
+    if (needGenProject_) {
+        // rewrite Expr which has inner aggExpr and push it up to Project.
+        auto *project = Project::make(qctx_, groupBy, projCols_);
+        project->setInputVar(groupBy->outputVar());
+        project->setColNames(projOutputColumnNames_);
+        root_ = project;
+    } else {
+        root_ = groupBy;
+    }
     tail_ = groupBy;
     return Status::OK();
 }
 
+Status GroupByValidator::groupClauseSemanticCheck() {
+    // check exprProps
+    if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty()) {
+        return Status::SemanticError("Only support input and variable in GroupBy sentence.");
+    }
+    if (!exprProps_.inputProps().empty() && !exprProps_.varProps().empty()) {
+        return Status::SemanticError("Not support both input and variable in GroupBy sentence.");
+    }
+    if (!exprProps_.varProps().empty() && exprProps_.varProps().size() > 1) {
+        return Status::SemanticError("Only one variable allowed to use.");
+    }
+
+    if (groupKeys_.empty()) {
+        groupKeys_ = yieldCols_;
+    } else {
+        std::unordered_set<Expression*> groupSet(groupKeys_.begin(), groupKeys_.end());
+        FindAnySubExprVisitor groupVisitor(groupSet, true);
+        for (auto* expr : yieldCols_) {
+            if (evaluableExpr(expr)) {
+                continue;
+            }
+            expr->accept(&groupVisitor);
+            if (!groupVisitor.found()) {
+                return Status::SemanticError("Yield non-agg expression `%s' must be"
+                " functionally dependent on items in GROUP BY clause", expr->toString().c_str());
+            }
+        }
+    }
+    return Status::OK();
+}
+
+Status GroupByValidator::rewriteInnerAggExpr(YieldColumn* col, bool& rewrited) {
+    auto colOldName = deduceColName(col);
+    auto* colExpr = col->expr();
+    // agg col need not rewrite
+    DCHECK(colExpr->kind() != Expression::Kind::kAggregate);
+    auto collectAggCol = colExpr->clone();
+    auto aggs = ExpressionUtils::collectAll(collectAggCol.get(),
+                                            {Expression::Kind::kAggregate});
+    if (aggs.size() > 1) {
+        return Status::SemanticError("Aggregate function nesting is not allowed: `%s'",
+                                     collectAggCol->toString().c_str());
+    }
+    if (aggs.size() == 1) {
+        auto colRewrited = colExpr->clone();
+        // set aggExpr
+        col->setExpr(aggs[0]->clone().release());
+        auto aggColName = col->expr()->toString();
+        // rewrite to VariablePropertyExpression
+        RewriteAggExprVisitor rewriteAggVisitor(new std::string(),
+                                                new std::string(aggColName));
+        colRewrited->accept(&rewriteAggVisitor);
+        rewrited = true;
+        needGenProject_ = true;
+        projCols_->addColumn(new YieldColumn(colRewrited.release(),
+                             new std::string(colOldName)));
+    }
+
+    return Status::OK();
+}
+
+Status GroupByValidator::checkAggExpr(AggregateExpression* aggExpr) {
+    auto func = aggExpr->name();
+    if (!func) {
+        return Status::SemanticError("`%s' aggregate function not set.",
+                                     aggExpr->toString().c_str());
+    }
+
+    auto iter = AggregateExpression::NAME_ID_MAP.find(func->c_str());
+    if (iter == AggregateExpression::NAME_ID_MAP.end()) {
+        return Status::SemanticError("Unknown aggregate function `%s'", func->c_str());
+    }
+
+    auto* aggArg = aggExpr->arg();
+    if (graph::ExpressionUtils::findAny(aggArg,
+                                        {Expression::Kind::kAggregate})) {
+        return Status::SemanticError("Aggregate function nesting is not allowed: `%s'",
+                                     aggExpr->toString().c_str());
+    }
+
+    if (iter->second != AggregateExpression::Function::kCount) {
+        if (aggArg->toString() == "*") {
+            return Status::SemanticError("Could not apply aggregation function `%s' on `*`",
+                                         aggExpr->toString().c_str());
+        }
+        if (aggArg->kind() == Expression::Kind::kInputProperty
+            || aggArg->kind() == Expression::Kind::kVarProperty) {
+            auto propExpr = static_cast<PropertyExpression*>(aggArg);
+            if (*propExpr->prop() == "*") {
+                return Status::SemanticError(
+                    "Could not apply aggregation function `%s' on `%s'",
+                    aggExpr->toString().c_str(), propExpr->toString().c_str());
+            }
+        }
+    }
+
+    return Status::OK();
+}
+
 }  // namespace graph
 }  // namespace nebula
diff --git a/src/validator/GroupByValidator.h b/src/validator/GroupByValidator.h
index 56d5cf35..c8ac22c0 100644
--- a/src/validator/GroupByValidator.h
+++ b/src/validator/GroupByValidator.h
@@ -20,26 +20,35 @@ public:
     GroupByValidator(Sentence *sentence, QueryContext *context)
         : Validator(sentence, context) {}
 
-private:
     Status validateImpl() override;
 
     Status toPlan() override;
 
+private:
     Status validateGroup(const GroupClause *groupClause);
 
     Status validateYield(const YieldClause *yieldClause);
 
+    Status groupClauseSemanticCheck();
+    Status rewriteInnerAggExpr(YieldColumn* col, bool& rewrited);
+    Status checkAggExpr(AggregateExpression* aggExpr);
+
 private:
-    std::vector<YieldColumn*>                         groupCols_;
-    std::vector<YieldColumn*>                         yieldCols_;
+    std::vector<Expression*>                         yieldCols_;
+    std::vector<Expression*>                         yieldAggs_;
 
     // key: alias, value: input name
     std::unordered_map<std::string, YieldColumn*>     aliases_;
 
+    bool                                              needGenProject_{false};
     std::vector<std::string>                          outputColumnNames_;
+    std::vector<std::string>                          projOutputColumnNames_;
+
+    // used to generate Project node when there is an internally nested aggregateExpression
+    YieldColumns*                                     projCols_;
 
     std::vector<Expression*>                          groupKeys_;
-    std::vector<Aggregate::GroupItem>                 groupItems_;
+    std::vector<Expression*>                          groupItems_;
 };
 
 
diff --git a/src/validator/Validator.h b/src/validator/Validator.h
index c14a759c..c3ac76f1 100644
--- a/src/validator/Validator.h
+++ b/src/validator/Validator.h
@@ -42,10 +42,6 @@ public:
         inputVarName_ = std::move(name);
     }
 
-    void setInputCols(ColsDef&& inputs) {
-        inputs_ = std::move(inputs);
-    }
-
     QueryContext* qctx() {
         return qctx_;
     }
@@ -62,10 +58,50 @@ public:
         return outputs_;
     }
 
+    void setOutputCols(const ColsDef&& outputCols) {
+        outputs_ = std::move(outputCols);
+    }
+
+    void setOutputCols(ColsDef& outputCols) {
+        outputs_ = outputCols;
+    }
+
     ColsDef inputCols() const {
         return inputs_;
     }
 
+    void setInputCols(ColsDef&& inputCols) {
+        inputs_ = std::move(inputCols);
+    }
+
+    void setInputCols(const ColsDef& inputCols) {
+        inputs_ = inputCols;
+    }
+
+    ExpressionProps exprProps() const {
+        return exprProps_;
+    }
+
+    void setExprProps(ExpressionProps&& exprProps) {
+        exprProps_ = std::move(exprProps);
+    }
+
+    void setExprProps(const ExpressionProps& exprProps) {
+        exprProps_ = exprProps;
+    }
+
+    const std::set<std::string>&  userDefinedVarNameList() const {
+        return userDefinedVarNameList_;
+    }
+
+    void setUserDefinedVarNameList(std::set<std::string>&& userDefinedVarNameList) {
+        userDefinedVarNameList_ = std::move(userDefinedVarNameList);
+    }
+
+    void setUserDefinedVarNameList(const std::set<std::string>& userDefinedVarNameList) {
+        userDefinedVarNameList_ = userDefinedVarNameList;
+    }
+
     void setNoSpaceRequired() {
         noSpaceRequired_ = true;
     }
@@ -141,19 +177,19 @@ protected:
     Sentence*                       sentence_{nullptr};
     QueryContext*                   qctx_{nullptr};
     ValidateContext*                vctx_{nullptr};
-    // The input columns and output columns of a sentence.
-    ColsDef                         outputs_;
-    ColsDef                         inputs_;
     // The variable name of the input node.
     std::string                     inputVarName_;
     // Admin sentences do not requires a space to be chosen.
     bool                            noSpaceRequired_{false};
 
+    // The input columns and output columns of a sentence.
+    ColsDef                         outputs_;
+    ColsDef                         inputs_;
+    ExpressionProps                 exprProps_;
+
     // root and tail of a subplan.
     PlanNode*                       root_{nullptr};
     PlanNode*                       tail_{nullptr};
-
-    ExpressionProps                 exprProps_;
     // user define Variable name list
     std::set<std::string>           userDefinedVarNameList_;
     // vid's Type
diff --git a/src/validator/YieldValidator.cpp b/src/validator/YieldValidator.cpp
index 8d175de3..4de8f571 100644
--- a/src/validator/YieldValidator.cpp
+++ b/src/validator/YieldValidator.cpp
@@ -28,8 +28,15 @@ Status YieldValidator::validateImpl() {
     }
 
     auto yield = static_cast<YieldSentence *>(sentence_);
-    NG_RETURN_IF_ERROR(validateYieldAndBuildOutputs(yield->yield()));
+    if (yield->hasAgg()) {
+        NG_RETURN_IF_ERROR(makeImplicitGroupByValidator());
+    }
     NG_RETURN_IF_ERROR(validateWhere(yield->where()));
+    if (groupByValidator_) {
+        NG_RETURN_IF_ERROR(validateImplicitGroupBy());
+    } else {
+        NG_RETURN_IF_ERROR(validateYieldAndBuildOutputs(yield->yield()));
+    }
 
     if (!exprProps_.srcTagProps().empty() || !exprProps_.dstTagProps().empty() ||
         !exprProps_.edgeProps().empty()) {
@@ -44,39 +51,23 @@ Status YieldValidator::validateImpl() {
         return Status::SemanticError("Only one variable allowed to use.");
     }
 
-    if (hasAggFun_) {
-        NG_RETURN_IF_ERROR(checkAggFunAndBuildGroupItems(yield->yield()));
-    }
-
-    if (exprProps_.inputProps().empty() && exprProps_.varProps().empty() && inputVarName_.empty()) {
+    if (!groupByValidator_ && exprProps_.inputProps().empty()
+        && exprProps_.varProps().empty() && inputVarName_.empty()) {
         // generate constant expression result into querycontext
         genConstantExprValues();
     }
 
     if (!exprProps_.varProps().empty() && !userDefinedVarNameList_.empty()) {
+        // TODO: Support Multiple userDefinedVars
+        if (userDefinedVarNameList_.size() != 1) {
+            return Status::SemanticError("Multiple user defined vars not supported yet.");
+        }
         userDefinedVarName_ = *userDefinedVarNameList_.begin();
     }
 
     return Status::OK();
 }
 
-Status YieldValidator::checkAggFunAndBuildGroupItems(const YieldClause *clause) {
-    auto yield = clause->yields();
-    for (auto column : yield->columns()) {
-        auto expr = column->expr();
-        auto fun = column->getAggFunName();
-        if (!evaluableExpr(expr) && fun.empty()) {
-            return Status::SemanticError(
-                "Input columns without aggregation are not supported in YIELD statement "
-                "without GROUP BY, near `%s'",
-                expr->toString().c_str());
-        }
-
-        groupItems_.emplace_back(Aggregate::GroupItem{expr, AggFun::nameIdMap_[fun], false});
-    }
-    return Status::OK();
-}
-
 Status YieldValidator::makeOutputColumn(YieldColumn *column) {
     columns_->addColumn(column);
 
@@ -117,6 +108,30 @@ void YieldValidator::genConstantExprValues() {
                              ResultBuilder().value(Value(std::move(ds))).finish());
 }
 
+Status YieldValidator::makeImplicitGroupByValidator() {
+    auto* groupSentence = qctx()->objPool()->add(
+            new GroupBySentence(
+                static_cast<YieldSentence *>(sentence_)->yield()->clone().release(),
+                nullptr, nullptr));
+    groupByValidator_ = std::make_unique<GroupByValidator>(groupSentence, qctx());
+    groupByValidator_->setInputCols(inputs_);
+
+    return Status::OK();
+}
+
+Status YieldValidator::validateImplicitGroupBy() {
+    NG_RETURN_IF_ERROR(groupByValidator_->validateImpl());
+    inputs_ = groupByValidator_->inputCols();
+    outputs_ = groupByValidator_->outputCols();
+    exprProps_.unionProps(groupByValidator_->exprProps());
+
+    const auto& groupVars = groupByValidator_->userDefinedVarNameList();
+    // TODO: Support Multiple userDefinedVars
+    userDefinedVarNameList_.insert(groupVars.begin(), groupVars.end());
+
+    return Status::OK();
+}
+
 Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
     auto columns = clause->columns();
     columns_ = qctx_->objPool()->add(new YieldColumns);
@@ -133,9 +148,6 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
                     auto newExpr = new InputPropertyExpression(new std::string(colDef.name));
                     NG_RETURN_IF_ERROR(makeOutputColumn(new YieldColumn(newExpr)));
                 }
-                if (!column->getAggFunName().empty()) {
-                    return Status::SemanticError("could not apply aggregation function on `$-.*'");
-                }
                 continue;
             }
         } else if (expr->kind() == Expression::Kind::kVarProperty) {
@@ -152,23 +164,10 @@ Status YieldValidator::validateYieldAndBuildOutputs(const YieldClause *clause) {
                                                                   new std::string(colDef.name));
                     NG_RETURN_IF_ERROR(makeOutputColumn(new YieldColumn(newExpr)));
                 }
-                if (!column->getAggFunName().empty()) {
-                    return Status::SemanticError("could not apply aggregation function on `$%s.*'",
-                                                 var->c_str());
-                }
                 continue;
             }
         }
 
-        auto fun = column->getAggFunName();
-        if (!fun.empty()) {
-            auto foundAgg = AggFun::nameIdMap_.find(fun);
-            if (foundAgg == AggFun::nameIdMap_.end()) {
-                return Status::SemanticError("Unkown aggregate function: `%s'", fun.c_str());
-            }
-            hasAggFun_ = true;
-        }
-
         NG_RETURN_IF_ERROR(makeOutputColumn(column->clone().release()));
     }
 
@@ -208,28 +207,43 @@ Status YieldValidator::toPlan() {
     }
 
     SingleInputNode *dedupDep = nullptr;
-    if (!hasAggFun_) {
-        dedupDep = Project::make(qctx_, filter, columns_);
-    } else {
-        // We do not use group items later, so move it is safe
-        dedupDep = Aggregate::make(qctx_, filter, {}, std::move(groupItems_));
-    }
-    if (filter == nullptr && !inputVar.empty()) {
-        dedupDep->setInputVar(inputVar);
-    }
 
-    dedupDep->setColNames(std::move(outputColumnNames_));
-    if (filter != nullptr) {
-        dedupDep->setInputVar(filter->outputVar());
-        tail_ = filter;
+    if (groupByValidator_) {
+        groupByValidator_->toPlan();
+        auto* groupByValidatorRoot = groupByValidator_->root();
+        auto* groupByValidatorTail = groupByValidator_->tail();
+        // groupBy validator only gen Project or Aggregate Node
+        DCHECK(groupByValidatorRoot->isSingleInput());
+        DCHECK(groupByValidatorTail->isSingleInput());
+        dedupDep = static_cast<SingleInputNode*>(groupByValidatorRoot);
+        if (filter != nullptr) {
+            if (!inputVar.empty()) {
+                filter->setInputVar(inputVar);
+            }
+            static_cast<SingleInputNode*>(groupByValidatorTail)->dependsOn(filter);
+            static_cast<SingleInputNode*>(groupByValidatorTail)->setInputVar(filter->outputVar());
+            tail_ = filter;
+        } else {
+            tail_ = groupByValidatorTail;
+            if (!inputVar.empty()) {
+                static_cast<SingleInputNode*>(tail_)->setInputVar(inputVar);
+            }
+        }
     } else {
-        tail_ = dedupDep;
+        dedupDep = Project::make(qctx_, filter, columns_);
+        dedupDep->setColNames(std::move(outputColumnNames_));
+        if (filter != nullptr) {
+            dedupDep->setInputVar(filter->outputVar());
+            tail_ = filter;
+        } else {
+            tail_ = dedupDep;
+        }
+        // Otherwise the input of tail_ would be set by pipe.
+        if (!inputVar.empty()) {
+            static_cast<SingleInputNode *>(tail_)->setInputVar(inputVar);
+        }
     }
 
-    // Otherwise the input of tail_ would be set by pipe.
-    if (!inputVar.empty()) {
-        static_cast<SingleInputNode *>(tail_)->setInputVar(inputVar);
-    }
 
     if (yield->yield()->isDistinct()) {
         auto dedup = Dedup::make(qctx_, dedupDep);
diff --git a/src/validator/YieldValidator.h b/src/validator/YieldValidator.h
index f5b50a91..93b7c108 100644
--- a/src/validator/YieldValidator.h
+++ b/src/validator/YieldValidator.h
@@ -12,6 +12,7 @@
 #include "common/base/Status.h"
 #include "planner/Query.h"
 #include "validator/Validator.h"
+#include "validator/GroupByValidator.h"
 
 namespace nebula {
 
@@ -37,18 +38,19 @@ public:
 private:
     Status validateYieldAndBuildOutputs(const YieldClause *clause);
     Status validateWhere(const WhereClause *clause);
-    Status checkAggFunAndBuildGroupItems(const YieldClause *clause);
     Status makeOutputColumn(YieldColumn *column);
+    Status makeImplicitGroupByValidator();
+    Status validateImplicitGroupBy();
     void genConstantExprValues();
 
 private:
-    bool hasAggFun_{false};
     YieldColumns *columns_{nullptr};
     std::vector<std::string> outputColumnNames_;
-    std::vector<Aggregate::GroupItem> groupItems_;
     std::string constantExprVar_;
     std::string userDefinedVarName_;
     Expression *filterCondition_{nullptr};
+    // validate for agg
+    std::unique_ptr<GroupByValidator> groupByValidator_{nullptr};
 };
 
 }   // namespace graph
diff --git a/src/validator/test/CMakeLists.txt b/src/validator/test/CMakeLists.txt
index af6ea318..243bff73 100644
--- a/src/validator/test/CMakeLists.txt
+++ b/src/validator/test/CMakeLists.txt
@@ -41,7 +41,6 @@ set(VALIDATOR_TEST_LIBS
     $<TARGET_OBJECTS:common_meta_client_obj>
     $<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
     $<TARGET_OBJECTS:common_function_manager_obj>
-    $<TARGET_OBJECTS:common_agg_function_obj>
     $<TARGET_OBJECTS:common_conf_obj>
     $<TARGET_OBJECTS:common_encryption_obj>
     $<TARGET_OBJECTS:common_http_client_obj>
diff --git a/src/validator/test/GroupByValidatorTest.cpp b/src/validator/test/GroupByValidatorTest.cpp
index 70023d12..501051d0 100644
--- a/src/validator/test/GroupByValidatorTest.cpp
+++ b/src/validator/test/GroupByValidatorTest.cpp
@@ -21,7 +21,7 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
     {
         std::string query =
             "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
-            "| GROUP BY $-.age YIELD COUNT($-.id)";
+            "| GROUP BY $-.age YIELD $-.age, COUNT($-.id)";
         std::vector<PlanNode::Kind> expected = {
             PK::kAggregate,
             PK::kProject,
@@ -42,6 +42,19 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
         };
         EXPECT_TRUE(checkResult(query, expected));
     }
+    {
+        std::string query =
+            "GO FROM \"NoExist\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+            "| GROUP BY $-.id YIELD $-.id AS id, abs(avg($-.age)) AS age";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kAggregate,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
     {
         std::string query = "GO FROM \"1\", \"2\" OVER like "
                             "YIELD $$.person.name as name, "
@@ -71,6 +84,36 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
         };
         EXPECT_TRUE(checkResult(query, expected));
     }
+    {
+        std::string query = "GO FROM \"1\", \"2\" OVER like "
+                            "YIELD $$.person.name as name, "
+                            "$$.person.age AS dst_age, "
+                            "$$.person.age AS src_age"
+                            "| GROUP BY $-.name "
+                            "YIELD $-.name AS name, "
+                            "SUM($-.dst_age) AS sum_dst_age, "
+                            "abs(AVG($-.dst_age)) AS avg_dst_age, "
+                            "MAX($-.src_age) AS max_src_age, "
+                            "MIN($-.src_age) AS min_src_age, "
+                            "STD($-.src_age) AS std_src_age, "
+                            "BIT_AND(1) AS bit_and, "
+                            "BIT_OR(2) AS bit_or, "
+                            "BIT_XOR(3) AS bit_xor";
+        std::vector<PlanNode::Kind> expected = {
+            PK::kProject,
+            PK::kAggregate,
+            PK::kProject,
+            PK::kDataJoin,
+            PK::kProject,
+            PK::kGetVertices,
+            PK::kDedup,
+            PK::kProject,
+            PK::kProject,
+            PK::kGetNeighbors,
+            PK::kStart
+        };
+        EXPECT_TRUE(checkResult(query, expected));
+    }
     {
         // group one col
         std::string query = "GO FROM \"1\" OVER like "
@@ -103,7 +146,7 @@ TEST_F(GroupByValidatorTest, TestGroupBy) {
                             "like._dst AS id, "
                             "like.start AS start_year, "
                             "like.end AS end_year"
-                            "| GROUP BY $-.name, abs(5) "
+                            "| GROUP BY $-.name, abs($-.end_year) "
                             "YIELD $-.name AS name, "
                             "SUM(1.5) AS sum, "
                             "COUNT(*) AS count, "
@@ -154,7 +197,7 @@ TEST_F(GroupByValidatorTest, VariableTest) {
     {
         std::string query =
             "$a = GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age; "
-            "GROUP BY $a.age YIELD COUNT($a.id)";
+            "GROUP BY $a.age YIELD $a.age, COUNT($a.id)";
         std::vector<PlanNode::Kind> expected = {
             PK::kAggregate, PK::kProject, PK::kGetNeighbors, PK::kStart};
         EXPECT_TRUE(checkResult(query, expected));
@@ -192,7 +235,7 @@ TEST_F(GroupByValidatorTest, VariableTest) {
                             "like._dst AS id, "
                             "like.start AS start_year, "
                             "like.end AS end_year;"
-                            "GROUP BY $a.name, abs(5) "
+                            "GROUP BY $a.name, abs($a.end_year) "
                             "YIELD $a.name AS name, "
                             "SUM(1.5) AS sum, "
                             "COUNT(*) AS count, "
@@ -220,7 +263,21 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
         std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
                             "| GROUP BY 1+1 YIELD COUNT(1), 1+1";
         auto result = checkResult(query);
-        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(1+1)` invalid");
+        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(1+1)' invalid");
+    }
+    {
+        // use groupby without input
+        std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+                            "| GROUP BY count(*)+1 YIELD COUNT(1), 1+1";
+        auto result = checkResult(query);
+        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(COUNT(*)+1)' invalid");
+    }
+    {
+        // use groupby without input
+        std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
+                            "| GROUP BY abs(1)+1 YIELD COUNT(1), 1+1";
+        auto result = checkResult(query);
+        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `(abs(1)+1)' invalid");
     }
     {
         // use dst
@@ -243,7 +300,8 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
         std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
                             "| GROUP BY noexist YIELD COUNT($-.age)";
         auto result = checkResult(query);
-        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `noexist` invalid");
+        EXPECT_EQ(std::string(result.message()),
+                   "SemanticError: Group `noexist' invalid");
     }
     {
         // use sum(*)
@@ -251,7 +309,7 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
                             "| GROUP BY $-.id YIELD SUM(*)";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `SUM(*)` invaild, * valid in count.");
+                  "SemanticError: Could not apply aggregation function `SUM(*)' on `*`");
     }
     {
         // use agg fun has more than two inputs
@@ -265,7 +323,7 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
         std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, $^.person.age AS age "
                             "| GROUP BY $-.id, SUM($-.age) YIELD $-.id, SUM($-.age)";
         auto result = checkResult(query);
-        EXPECT_EQ(std::string(result.message()), "SemanticError: Use invalid group function `SUM`");
+        EXPECT_EQ(std::string(result.message()), "SemanticError: Group `SUM($-.age)' invalid");
     }
     {
         // yield without group by
@@ -273,24 +331,44 @@ TEST_F(GroupByValidatorTest, InvalidTest) {
                             "COUNT(like._dst) AS id ";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `COUNT(like._dst) AS id', not support "
-                  "aggregate function in go sentence.");
+                  "SemanticError: `COUNT(like._dst) AS id', "
+                  "not support aggregate function in go sentence.");
+    }
+    {
+        // yield without group by
+        std::string query = "GO FROM \"1\" OVER like YIELD $^.person.age AS age, "
+                            "COUNT(like._dst)+1 AS id ";
+        auto result = checkResult(query);
+        EXPECT_EQ(std::string(result.message()),
+                  "SemanticError: `(COUNT(like._dst)+1) AS id', "
+                  "not support aggregate function in go sentence.");
+    }
+    {
+        // yield without group by
+        std::string query = "GO FROM \"1\" OVER like WHERE count(*) + 1 >3 "
+                            "YIELD $^.person.age AS age, "
+                            "COUNT(like._dst)+1 AS id ";
+        auto result = checkResult(query);
+        EXPECT_EQ(std::string(result.message()),
+                  "SemanticError: `((COUNT(*)+1)>3)', "
+                  "not support aggregate function in where sentence.");
     }
-     {
+    {
         // yield col not in group output
         std::string query = "GO FROM \"1\" OVER like "
                             "YIELD $$.person.name as name, "
                             "like._dst AS id, "
                             "like.start AS start_year, "
                             "like.end AS end_year"
-                            "| GROUP BY $-.start_year, abs(5) "
+                            "| GROUP BY $-.start_year, abs($-.end_year) "
                             "YIELD $-.name AS name, "
                             "SUM(1.5) AS sum, "
                             "COUNT(*) AS count, "
                             "1+1 AS cal";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: Yield `$-.name AS name` isn't in output fields");
+                  "SemanticError: Yield non-agg expression `$-.name' "
+                  "must be functionally dependent on items in GROUP BY clause");
     }
     {
         // duplicate col
diff --git a/src/validator/test/QueryValidatorTest.cpp b/src/validator/test/QueryValidatorTest.cpp
index 084add46..260c226f 100644
--- a/src/validator/test/QueryValidatorTest.cpp
+++ b/src/validator/test/QueryValidatorTest.cpp
@@ -1086,8 +1086,11 @@ TEST_F(QueryValidatorTest, GoInvalid) {
         EXPECT_FALSE(checkResult(query));
     }
     {
+        // yield agg without groupBy is not supported
         std::string query = "GO FROM \"2\" OVER like YIELD COUNT(123);";
-        EXPECT_FALSE(checkResult(query));
+        auto result = checkResult(query);
+        EXPECT_EQ(std::string(result.message()), "SemanticError: `COUNT(123)', "
+                  "not support aggregate function in go sentence.");
     }
     {
         std::string query = "GO FROM \"1\" OVER like YIELD like._dst AS id, like._src AS id | GO "
diff --git a/src/validator/test/YieldValidatorTest.cpp b/src/validator/test/YieldValidatorTest.cpp
index 88087a25..6458c349 100644
--- a/src/validator/test/YieldValidatorTest.cpp
+++ b/src/validator/test/YieldValidatorTest.cpp
@@ -113,19 +113,19 @@ TEST_F(YieldValidatorTest, FuncitonCall) {
         std::string query = "YIELD abs(true)";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `abs(true)` is not a valid expression : Parameter's type error");
+                  "SemanticError: `abs(true)' is not a valid expression : Parameter's type error");
     }
     {
         std::string query = "YIELD abs(\"test\")";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `abs(test)` is not a valid expression : Parameter's type error");
+                  "SemanticError: `abs(test)' is not a valid expression : Parameter's type error");
     }
     {
         std::string query = "YIELD noexist(12)";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `noexist(12)` is not a valid expression : Function `noexist' not "
+                  "SemanticError: `noexist(12)' is not a valid expression : Function `noexist' not "
                   "defined");
     }
 }
@@ -164,13 +164,13 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
         std::string query = "YIELD (int)\"123abc\"";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `(INT)123abc` is not a valid expression ");
+                  "SemanticError: `(INT)123abc' is not a valid expression ");
     }
     {
         std::string query = "YIELD (int)\"abc123\"";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `(INT)abc123` is not a valid expression ");
+                  "SemanticError: `(INT)abc123' is not a valid expression ");
     }
     {
         std::string query = "YIELD (doublE)\"123\"";
@@ -184,7 +184,7 @@ TEST_F(YieldValidatorTest, TypeCastTest) {
         std::string query = "YIELD (doublE)\".a123\"";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: `(FLOAT).a123` is not a valid expression ");
+                  "SemanticError: `(FLOAT).a123' is not a valid expression ");
     }
     {
         std::string query = "YIELD (STRING)1.23";
@@ -485,12 +485,8 @@ TEST_F(YieldValidatorTest, AggCall) {
                      "YIELD $^.person.age AS age, "
                      "like.likeness AS like"
                      "| YIELD COUNT(*), $-.age";
-        auto result = checkResult(query);
-        EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: Input columns without aggregation are not supported in YIELD "
-                  "statement without GROUP BY, near `$-.age'");
+        EXPECT_TRUE(checkResult(query));
     }
-    // Test input
     {
         auto query = "GO FROM \"1\" OVER like "
                      "YIELD $^.person.age AS age, "
@@ -537,7 +533,7 @@ TEST_F(YieldValidatorTest, AggCall) {
                      "YIELD $^.person.age AS age, "
                      "like.likeness AS like"
                      "| YIELD AVG($-.age), SUM($-.like), COUNT(*), $-.age + 1";
-        EXPECT_FALSE(checkResult(query));
+        EXPECT_TRUE(checkResult(query));
     }
     {
         auto query = "GO FROM \"1\" OVER like "
@@ -546,7 +542,7 @@ TEST_F(YieldValidatorTest, AggCall) {
                      "| YIELD AVG($-.*)";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: could not apply aggregation function on `$-.*'");
+                  "SemanticError: Could not apply aggregation function `AVG($-.*)' on `$-.*'");
     }
     // Yield field has not input
     {
@@ -589,7 +585,7 @@ TEST_F(YieldValidatorTest, AggCall) {
                      "YIELD $^.person.age AS age, "
                      "like.likeness AS like;"
                      "YIELD AVG($var.age), SUM($var.like), COUNT(*), $var.age + 1";
-        EXPECT_FALSE(checkResult(query));
+        EXPECT_TRUE(checkResult(query));
     }
     {
         auto query = "$var = GO FROM \"1\" OVER like "
@@ -626,7 +622,7 @@ TEST_F(YieldValidatorTest, AggCall) {
                      "YIELD AVG($var.*)";
         auto result = checkResult(query);
         EXPECT_EQ(std::string(result.message()),
-                  "SemanticError: could not apply aggregation function on `$var.*'");
+                  "SemanticError: Could not apply aggregation function `AVG($var.*)' on `$var.*'");
     }
 }
 
diff --git a/src/visitor/CMakeLists.txt b/src/visitor/CMakeLists.txt
index a7e61a16..01a11588 100644
--- a/src/visitor/CMakeLists.txt
+++ b/src/visitor/CMakeLists.txt
@@ -12,8 +12,10 @@ nebula_add_library(
     ExtractPropExprVisitor.cpp
     ExtractFilterExprVisitor.cpp
     FindAnyExprVisitor.cpp
+    FindAnySubExprVisitor.cpp
     FoldConstantExprVisitor.cpp
     RewriteLabelAttrVisitor.cpp
+    RewriteAggExprVisitor.cpp
     RewriteInputPropVisitor.cpp
     RewriteSymExprVisitor.cpp
     RewriteMatchLabelVisitor.cpp
diff --git a/src/visitor/CollectAllExprsVisitor.cpp b/src/visitor/CollectAllExprsVisitor.cpp
index 7f6366d1..e1599c1e 100644
--- a/src/visitor/CollectAllExprsVisitor.cpp
+++ b/src/visitor/CollectAllExprsVisitor.cpp
@@ -30,6 +30,11 @@ void CollectAllExprsVisitor::visit(FunctionCallExpression *expr) {
     }
 }
 
+void CollectAllExprsVisitor::visit(AggregateExpression *expr) {
+    collectExpr(expr);
+    expr->arg()->accept(this);
+}
+
 void CollectAllExprsVisitor::visit(ListExpression *expr) {
     collectExpr(expr);
     for (auto &item : expr->items()) {
diff --git a/src/visitor/CollectAllExprsVisitor.h b/src/visitor/CollectAllExprsVisitor.h
index ba6aa8da..fa6b16c2 100644
--- a/src/visitor/CollectAllExprsVisitor.h
+++ b/src/visitor/CollectAllExprsVisitor.h
@@ -32,6 +32,7 @@ private:
     void visit(TypeCastingExpression* expr) override;
     void visit(UnaryExpression* expr) override;
     void visit(FunctionCallExpression* expr) override;
+    void visit(AggregateExpression* expr) override;
     void visit(ListExpression* expr) override;
     void visit(SetExpression* expr) override;
     void visit(MapExpression* expr) override;
diff --git a/src/visitor/DeduceTypeVisitor.cpp b/src/visitor/DeduceTypeVisitor.cpp
index 49f2258c..1bc42b21 100644
--- a/src/visitor/DeduceTypeVisitor.cpp
+++ b/src/visitor/DeduceTypeVisitor.cpp
@@ -182,7 +182,7 @@ void DeduceTypeVisitor::visit(TypeCastingExpression *expr) {
     auto val = expr->eval(ctx(nullptr));
     if (val.isNull()) {
         status_ =
-            Status::SemanticError("`%s` is not a valid expression ", expr->toString().c_str());
+            Status::SemanticError("`%s' is not a valid expression ", expr->toString().c_str());
         return;
     }
     type_ = val.type();
@@ -386,7 +386,7 @@ void DeduceTypeVisitor::visit(FunctionCallExpression *expr) {
     }
     auto result = FunctionManager::getReturnType(funName, argsTypeList);
     if (!result.ok()) {
-        status_ = Status::SemanticError("`%s` is not a valid expression : %s",
+        status_ = Status::SemanticError("`%s' is not a valid expression : %s",
                                         expr->toString().c_str(),
                                         result.status().toString().c_str());
         return;
@@ -394,6 +394,65 @@ void DeduceTypeVisitor::visit(FunctionCallExpression *expr) {
     type_ = result.value();
 }
 
+void DeduceTypeVisitor::visit(AggregateExpression *expr) {
+    expr->arg()->accept(this);
+    if (!ok()) return;
+    auto arg_type = type_;
+
+    auto func = AggregateExpression::NAME_ID_MAP[expr->name()->c_str()];
+    switch (func) {
+        case AggregateExpression::Function::kCount: {
+            type_ = Value::Type::INT;
+            break;
+        }
+        case AggregateExpression::Function::kSum: {
+            type_ = arg_type;
+            break;
+        }
+        case AggregateExpression::Function::kAvg: {
+            type_ = Value::Type::FLOAT;
+            break;
+        }
+        case AggregateExpression::Function::kMax: {
+            type_ = arg_type;
+            break;
+        }
+        case AggregateExpression::Function::kMin: {
+            type_ = arg_type;
+            break;
+        }
+        case AggregateExpression::Function::kStdev: {
+            type_ = Value::Type::FLOAT;
+            break;
+        }
+        case AggregateExpression::Function::kBitAnd: {
+            type_ = Value::Type::INT;
+            break;
+        }
+        case AggregateExpression::Function::kBitOr: {
+            type_ = Value::Type::INT;
+            break;
+        }
+        case AggregateExpression::Function::kBitXor: {
+            type_ = Value::Type::INT;
+            break;
+        }
+        case AggregateExpression::Function::kCollect: {
+            type_ = Value::Type::LIST;
+            break;
+        }
+        case AggregateExpression::Function::kCollectSet: {
+            type_ = Value::Type::SET;
+            break;
+        }
+        default: {
+            LOG(FATAL) << "Invalid Aggregate expression kind: "
+                       << expr->name()->c_str();
+            break;
+        }
+    }
+}
+
 void DeduceTypeVisitor::visit(UUIDExpression *) {
     type_ = Value::Type::STRING;
 }
diff --git a/src/visitor/DeduceTypeVisitor.h b/src/visitor/DeduceTypeVisitor.h
index ed35c9cb..fe3e0a9a 100644
--- a/src/visitor/DeduceTypeVisitor.h
+++ b/src/visitor/DeduceTypeVisitor.h
@@ -55,6 +55,7 @@ private:
     void visit(LogicalExpression *expr) override;
     // function call
     void visit(FunctionCallExpression *expr) override;
+    void visit(AggregateExpression *expr) override;
     void visit(UUIDExpression *expr) override;
     // variable expression
     void visit(VariableExpression *expr) override;
diff --git a/src/visitor/ExprVisitorImpl.cpp b/src/visitor/ExprVisitorImpl.cpp
index 2a2cfd0a..9f549e99 100644
--- a/src/visitor/ExprVisitorImpl.cpp
+++ b/src/visitor/ExprVisitorImpl.cpp
@@ -64,6 +64,11 @@ void ExprVisitorImpl::visit(FunctionCallExpression *expr) {
     }
 }
 
+void ExprVisitorImpl::visit(AggregateExpression *expr) {
+    DCHECK(ok());
+    expr->arg()->accept(this);
+}
+
 // container expression
 void ExprVisitorImpl::visit(ListExpression *expr) {
     DCHECK(ok());
diff --git a/src/visitor/ExprVisitorImpl.h b/src/visitor/ExprVisitorImpl.h
index 4ebf4db0..2f24b5d7 100644
--- a/src/visitor/ExprVisitorImpl.h
+++ b/src/visitor/ExprVisitorImpl.h
@@ -25,6 +25,7 @@ public:
     void visit(LogicalExpression *expr) override;
     // function call
     void visit(FunctionCallExpression *expr) override;
+    void visit(AggregateExpression *expr) override;
     // container expression
     void visit(ListExpression *expr) override;
     void visit(SetExpression *expr) override;
diff --git a/src/visitor/FindAnyExprVisitor.cpp b/src/visitor/FindAnyExprVisitor.cpp
index c3abfb42..6b85c5d6 100644
--- a/src/visitor/FindAnyExprVisitor.cpp
+++ b/src/visitor/FindAnyExprVisitor.cpp
@@ -35,6 +35,12 @@ void FindAnyExprVisitor::visit(FunctionCallExpression *expr) {
     }
 }
 
+void FindAnyExprVisitor::visit(AggregateExpression *expr) {
+    findExpr(expr);
+    if (found_) return;
+    expr->arg()->accept(this);
+}
+
 void FindAnyExprVisitor::visit(ListExpression *expr) {
     findExpr(expr);
     if (found_) return;
diff --git a/src/visitor/FindAnyExprVisitor.h b/src/visitor/FindAnyExprVisitor.h
index 8f91bc22..fb53103b 100644
--- a/src/visitor/FindAnyExprVisitor.h
+++ b/src/visitor/FindAnyExprVisitor.h
@@ -34,6 +34,7 @@ private:
     void visit(TypeCastingExpression* expr) override;
     void visit(UnaryExpression* expr) override;
     void visit(FunctionCallExpression* expr) override;
+    void visit(AggregateExpression* expr) override;
     void visit(ListExpression* expr) override;
     void visit(SetExpression* expr) override;
     void visit(MapExpression* expr) override;
diff --git a/src/visitor/FindAnySubExprVisitor.cpp b/src/visitor/FindAnySubExprVisitor.cpp
new file mode 100644
index 00000000..79dbf8df
--- /dev/null
+++ b/src/visitor/FindAnySubExprVisitor.cpp
@@ -0,0 +1,206 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include "visitor/FindAnySubExprVisitor.h"
+
+namespace nebula {
+namespace graph {
+
+FindAnySubExprVisitor::FindAnySubExprVisitor(std::unordered_set<Expression*> &subExprs,
+                                             bool needRecursiveSearch)
+    : subExprs_(subExprs), needRecursiveSearch_(needRecursiveSearch) {
+    DCHECK(!subExprs_.empty());
+}
+
+void FindAnySubExprVisitor::visit(TypeCastingExpression *expr) {
+    compareWithSubExprs<TypeCastingExpression>(expr);
+    if (needRecursiveSearch_) {
+        expr->operand()->accept(this);
+    }
+}
+
+void FindAnySubExprVisitor::visit(UnaryExpression *expr) {
+    compareWithSubExprs<UnaryExpression>(expr);
+    if (needRecursiveSearch_) {
+        expr->operand()->accept(this);
+    }
+}
+
+void FindAnySubExprVisitor::visit(FunctionCallExpression *expr) {
+    compareWithSubExprs<FunctionCallExpression>(expr);
+    if (needRecursiveSearch_) {
+        for (const auto &arg : expr->args()->args()) {
+            arg->accept(this);
+            if (found_) return;
+        }
+    }
+}
+
+void FindAnySubExprVisitor::visit(AggregateExpression *expr) {
+    compareWithSubExprs<AggregateExpression>(expr);
+    if (needRecursiveSearch_) {
+        expr->arg()->accept(this);
+    }
+}
+
+void FindAnySubExprVisitor::visit(ListExpression *expr) {
+    compareWithSubExprs<ListExpression>(expr);
+    if (needRecursiveSearch_) {
+        for (const auto &item : expr->items()) {
+            item->accept(this);
+            if (found_) return;
+        }
+    }
+}
+
+void FindAnySubExprVisitor::visit(SetExpression *expr) {
+    compareWithSubExprs<SetExpression>(expr);
+    if (needRecursiveSearch_) {
+        for (const auto &item : expr->items()) {
+            item->accept(this);
+            if (found_) return;
+        }
+    }
+}
+
+void FindAnySubExprVisitor::visit(MapExpression *expr) {
+    compareWithSubExprs<MapExpression>(expr);
+    if (needRecursiveSearch_) {
+        for (const auto &pair : expr->items()) {
+            pair.second->accept(this);
+            if (found_) return;
+        }
+    }
+}
+
+void FindAnySubExprVisitor::visit(CaseExpression *expr) {
+    compareWithSubExprs<CaseExpression>(expr);
+    if (needRecursiveSearch_) {
+        if (expr->hasCondition()) {
+            expr->condition()->accept(this);
+            if (found_) return;
+        }
+        if (expr->hasDefault()) {
+            expr->defaultResult()->accept(this);
+            if (found_) return;
+        }
+        for (const auto &whenThen : expr->cases()) {
+            whenThen.when->accept(this);
+            if (found_) return;
+            whenThen.then->accept(this);
+            if (found_) return;
+        }
+    }
+}
+
+void FindAnySubExprVisitor::visit(ConstantExpression *expr) {
+    compareWithSubExprs<ConstantExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgePropertyExpression *expr) {
+    compareWithSubExprs<EdgePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(TagPropertyExpression *expr) {
+    compareWithSubExprs<TagPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(InputPropertyExpression *expr) {
+    compareWithSubExprs<InputPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VariablePropertyExpression *expr) {
+    compareWithSubExprs<VariablePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(SourcePropertyExpression *expr) {
+    compareWithSubExprs<SourcePropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(DestPropertyExpression *expr) {
+    compareWithSubExprs<DestPropertyExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeSrcIdExpression *expr) {
+    compareWithSubExprs<EdgeSrcIdExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeTypeExpression *expr) {
+    compareWithSubExprs<EdgeTypeExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeRankExpression *expr) {
+    compareWithSubExprs<EdgeRankExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeDstIdExpression *expr) {
+    compareWithSubExprs<EdgeDstIdExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(UUIDExpression *expr) {
+    compareWithSubExprs<UUIDExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VariableExpression *expr) {
+    compareWithSubExprs<VariableExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VersionedVariableExpression *expr) {
+    compareWithSubExprs<VersionedVariableExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(LabelExpression *expr) {
+    compareWithSubExprs<LabelExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(VertexExpression *expr) {
+    compareWithSubExprs<VertexExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(EdgeExpression *expr) {
+    compareWithSubExprs<EdgeExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visit(ColumnExpression *expr) {
+    compareWithSubExprs<ColumnExpression>(expr);
+}
+
+void FindAnySubExprVisitor::visitBinaryExpr(BinaryExpression *expr) {
+    compareWithSubExprs<BinaryExpression>(expr);
+    if (needRecursiveSearch_) {
+        expr->left()->accept(this);
+        if (found_) return;
+        expr->right()->accept(this);
+    }
+}
+
+void FindAnySubExprVisitor::checkExprKind(const Expression *expr, const Expression *sub_expr) {
+    if (expr == sub_expr) {
+        found_ = true;
+        continue_ = false;
+    } else if (expr == nullptr || sub_expr == nullptr) {
+        continue_ = false;
+    } else if (expr->kind() != sub_expr->kind()) {
+       continue_ = false;
+    }
+}
+
+template <typename T>
+void FindAnySubExprVisitor::compareWithSubExprs(T* expr) {
+    for (Expression* sub : subExprs_) {
+        continue_ = true;
+        checkExprKind(expr, sub);
+        if (found_) return;
+        if (!continue_) continue;
+        if (*static_cast<T*>(sub) == *expr) {
+            found_ = true;
+            return;
+        }
+    }
+}
+
+}   // namespace graph
+}   // namespace nebula
diff --git a/src/visitor/FindAnySubExprVisitor.h b/src/visitor/FindAnySubExprVisitor.h
new file mode 100644
index 00000000..c9e33489
--- /dev/null
+++ b/src/visitor/FindAnySubExprVisitor.h
@@ -0,0 +1,83 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#ifndef VISITOR_FINDANYSUBEXPRVISITOR_H_
+#define VISITOR_FINDANYSUBEXPRVISITOR_H_
+
+#include <unordered_set>
+
+#include "common/expression/Expression.h"
+#include "visitor/ExprVisitorImpl.h"
+
+namespace nebula {
+namespace graph {
+
+class FindAnySubExprVisitor final : public ExprVisitorImpl {
+public:
+    explicit FindAnySubExprVisitor(std::unordered_set<Expression*> &subExprs,
+                                   bool needRecursiveSearch);
+
+    bool ok() const override {
+        return !found_;
+    }
+
+    bool found() const {
+        return found_;
+    }
+
+    const Expression* expr() const {
+        return expr_;
+    }
+
+private:
+    using ExprVisitorImpl::visit;
+
+    void visit(TypeCastingExpression* expr) override;
+    void visit(UnaryExpression* expr) override;
+    void visit(FunctionCallExpression* expr) override;
+    void visit(AggregateExpression* expr) override;
+    void visit(ListExpression* expr) override;
+    void visit(SetExpression* expr) override;
+    void visit(MapExpression* expr) override;
+    void visit(CaseExpression* expr) override;
+
+    void visit(ConstantExpression* expr) override;
+    void visit(EdgePropertyExpression* expr) override;
+    void visit(TagPropertyExpression* expr) override;
+    void visit(InputPropertyExpression* expr) override;
+    void visit(VariablePropertyExpression* expr) override;
+    void visit(SourcePropertyExpression* expr) override;
+    void visit(DestPropertyExpression* expr) override;
+    void visit(EdgeSrcIdExpression* expr) override;
+    void visit(EdgeTypeExpression* expr) override;
+    void visit(EdgeRankExpression* expr) override;
+    void visit(EdgeDstIdExpression* expr) override;
+    void visit(UUIDExpression* expr) override;
+    void visit(VariableExpression* expr) override;
+    void visit(VersionedVariableExpression* expr) override;
+    void visit(LabelExpression* expr) override;
+    void visit(VertexExpression* expr) override;
+    void visit(EdgeExpression* expr) override;
+    void visit(ColumnExpression* expr) override;
+    void visitBinaryExpr(BinaryExpression* expr) override;
+
+    void checkExprKind(const Expression*, const Expression*);
+
+    template <typename T>
+    void compareWithSubExprs(T* expr);
+
+    bool found_{false};
+    // need continue search
+    bool continue_{true};
+    const Expression* expr_{nullptr};
+    const std::unordered_set<Expression*> subExprs_;
+    bool needRecursiveSearch_{false};
+};
+
+}   // namespace graph
+}   // namespace nebula
+
+#endif   // VISITOR_FINDANYSUBEXPRVISITOR_H_
diff --git a/src/visitor/FoldConstantExprVisitor.cpp b/src/visitor/FoldConstantExprVisitor.cpp
index 82184be9..a8abe6ac 100644
--- a/src/visitor/FoldConstantExprVisitor.cpp
+++ b/src/visitor/FoldConstantExprVisitor.cpp
@@ -115,6 +115,16 @@ void FoldConstantExprVisitor::visit(FunctionCallExpression *expr) {
     canBeFolded_ = canBeFolded;
 }
 
+void FoldConstantExprVisitor::visit(AggregateExpression *expr) {
+    // TODO : impl AggExpr foldConstantExprVisitor
+    if (!isConstant(expr->arg())) {
+        expr->arg()->accept(this);
+        if (canBeFolded_) {
+            expr->setArg(fold(expr->arg()));
+        }
+    }
+}
+
 void FoldConstantExprVisitor::visit(UUIDExpression *expr) {
     UNUSED(expr);
     canBeFolded_ = false;
diff --git a/src/visitor/FoldConstantExprVisitor.h b/src/visitor/FoldConstantExprVisitor.h
index a111122b..18995604 100644
--- a/src/visitor/FoldConstantExprVisitor.h
+++ b/src/visitor/FoldConstantExprVisitor.h
@@ -35,6 +35,7 @@ public:
     void visit(LogicalExpression *expr) override;
     // function call
     void visit(FunctionCallExpression *expr) override;
+    void visit(AggregateExpression *expr) override;
     void visit(UUIDExpression *expr) override;
     // variable expression
     void visit(VariableExpression *expr) override;
diff --git a/src/visitor/RewriteAggExprVisitor.cpp b/src/visitor/RewriteAggExprVisitor.cpp
new file mode 100644
index 00000000..223c42c3
--- /dev/null
+++ b/src/visitor/RewriteAggExprVisitor.cpp
@@ -0,0 +1,71 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#include "visitor/RewriteAggExprVisitor.h"
+#include "util/ExpressionUtils.h"
+
+#include "common/base/Logging.h"
+
+namespace nebula {
+namespace graph {
+
+RewriteAggExprVisitor::RewriteAggExprVisitor(std::string* var,
+                                             std::string* prop) {
+    var_.reset(var);
+    prop_.reset(prop);
+}
+
+void RewriteAggExprVisitor::visit(TypeCastingExpression* expr) {
+    if (isAggExpr(expr->operand())) {
+        expr->setOperand(ExpressionUtils::newVarPropExpr(prop_->c_str(), var_->c_str()));
+    } else {
+        expr->operand()->accept(this);
+    }
+}
+
+// rewrite AggregateExpression to VariablePropertyExpression
+void RewriteAggExprVisitor::visit(FunctionCallExpression* expr) {
+    for (auto& arg : expr->args()->args()) {
+        if (isAggExpr(arg.get())) {
+            arg.reset(new VariablePropertyExpression(var_.release(),
+                                                     prop_.release()));
+        } else {
+            arg->accept(this);
+        }
+    }
+}
+
+void RewriteAggExprVisitor::visit(ArithmeticExpression *expr) {
+    visitBinaryExpr(static_cast<BinaryExpression*>(expr));
+}
+
+void RewriteAggExprVisitor::visitBinaryExpr(BinaryExpression *expr) {
+    DCHECK(ok());
+    auto* lhs = expr->left();
+    if (isAggExpr(lhs)) {
+        expr->setLeft(new VariablePropertyExpression(std::move(var_).release(),
+                                                     std::move(prop_).release()));
+        // only support rewrite single agg expr
+        return;
+    } else {
+        lhs->accept(this);
+    }
+    auto* rhs = expr->right();
+    if (isAggExpr(rhs)) {
+        expr->setRight(new VariablePropertyExpression(std::move(var_).release(),
+                                                     std::move(prop_).release()));
+        return;
+    } else {
+        rhs->accept(this);
+    }
+}
+bool RewriteAggExprVisitor::isAggExpr(const Expression* expr) {
+    return expr->kind() == Expression::Kind::kAggregate;
+}
+
+
+}   // namespace graph
+}   // namespace nebula
diff --git a/src/visitor/RewriteAggExprVisitor.h b/src/visitor/RewriteAggExprVisitor.h
new file mode 100644
index 00000000..598366d5
--- /dev/null
+++ b/src/visitor/RewriteAggExprVisitor.h
@@ -0,0 +1,70 @@
+/* Copyright (c) 2020 vesoft inc. All rights reserved.
+ *
+ * This source code is licensed under Apache 2.0 License,
+ * attached with Common Clause Condition 1.0, found in the LICENSES directory.
+ */
+
+#ifndef VISITOR_REWRITEAGGEXPRVISITOR_H_
+#define VISITOR_REWRITEAGGEXPRVISITOR_H_
+
+#include <memory>
+#include <vector>
+
+#include "visitor/ExprVisitorImpl.h"
+
+namespace nebula {
+namespace graph {
+
+class RewriteAggExprVisitor final : public ExprVisitorImpl {
+public:
+    explicit RewriteAggExprVisitor(std::string* var,
+                                   std::string* prop);
+
+    bool ok() const override {
+        return true;
+    }
+
+private:
+    using ExprVisitorImpl::visit;
+
+    void visit(TypeCastingExpression *expr) override;
+    void visit(FunctionCallExpression *expr) override;
+    void visit(ArithmeticExpression *expr) override;
+
+    void visit(UnaryExpression *) override {}
+    void visit(ListExpression *) override {}
+    void visit(SetExpression *) override {}
+    void visit(MapExpression *) override {}
+    void visit(CaseExpression *) override {}
+    void visit(ConstantExpression *) override {}
+    void visit(LabelExpression *) override {}
+    void visit(UUIDExpression *) override {}
+    void visit(LabelAttributeExpression *) override {}
+    void visit(VariableExpression *) override {}
+    void visit(VersionedVariableExpression *) override {}
+    void visit(TagPropertyExpression *) override {}
+    void visit(EdgePropertyExpression *) override {}
+    void visit(InputPropertyExpression *) override {}
+    void visit(VariablePropertyExpression *) override {}
+    void visit(DestPropertyExpression *) override {}
+    void visit(SourcePropertyExpression *) override {}
+    void visit(EdgeSrcIdExpression *) override {}
+    void visit(EdgeTypeExpression *) override {}
+    void visit(EdgeRankExpression *) override {}
+    void visit(EdgeDstIdExpression *) override {}
+    void visit(VertexExpression *) override {}
+    void visit(EdgeExpression *) override {}
+    void visit(ColumnExpression *) override {}
+    void visitBinaryExpr(BinaryExpression *) override;
+
+    static bool isAggExpr(const Expression* expr);
+
+private:
+    std::unique_ptr<std::string> var_;
+    std::unique_ptr<std::string> prop_;
+};
+
+}   // namespace graph
+}   // namespace nebula
+
+#endif   // VISITOR_REWRITEAGGEXPRVISITOR_H_
diff --git a/src/visitor/RewriteInputPropVisitor.cpp b/src/visitor/RewriteInputPropVisitor.cpp
index bd5db3d2..b93635ca 100644
--- a/src/visitor/RewriteInputPropVisitor.cpp
+++ b/src/visitor/RewriteInputPropVisitor.cpp
@@ -171,6 +171,14 @@ void RewriteInputPropVisitor::visit(FunctionCallExpression* expr) {
     }
 }
 
+void RewriteInputPropVisitor::visit(AggregateExpression* expr) {
+    auto* arg = expr->arg();
+    arg->accept(this);
+    if (ok()) {
+        expr->setArg(std::move(result_).release());
+    }
+}
+
 void RewriteInputPropVisitor::visit(TypeCastingExpression* expr) {
     expr->operand()->accept(this);
     if (ok()) {
diff --git a/src/visitor/RewriteInputPropVisitor.h b/src/visitor/RewriteInputPropVisitor.h
index b14d3639..a15a9479 100644
--- a/src/visitor/RewriteInputPropVisitor.h
+++ b/src/visitor/RewriteInputPropVisitor.h
@@ -47,6 +47,7 @@ private:
     void visit(LogicalExpression *) override;
     // function call
     void visit(FunctionCallExpression *) override;
+    void visit(AggregateExpression *) override;
     void visit(UUIDExpression *) override;
     // variable expression
     void visit(VariableExpression *) override;
diff --git a/src/visitor/RewriteSymExprVisitor.cpp b/src/visitor/RewriteSymExprVisitor.cpp
index 403627ae..892c1fd5 100644
--- a/src/visitor/RewriteSymExprVisitor.cpp
+++ b/src/visitor/RewriteSymExprVisitor.cpp
@@ -93,6 +93,14 @@ void RewriteSymExprVisitor::visit(FunctionCallExpression *expr) {
     }
 }
 
+void RewriteSymExprVisitor::visit(AggregateExpression *expr) {
+    auto* arg = expr->arg();
+    arg->accept(this);
+    if (expr_) {
+        expr->setArg(std::move(expr_).release());
+    }
+}
+
 void RewriteSymExprVisitor::visit(UUIDExpression *expr) {
     UNUSED(expr);
     hasWrongType_ = true;
diff --git a/src/visitor/RewriteSymExprVisitor.h b/src/visitor/RewriteSymExprVisitor.h
index b37fa3c3..6e14c56f 100644
--- a/src/visitor/RewriteSymExprVisitor.h
+++ b/src/visitor/RewriteSymExprVisitor.h
@@ -42,6 +42,7 @@ public:
     void visit(LogicalExpression *expr) override;
     // function call
     void visit(FunctionCallExpression *expr) override;
+    void visit(AggregateExpression *expr) override;
     void visit(UUIDExpression *expr) override;
     // variable expression
     void visit(VariableExpression *expr) override;
diff --git a/src/visitor/test/CMakeLists.txt b/src/visitor/test/CMakeLists.txt
index 583e5a4d..231eb9b8 100644
--- a/src/visitor/test/CMakeLists.txt
+++ b/src/visitor/test/CMakeLists.txt
@@ -42,7 +42,6 @@ nebula_add_test(
         $<TARGET_OBJECTS:common_meta_client_obj>
         $<TARGET_OBJECTS:common_file_based_cluster_id_man_obj>
         $<TARGET_OBJECTS:common_function_manager_obj>
-        $<TARGET_OBJECTS:common_agg_function_obj>
         $<TARGET_OBJECTS:common_conf_obj>
         $<TARGET_OBJECTS:common_encryption_obj>
         $<TARGET_OBJECTS:common_http_client_obj>
diff --git a/tests/query/v1/test_yield.py b/tests/query/v1/test_yield.py
index 83842f07..31c414a5 100644
--- a/tests/query/v1/test_yield.py
+++ b/tests/query/v1/test_yield.py
@@ -404,13 +404,13 @@ class TestYield(NebulaTestSuite):
         resp = self.execute(query)
         self.check_resp_failed(resp, ttypes.ErrorCode.E_SEMANTIC_ERROR)
 
-        # query = '''YIELD 1+COUNT(*), 1+1'''
-        # resp = self.execute(query)
-        # self.check_resp_succeeded(resp)
-        # columns = ["(1+count(*))", "(1+1)"]
-        # self.check_column_names(resp, columns)
-        # expect_result = [[2, 2]]
-        # self.check_result(resp, expect_result)
+        query = '''YIELD 1+COUNT(*), 1+1'''
+        resp = self.execute(query)
+        self.check_resp_succeeded(resp)
+        columns = ["(1+COUNT(*))", "(1+1)"]
+        self.check_column_names(resp, columns)
+        expect_result = [[2, 2]]
+        self.check_result(resp, expect_result)
 
         query = '''YIELD COUNT(*), 1+1'''
         resp = self.execute(query)
@@ -423,7 +423,7 @@ class TestYield(NebulaTestSuite):
         query = '''GO FROM "Carmelo Anthony" OVER like YIELD $$.player.age AS age, like.likeness AS like \
         | YIELD COUNT(*), $-.age'''
         resp = self.execute(query)
-        self.check_resp_failed(resp, ttypes.ErrorCode.E_SEMANTIC_ERROR)
+        self.check_resp_succeeded(resp)
 
         # Test input
         query = '''GO FROM "Carmelo Anthony" OVER like YIELD $$.player.age AS age, like.likeness AS like \
diff --git a/tests/tck/features/agg/Agg.feature b/tests/tck/features/agg/Agg.feature
new file mode 100644
index 00000000..d9998f9c
--- /dev/null
+++ b/tests/tck/features/agg/Agg.feature
@@ -0,0 +1,559 @@
+Feature: Basic Aggregate and GroupBy
+
+  Background:
+    Given a graph with space named "nba"
+
+  Scenario: Basic Aggregate
+    When executing query:
+      """
+      YIELD COUNT(*), 1+1
+      """
+    Then the result should be, in any order, with relax comparison:
+      | COUNT(*) | (1+1) |
+      | 1        | 2     |
+    When executing query:
+      """
+      YIELD count(*)+1 ,1+2 ,(INT)abs(count(2))
+      """
+    Then the result should be, in any order, with relax comparison:
+      | (COUNT(*)+1) | (1+2) | (INT)abs(COUNT(2)) |
+      | 2            | 3     | 1                  |
+
+  Scenario: Basic GroupBy
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | YIELD count(*) AS count
+      """
+    Then the result should be, in any order, with relax comparison:
+      | count |
+      | 2     |
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | YIELD DISTINCT count(*) AS count where $-.age > 40
+      """
+    Then the result should be, in any order, with relax comparison:
+      | count |
+      | 1     |
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+      YIELD count($var.dst) AS count
+      """
+    Then the result should be, in any order, with relax comparison:
+      | count |
+      | 2     |
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD DISTINCT like._dst AS dst, $$.player.age AS age;
+      YIELD count($var.dst) AS count where $var.age > 40
+      """
+    Then the result should be, in any order, with relax comparison:
+      | count |
+      | 1     |
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  |
+      | "Tony Parker"   | 36.0 |
+      | "Manu Ginobili" | 41.0 |
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  |
+      | "Tony Parker"   | 36.0 |
+      | "Manu Ginobili" | 41.0 |
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.dst YIELD $-.dst AS dst, avg(distinct $-.age) AS age
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  |
+      | "Tony Parker"   | 36.0 |
+      | "Manu Ginobili" | 41.0 |
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+      YIELD DISTINCT $var.dst AS dst, avg(distinct $var.age) AS age
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  |
+      | "Tony Parker"   | 36.0 |
+      | "Manu Ginobili" | 41.0 |
+    When executing query:
+      """
+      GO FROM 'Aron Baynes', 'Tracy McGrady' OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id,
+               serve.start_year AS start_year,
+               serve.end_year AS end_year
+         | GROUP BY $-.name, $-.start_year
+           YIELD $-.name AS teamName,
+                 $-.start_year AS start_year,
+                 MAX($-.start_year),
+                 MIN($-.end_year),
+                 AVG($-.end_year) AS avg_end_year,
+                 STD($-.end_year) AS std_end_year,
+                 COUNT($-.id)
+      """
+    Then the result should be, in any order, with relax comparison:
+      | teamName  | start_year | MAX($-.start_year) | MIN($-.end_year) | avg_end_year | std_end_year | COUNT($-.id) |
+      | "Celtics" | 2017       | 2017               | 2019             | 2019.0       | 0.0          | 1            |
+      | "Magic"   | 2000       | 2000               | 2004             | 2004.0       | 0.0          | 1            |
+      | "Pistons" | 2015       | 2015               | 2017             | 2017.0       | 0.0          | 1            |
+      | "Raptors" | 1997       | 1997               | 2000             | 2000.0       | 0.0          | 1            |
+      | "Rockets" | 2004       | 2004               | 2010             | 2010.0       | 0.0          | 1            |
+      | "Spurs"   | 2013       | 2013               | 2013             | 2014.0       | 1.0          | 2            |
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+         serve._dst AS id,
+         serve.start_year AS start_year,
+         serve.end_year AS end_year
+      | GROUP BY $-.start_year
+        YIELD COUNT($-.id),
+              $-.start_year AS start_year,
+              AVG($-.end_year) as avg
+      """
+    Then the result should be, in any order, with relax comparison:
+      | COUNT($-.id) | start_year | avg    |
+      | 2            | 2018       | 2018.5 |
+      | 1            | 2017       | 2018.0 |
+      | 1            | 2016       | 2017.0 |
+      | 1            | 2009       | 2010.0 |
+      | 1            | 2007       | 2009.0 |
+      | 1            | 2012       | 2013.0 |
+      | 1            | 2013       | 2015.0 |
+      | 1            | 2015       | 2016.0 |
+      | 1            | 2010       | 2012.0 |
+    When executing query:
+      """
+      GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+         YIELD $$.player.name AS name,
+               $$.player.age AS dst_age,
+               $$.player.age AS src_age,
+               like.likeness AS likeness
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM($-.dst_age) AS sum_dst_age,
+                 AVG($-.dst_age) AS avg_dst_age,
+                 MAX($-.src_age) AS max_src_age,
+                 MIN($-.src_age) AS min_src_age,
+                 BIT_AND(1) AS bit_and,
+                 BIT_OR(2) AS bit_or,
+                 BIT_XOR(3) AS bit_xor,
+                 COUNT($-.likeness),
+                 COUNT(DISTINCT $-.likeness)
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name              | sum_dst_age | avg_dst_age | max_src_age | min_src_age | bit_and | bit_or | bit_xor | COUNT($-.likeness) | COUNT(distinct $-.likeness) |
+      | "LeBron James"    | 68          | 34.0        | 34          | 34          | 1       | 2      | 0       | 2                  | 1                           |
+      | "Chris Paul"      | 66          | 33.0        | 33          | 33          | 1       | 2      | 0       | 2                  | 1                           |
+      | "Dwyane Wade"     | 37          | 37.0        | 37          | 37          | 1       | 2      | 3       | 1                  | 1                           |
+      | "Carmelo Anthony" | 34          | 34.0        | 34          | 34          | 1       | 2      | 3       | 1                  | 1                           |
+    When executing query:
+      """
+      GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+         YIELD $$.player.name AS name,
+               $$.player.age AS dst_age,
+              $$.player.age AS src_age,
+              like.likeness AS likeness
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM($-.dst_age) AS sum_dst_age,
+                 AVG($-.dst_age) AS avg_dst_age,
+                 MAX($-.src_age) AS max_src_age,
+                 MIN($-.src_age) AS min_src_age,
+                 BIT_AND(1) AS bit_and,
+                 BIT_OR(2) AS bit_or,
+                 BIT_XOR(3) AS bit_xor,
+                 COUNT($-.likeness)
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name              | sum_dst_age | avg_dst_age | max_src_age | min_src_age | bit_and | bit_or | bit_xor | COUNT($-.likeness) |
+      | "LeBron James"    | 68          | 34.0        | 34          | 34          | 1       | 2      | 0       | 2                  |
+      | "Chris Paul"      | 66          | 33.0        | 33          | 33          | 1       | 2      | 0       | 2                  |
+      | "Dwyane Wade"     | 37          | 37.0        | 37          | 37          | 1       | 2      | 3       | 1                  |
+      | "Carmelo Anthony" | 34          | 34.0        | 34          | 34          | 1       | 2      | 3       | 1                  |
+    When executing query:
+      """
+      GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+         | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+         | GROUP BY $-.dst
+           YIELD $-.dst AS dst, BIT_OR($-.following) AS following
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | following |
+      | "Tony Parker"   | BAD_TYPE  |
+      | "Manu Ginobili" | BAD_TYPE  |
+    When executing query:
+      """
+      GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+         | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+         | GROUP BY $-.dst
+           YIELD $-.dst AS dst,
+                 BIT_OR(case when $-.following==true then 1 else 0 end) AS following
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | following |
+      | "Tony Parker"   | 1         |
+      | "Manu Ginobili" | 1         |
+    When executing query:
+      """
+      GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+         | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+         | GROUP BY $-.dst
+           YIELD $-.dst AS dst,
+                 BIT_AND($-.following) AS following
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | following |
+      | "Tony Parker"   | BAD_TYPE  |
+      | "Manu Ginobili" | BAD_TYPE  |
+    When executing query:
+      """
+      GO FROM 'Tim Duncan' OVER like YIELD like._dst as dst
+         | GO FROM $-.dst over like YIELD $-.dst as dst, like._dst == 'Tim Duncan' as following
+         | GROUP BY $-.dst
+           YIELD $-.dst AS dst,
+                 BIT_AND(case when $-.following==true then 1 else 0 end) AS following
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | following |
+      | "Tony Parker"   | 0         |
+      | "Manu Ginobili" | 1         |
+    When executing query:
+      """
+      GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+         YIELD $$.player.name AS name
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM(1.5) AS sum,
+                 COUNT(*) AS count,
+                 1+1 AS cal
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name              | sum | count | cal |
+      | "LeBron James"    | 3.0 | 2     | 2   |
+      | "Chris Paul"      | 3.0 | 2     | 2   |
+      | "Dwyane Wade"     | 1.5 | 1     | 2   |
+      | "Carmelo Anthony" | 1.5 | 1     | 2   |
+    When executing query:
+      """
+      GO FROM 'Paul Gasol' OVER like
+         YIELD $$.player.age AS age,
+               like._dst AS id
+         | GROUP BY $-.id
+           YIELD $-.id AS id,
+                 SUM($-.age) AS age
+           | GO FROM $-.id OVER serve
+             YIELD $$.team.name AS name,
+                   $-.age AS sumAge
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name        | sumAge |
+      | "Grizzlies" | 34     |
+      | "Raptors"   | 34     |
+      | "Lakers"    | 40     |
+
+  Scenario: Implicit GroupBy
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | YIELD $-.dst AS dst, 1+avg(distinct $-.age) AS age, abs(5) as abs
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  | abs |
+      | "Tony Parker"   | 37.0 | 5   |
+      | "Manu Ginobili" | 42.0 | 5   |
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | YIELD $-.dst AS dst, 1+avg(distinct $-.age) AS age where $-.age > 40
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age  |
+      | "Manu Ginobili" | 42.0 |
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.age+1 YIELD (INT)($-.age+1) AS age, 1+count(distinct $-.dst) AS count
+      """
+    Then the result should be, in any order, with relax comparison:
+      | age | count |
+      | 37  | 2     |
+      | 42  | 2     |
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+      YIELD $var.dst AS dst, (INT)abs(1+avg(distinct $var.age)) AS age
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | age |
+      | "Tony Parker"   | 37  |
+      | "Manu Ginobili" | 42  |
+    When executing query:
+      """
+      $var1=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst;
+      $var2=GO FROM "Tim Duncan" OVER serve YIELD serve._dst AS dst;
+      YIELD $var1.dst AS dst, count($var1.dst) AS count
+      """
+    Then the result should be, in any order, with relax comparison:
+      | dst             | count |
+      | "Tony Parker"   | 1     |
+      | "Manu Ginobili" | 1     |
+
+  Scenario: Empty input
+    When executing query:
+      """
+      GO FROM 'noexist' OVER like
+         YIELD $$.player.name AS name
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM(1.5) AS sum,
+                 COUNT(*) AS count
+                | ORDER BY $-.sum
+                | LIMIT 2
+      """
+    Then the result should be, in order, with relax comparison:
+      | name | sum | count |
+    When executing query:
+      """
+      GO FROM 'noexist' OVER serve
+                YIELD $^.player.name as name,
+                serve.start_year as start,
+                $$.team.name as team
+                | YIELD $-.name as name
+                WHERE $-.start > 20000
+                | GROUP BY $-.name
+                YIELD $-.name AS name
+      """
+    Then the result should be, in order, with relax comparison:
+      | name |
+    When executing query:
+      """
+      GO FROM 'noexist' OVER serve
+                YIELD $^.player.name as name,
+                serve.start_year as start,
+                $$.team.name as team
+                | YIELD $-.name as name
+                WHERE $-.start > 20000
+                | Limit 1
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name |
+
+  Scenario: Duplicate column
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id,
+               serve.start_year AS start_year,
+               serve.end_year AS start_year
+          | GROUP BY $-.start_year
+            YIELD COUNT($-.id),
+                  $-.start_year AS start_year,
+                  AVG($-.end_year) as avg
+      """
+    Then a SemanticError should be raised at runtime:
+    When executing query:
+      """
+      GO FROM 'noexist' OVER serve
+         YIELD $^.player.name as name,
+               serve.start_year as start,
+              $$.team.name as name
+         | GROUP BY $-.name
+           YIELD $-.name AS name
+      """
+    Then a SemanticError should be raised at runtime:
+
+  Scenario: order by and limit
+    When executing query:
+      """
+      GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+         YIELD $$.player.name AS name
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM(1.5) AS sum,
+                 COUNT(*) AS count
+            | ORDER BY $-.sum, $-.name
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name              | sum | count |
+      | "Carmelo Anthony" | 1.5 | 1     |
+      | "Dwyane Wade"     | 1.5 | 1     |
+      | "Chris Paul"      | 3.0 | 2     |
+      | "LeBron James"    | 3.0 | 2     |
+    When executing query:
+      """
+      GO FROM 'Carmelo Anthony', 'Dwyane Wade' OVER like
+         YIELD $$.player.name AS name
+         | GROUP BY $-.name
+           YIELD $-.name AS name,
+                 SUM(1.5) AS sum,
+                 COUNT(*) AS count
+            | ORDER BY $-.sum, $-.name  DESC
+            | LIMIT 2
+      """
+    Then the result should be, in any order, with relax comparison:
+      | name              | sum | count |
+      | "Carmelo Anthony" | 1.5 | 1     |
+      | "Dwyane Wade"     | 1.5 | 1     |
+
+  Scenario: Error Check
+    When executing query:
+      """
+      YIELD avg(*)+1 ,1+2 ,(INT)abs(min(2))
+      """
+    Then a SemanticError should be raised at runtime: Could not apply aggregation function `AVG(*)' on `*`
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.dst,$-.x YIELD avg(distinct $-.age) AS age
+      """
+    Then a SemanticError should be raised at runtime:  `$-.x', not exist prop `x'
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.age+1 YIELD $-.age+1,age,avg(distinct $-.age) AS age
+      """
+    Then a SemanticError should be raised at runtime: Not supported expression `age' for props deduction.
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.age+1 YIELD $-.age,avg(distinct $-.age) AS age
+      """
+    Then a SemanticError should be raised at runtime: Yield non-agg expression `$-.age' must be functionally dependent on items in GROUP BY clause
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.age+1 YIELD $-.age+1,abs(avg(distinct count($-.age))) AS age
+      """
+    Then a SemanticError should be raised at runtime: Aggregate function nesting is not allowed: `abs(AVG(distinct COUNT($-.age)))'
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY $-.age+1 YIELD $-.age+1,avg(distinct count($-.age+1)) AS age
+      """
+    Then a SemanticError should be raised at runtime: Aggregate function nesting is not allowed: `AVG(distinct COUNT(($-.age+1)))'
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | GROUP BY avg($-.age+1)+1 YIELD $-.age,avg(distinct $-.age) AS age
+      """
+    Then a SemanticError should be raised at runtime:  Group `(AVG(($-.age+1))+1)' invalid
+    When executing query:
+      """
+      $var=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age;
+      GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst, $$.player.age AS age
+      | YIELD $var.dst AS dst, avg(distinct $-.age) AS age
+      """
+    Then a SemanticError should be raised at runtime: Not support both input and variable in GroupBy sentence.
+    When executing query:
+      """
+      $var1=GO FROM "Tim Duncan" OVER like YIELD like._dst AS dst;
+      $var2=GO FROM "Tim Duncan" OVER serve YIELD serve._dst AS dst;
+      YIELD count($var1.dst),$var2.dst AS count
+      """
+    Then a SemanticError should be raised at runtime: Only one variable allowed to use.
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like YIELD count(*)
+      """
+    Then a SemanticError should be raised at runtime: `COUNT(*)', not support aggregate function in go sentence.
+    When executing query:
+      """
+      GO FROM "Tim Duncan" OVER like where count(*) > 2
+      """
+    Then a SemanticError should be raised at runtime: `(COUNT(*)>2)', not support aggregate function in where sentence.
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve.end_year AS end_year
+         | GROUP BY $-.end_year
+           YIELD COUNT($$.team.name)
+      """
+    Then a SemanticError should be raised at runtime:  Only support input and variable in GroupBy sentence.
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id
+         | GROUP BY $-.start_year
+           YIELD COUNT($-.id)
+      """
+    Then a SemanticError should be raised at runtime: `$-.start_year', not exist prop `start_year'
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+        YIELD $$.team.name AS name,
+              serve._dst AS id
+        | GROUP BY team
+          YIELD COUNT($-.id),
+                $-.name AS teamName
+      """
+    Then a SemanticError should be raised at runtime:  Group `team' invalid
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id
+         | GROUP BY $-.name
+           YIELD COUNT($-.start_year)
+      """
+    Then a SemanticError should be raised at runtime: `$-.start_year', not exist prop `start_year'
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id
+         | GROUP BY $-.name
+           YIELD SUM(*)
+      """
+    Then a SemanticError should be raised at runtime:  Could not apply aggregation function `SUM(*)' on `*`
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id
+         | GROUP BY $-.name
+           YIELD COUNT($-.name, $-.id)
+      """
+    Then a SyntaxError should be raised at runtime: syntax error near `, $-.id)'
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               serve._dst AS id
+         | GROUP BY $-.name, SUM($-.id)
+           YIELD $-.name,  SUM($-.id)
+      """
+    Then a SemanticError should be raised at runtime:  Group `SUM($-.id)' invalid
+    When executing query:
+      """
+      GO FROM "Marco Belinelli" OVER serve
+         YIELD $$.team.name AS name,
+               COUNT(serve._dst) AS id
+      """
+    Then a SemanticError should be raised at runtime: `COUNT(serve._dst) AS id', not support aggregate function in go sentence.
+
+# When executing query:
+# """
+# GO FROM "Marco Belinelli" OVER serve
+# YIELD $$.team.name AS name,
+# serve.end_year AS end_year
+# | GROUP BY $-.end_year
+# YIELD COUNT($var)
+# """
+# Then a SemanticError should be raised at runtime:
diff --git a/tests/tck/features/go/GO.feature b/tests/tck/features/go/GO.feature
index e8b4ae4c..ab7754b5 100644
--- a/tests/tck/features/go/GO.feature
+++ b/tests/tck/features/go/GO.feature
@@ -1610,7 +1610,8 @@ Feature: Go Sentence
     When executing query:
       """
       GO 1 TO 2 STEPS FROM "Tim Duncan" OVER like WHERE like._dst != "YAO MING" YIELD like._dst AS vid
-      | GROUP BY $-.vid YIELD 1 AS id | GROUP BY $-.id YIELD COUNT($-.id);
+      | GROUP BY $-.vid YIELD 1 AS id
+      | GROUP BY $-.id YIELD COUNT($-.id);
       """
     Then the result should be, in any order, with relax comparison:
       | COUNT($-.id) |
diff --git a/tests/tck/features/go/GroupbyLimit.IntVid.feature b/tests/tck/features/go/GroupbyLimit.IntVid.feature
index e919b036..ce2ad2ae 100644
--- a/tests/tck/features/go/GroupbyLimit.IntVid.feature
+++ b/tests/tck/features/go/GroupbyLimit.IntVid.feature
@@ -178,7 +178,7 @@ Feature: Groupby & limit Sentence
     Then a SemanticError should be raised at runtime:
     When executing query:
       """
-      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count, 1+1 AS cal
       """
     Then the result should be, in any order, with relax comparison:
@@ -275,7 +275,7 @@ Feature: Groupby & limit Sentence
   Scenario: Groupby works with orderby or limit test
     When executing query:
       """
-      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name
       """
     Then the result should be, in order, with relax comparison:
@@ -286,7 +286,7 @@ Feature: Groupby & limit Sentence
       | "LeBron James"    | 3.0 | 2     |
     When executing query:
       """
-      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM hash("Carmelo Anthony"),hash("Dwyane Wade") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name DESC | LIMIT 2
       """
     Then the result should be, in order, with relax comparison:
@@ -303,7 +303,7 @@ Feature: Groupby & limit Sentence
     Then a SemanticError should be raised at runtime:
     When executing query:
       """
-      GO FROM hash("NON EXIST VERTEX ID") OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM hash("NON EXIST VERTEX ID") OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum | LIMIT 2
       """
     Then the result should be, in order, with relax comparison:
diff --git a/tests/tck/features/go/GroupbyLimit.feature b/tests/tck/features/go/GroupbyLimit.feature
index 6445ce97..fb2a65cb 100644
--- a/tests/tck/features/go/GroupbyLimit.feature
+++ b/tests/tck/features/go/GroupbyLimit.feature
@@ -193,7 +193,7 @@ Feature: Groupby & limit Sentence
     Then a SemanticError should be raised at runtime:
     When executing query:
       """
-      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count, 1+1 AS cal
       """
     Then the result should be, in any order, with relax comparison:
@@ -290,7 +290,7 @@ Feature: Groupby & limit Sentence
   Scenario: Groupby works with orderby or limit test
     When executing query:
       """
-      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name
       """
     Then the result should be, in order, with relax comparison:
@@ -301,7 +301,7 @@ Feature: Groupby & limit Sentence
       | "LeBron James"    | 3.0 | 2     |
     When executing query:
       """
-      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM "Carmelo Anthony","Dwyane Wade" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum, $-.name DESC | LIMIT 2
       """
     Then the result should be, in order, with relax comparison:
@@ -318,7 +318,7 @@ Feature: Groupby & limit Sentence
     Then a SemanticError should be raised at runtime:
     When executing query:
       """
-      GO FROM "NON EXIST VERTEX ID" OVER like YIELD $$.player.name AS name | GROUP BY $-.name, abs(5)
+      GO FROM "NON EXIST VERTEX ID" OVER like YIELD $$.player.name AS name | GROUP BY $-.name
       YIELD $-.name AS name, SUM(1.5) AS sum, COUNT(*) AS count | ORDER BY $-.sum | LIMIT 2
       """
     Then the result should be, in order, with relax comparison:
-- 
GitLab