diff --git a/.gitignore b/.gitignore
index 01f7ec21775f2946e6e4d1b3bab3f68f0f7a8cdf..c3914e63ac5ffa59f64b4c01f7f4971f4903b416 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 3c73016437aee984545ad7b022a417ee9166857d..ab775c1da4c3420a58ab2fdad39cc4abbd0a4e7b 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 201505bcdd5db42bbfda36e0196b7a244a8b3564..fb69079ac099c7e365d80a1107a70c9f896aecb7 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 84ba61913f5418ed83a768f9c81ef02b549008be..3e3e59029f1a59a7bdb2cd75484f06d84913891d 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 d1e8316e0990f0f2c65b6a8ebce7caf37a32d3e9..4e0f8841292b88a8273684ecd3a8a12c97facdfe 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 30b4309a2760edde0d062e56064a7366cb664c68..bb12ce0c4822232d00986382a139aaab86cd77a0 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 a814808d7c9c7f9424bb4f510fd4dc9059507c6d..f2b17693dec199b8e6f688de321b2d40b3b91a89 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 14fe395054389f267d8b8d54007f8e2e9d751fd1..ba3c961f0c0752d096d44de3468bbb09c61d991d 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 bd036555031e825715922d0e5f6d6e730e4a7783..d00285aee4f666b6d33d01ea1ec1dddf409da1f6 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 9044188c9fb64fc92f2895bb17660d8713bce2ec..73104504c33439b6bd59cedd5f3b6ed9b66f5b5b 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 f20b7fb88f9c1744948b9c8c9bf366e9d9e03efa..856f6ca5aafc41700d1785aa2baac587be7bf4fc 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 9c689d634f9186e97cb6e4f7b603342c6588e456..c4312ddc87073651f08596e1894eef271cd95ffd 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 f3baece9cb8a243c7aceaab511971f997ecfa026..e23d4797ae013a1a02d265202894ccd139f6c34c 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 814adbdb79d17b5177428a1e7a6702bf5d05f3f7..6f1c28334d3f46629d09572f307d61cf72130080 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 7d64edd4e4d6fc1a747e7936964d32b5287f621e..7eba4edd355a2ec3be355a1e36d3666ea1ccb716 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 49da92d44eaf8ccd961041fc50a48a8455e995af..5fd931e986e2392190f8cc1ae478997be965811a 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 c9411c7f665cf96d6320ece045e9a2f16df30872..713d752dc8b925c63e0bc37bf4cc6316f857781e 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 223ea6428a7070ec5ac55f2d6a729ee99df596cf..ab6751c0bbf3e3adcbc8e90f91c95faabb57c28e 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 a8db5e6d98e8e2cd0212222ce6ad03d2234b1146..eeba83d03dd9214afa7ee06db6e67bdb3940ba32 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 9e549863bd644129287c5cc7c1abb5003e0da41d..2523024c24b6760d009f490c3a9e29258c1ccadf 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 98ae172999e2b11fe3346c2d35af48c2a4a6587f..d71c6c17cb0acc9c837683c259aacd722098d8bf 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 ea176d82f4710b88ed60b66184aa1706f78367f0..d45746dbae48da79ea7fbb1df77e6da06ba68463 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 6f241a8906cf097213ff13f70710e73f4f16ea96..ae0ffdc6ad354e14a94fa42e22f0d4e93ce61eba 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 9885a46508822df3b09191e3bbb5ff5892f1451d..b59281161b70af4325498850c44c7b6ae704d71c 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 56d5cf3569e0b75945db283e87a4681d913179ec..c8ac22c026bc6ccbd690293b7104a152531bff3c 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 c14a759cabb5b8dadef3a40c2d49ba153a122032..c3ac76f1fcc31c0425a28742d0884f7e87891f29 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 8d175de3b7a94d7c95439dd0cc453a70ccda9b2e..4de8f571785cfa95bd39b15ad84c4ea4a4f9d10b 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 f5b50a91df953911b8d0d1eb6956e6df71cc557d..93b7c108f28362cf5d30e276143f74ae87bc9300 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 af6ea318724f795b3ec6729552e1e73bb4f99a72..243bff7349225f2abfa8e7500d7e2d92e33668d9 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 70023d12b1f23cdd44c238aad876071887e6a61f..501051d05602a73f073cf85db4286959b950d268 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 084add461315013eaad4a9cf8dd97e598e7f3453..260c226ff4a42cd18e276c299ab07fed2085e1b4 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 88087a25d08320870b0050141ab3d6566d8b2f92..6458c3495553fb6a0c0c46cb1de722d4c6e6ce89 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 a7e61a16b0ea7e16a236aeb708459fabc7caefc7..01a11588c3e42dab2f57c99ec874ca6fce458a64 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 7f6366d1a3e105e84e63ea9fb86c14d1f45c29f9..e1599c1e064b59f5f08886ea9f548be7becd3d37 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 ba6aa8daa5b4f485c611675ee66c69fc8175324e..fa6b16c25490bda6b9386f6592cc0badf0d3c264 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 49f2258c94498b695e9b830671dff67c63722af1..1bc42b21e942f86acdb26ed2620ac992e47b2ee1 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 ed35c9cb78d31d9f43cd2386e9ea2f29b4efecd1..fe3e0a9a34bac539cf4014af5b8e104b9fe6d488 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 2a2cfd0aa5f7376a0081f26dffd1f00271c7d9f5..9f549e99fdfd71eceedd08fec2f12f6503ebd668 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 4ebf4db09ff38855d3cbaa4ff710996a00077d3c..2f24b5d7aad75363834b5a5ebfbb064a18629c79 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 c3abfb42c3376c05f857466f6dd55c8bbeceb1e3..6b85c5d618bbbfef7ba807cfa98c001d525410a9 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 8f91bc222621a1add4bf5080e58c71d33833646a..fb53103b03cabb4f91e2d81476ffec89dbacf8b7 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 0000000000000000000000000000000000000000..79dbf8df048ba05f3787128917e67e5b42142cd3
--- /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 0000000000000000000000000000000000000000..c9e33489b51a5452b24834446d520acb67316fb2
--- /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 82184be908e1ed68929a4c6b8fbc8841400b3e8d..a8abe6ac06ea18d434b023b10b5c8387fca5408f 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 a111122b8ae4622e4319c4c3cf15a2cb1c3cf589..18995604ebcfb659f67c269155595cd128e996a7 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 0000000000000000000000000000000000000000..223c42c3ef9e0160fc96f5757fb8ba45970ebdb3
--- /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 0000000000000000000000000000000000000000..598366d5883d2d2e107682d42572081a40ffd0f9
--- /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 bd5db3d2fd781ded158a2055418b3aa27de1632f..b93635ca7dee09e790417bc523d27aba6d054c56 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 b14d363939a051005311ed169b8d75cae50a7cd7..a15a94797ebf9fabdedf1733fc83e2c8924a0513 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 403627aef43beea4e69d14cb2b399c654f9b4407..892c1fd5908f381db5ae8029b0b925383f6cc4f3 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 b37fa3c3db66e87ae7af6d24feaa3a84087060e5..6e14c56fa589c9da5f7f147dcc7173c04868ef97 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 583e5a4d2b1db848ab63f0aa59759c37eb4ce532..231eb9b868b14706f1e0e91fc7dfdcb1221d3aea 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 83842f07f71d3b2f6f21033d49e60642f492e9bd..31c414a513962ce9c7d197c115df0001382f5362 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 0000000000000000000000000000000000000000..d9998f9c83586b9fae89554d8c65f3d9c7b7f287
--- /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 e8b4ae4c46b3fabfc01c41d31105f7ee1010866b..ab7754b541878f098cc164aed90de06ee6ebf8e0 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 e919b03624e75bcd14296dcc165a3d2f9ef3c8fc..ce2ad2ae14e74faaab0965664de95d5cca7de36a 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 6445ce972947fd6c59e6eacd7ec80bd6fd10827a..fb2a65cb49150d885106af0841a63fb82ae33ecc 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: