diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp
index b3a972022..cac2021d9 100644
--- a/src/query/frontend/ast/ast.hpp
+++ b/src/query/frontend/ast/ast.hpp
@@ -363,6 +363,24 @@ class PropertyLookup : public Expression {
       : Expression(uid), expression_(expression), property_(property) {}
 };
 
+class Aggregation : public UnaryOperator {
+  friend class AstTreeStorage;
+
+ public:
+  enum class Op { COUNT, MIN, MAX, SUM, AVG };
+  Op op_;
+
+  void Accept(TreeVisitorBase &visitor) override {
+    visitor.Visit(*this);
+    expression_->Accept(visitor);
+    visitor.PostVisit(*this);
+  }
+
+ protected:
+  Aggregation(int uid, Expression *expression, Op op)
+      : UnaryOperator(uid, expression), op_(op) {}
+};
+
 class NamedExpression : public Tree {
   friend class AstTreeStorage;
 
diff --git a/src/query/frontend/ast/ast_visitor.hpp b/src/query/frontend/ast/ast_visitor.hpp
index 871f32c84..4ddc17583 100644
--- a/src/query/frontend/ast/ast_visitor.hpp
+++ b/src/query/frontend/ast/ast_visitor.hpp
@@ -9,6 +9,7 @@ class Query;
 class NamedExpression;
 class Identifier;
 class PropertyLookup;
+class Aggregation;
 class Create;
 class Match;
 class Return;
@@ -48,6 +49,7 @@ using TreeVisitorBase = ::utils::Visitor<
     DivisionOperator, ModOperator, NotEqualOperator, EqualOperator,
     LessOperator, GreaterOperator, LessEqualOperator, GreaterEqualOperator,
     UnaryPlusOperator, UnaryMinusOperator, Identifier, Literal, PropertyLookup,
-    Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom, Delete, Where,
-    SetProperty, SetProperties, SetLabels, RemoveProperty, RemoveLabels>;
+    Aggregation, Create, Match, Return, With, Pattern, NodeAtom, EdgeAtom,
+    Delete, Where, SetProperty, SetProperties, SetLabels, RemoveProperty,
+    RemoveLabels>;
 }
diff --git a/src/query/frontend/logical/operator.cpp b/src/query/frontend/logical/operator.cpp
index 036d9a9e6..e6ccfbc42 100644
--- a/src/query/frontend/logical/operator.cpp
+++ b/src/query/frontend/logical/operator.cpp
@@ -905,5 +905,20 @@ bool Accumulate::AccumulateCursor::Pull(Frame &frame,
   return true;
 }
 
+Aggregate::Aggregate(const std::shared_ptr<LogicalOperator> &input,
+                     const std::vector<Aggregate::Element> &aggregations,
+                     std::vector<NamedExpression *> group_by)
+    : input_(input), aggregations_(aggregations), group_by_(group_by) {}
+
+void Aggregate::Accept(LogicalOperatorVisitor &visitor) {
+  visitor.Visit(*this);
+  input_->Accept(visitor);
+  visitor.PostVisit(*this);
+}
+
+std::unique_ptr<Cursor> Aggregate::MakeCursor(GraphDbAccessor &db) {
+  return std::unique_ptr<Cursor>();
+}
+
 }  // namespace plan
 }  // namespace query
diff --git a/src/query/frontend/logical/operator.hpp b/src/query/frontend/logical/operator.hpp
index dad258df5..c61be1735 100644
--- a/src/query/frontend/logical/operator.hpp
+++ b/src/query/frontend/logical/operator.hpp
@@ -56,13 +56,16 @@ template <typename TAccessor>
 class ExpandUniquenessFilter;
 class Accumulate;
 class AdvanceCommand;
+class Aggregate;
 
 /** @brief Base class for visitors of @c LogicalOperator class hierarchy. */
-using LogicalOperatorVisitor = ::utils::Visitor<
-    CreateNode, CreateExpand, ScanAll, Expand, NodeFilter, EdgeFilter, Filter,
-    Produce, Delete, SetProperty, SetProperties, SetLabels, RemoveProperty,
-    RemoveLabels, ExpandUniquenessFilter<VertexAccessor>,
-    ExpandUniquenessFilter<EdgeAccessor>, Accumulate, AdvanceCommand>;
+using LogicalOperatorVisitor =
+    ::utils::Visitor<CreateNode, CreateExpand, ScanAll, Expand, NodeFilter,
+                     EdgeFilter, Filter, Produce, Delete, SetProperty,
+                     SetProperties, SetLabels, RemoveProperty, RemoveLabels,
+                     ExpandUniquenessFilter<VertexAccessor>,
+                     ExpandUniquenessFilter<EdgeAccessor>, Accumulate,
+                     AdvanceCommand, Aggregate>;
 
 /** @brief Base class for logical operators.
  *
@@ -771,8 +774,8 @@ class ExpandUniquenessFilter : public LogicalOperator {
  */
 class Accumulate : public LogicalOperator {
  public:
-  Accumulate(std::shared_ptr<LogicalOperator> input, const std::vector<Symbol> &symbols,
-             bool advance_command=false);
+  Accumulate(std::shared_ptr<LogicalOperator> input,
+             const std::vector<Symbol> &symbols, bool advance_command = false);
   void Accept(LogicalOperatorVisitor &visitor) override;
   std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override;
 
@@ -785,6 +788,7 @@ class Accumulate : public LogicalOperator {
    public:
     AccumulateCursor(Accumulate &self, GraphDbAccessor &db);
     bool Pull(Frame &frame, SymbolTable &symbol_table) override;
+
    private:
     Accumulate &self_;
     GraphDbAccessor &db_;
@@ -795,5 +799,38 @@ class Accumulate : public LogicalOperator {
   };
 };
 
+/** @brief Performs an arbitrary number of aggregations of data
+ * from the given input grouped by the given criteria.
+ *
+ * Aggregations are defined by triples that define
+ * (input data expression, type of aggregation, output symbol).
+ * Input data is grouped based on the given set of named
+ * expressions. Grouping is done on unique values.
+ *
+ * IMPORTANT:
+ * Ops taking their input from an aggregation are only
+ * allowed to use frame values that are either aggregation
+ * outputs or group-by named-expressions. All other frame
+ * elements are in an undefined state after aggregation.
+ */
+class Aggregate : public LogicalOperator {
+ public:
+  /** @brief An aggregation element, contains:
+   * (input data expression, type of aggregation, output symbol).
+   */
+  using Element = std::tuple<Expression *, Aggregation::Op, Symbol>;
+
+  Aggregate(const std::shared_ptr<LogicalOperator> &input,
+            const std::vector<Element> &aggregations,
+            std::vector<NamedExpression *> group_by);
+  void Accept(LogicalOperatorVisitor &visitor) override;
+  std::unique_ptr<Cursor> MakeCursor(GraphDbAccessor &db) override;
+
+ private:
+  std::shared_ptr<LogicalOperator> input_;
+  std::vector<Element> aggregations_;
+  std::vector<NamedExpression *> group_by_;
+};
+
 }  // namespace plan
 }  // namespace query