diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp
index 48464d097..6a51c5498 100644
--- a/src/query/frontend/ast/ast.hpp
+++ b/src/query/frontend/ast/ast.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include <map>
 #include <memory>
 #include <vector>
 
@@ -13,6 +14,7 @@ class AstTreeStorage;
 
 class Tree : public ::utils::Visitable<TreeVisitorBase> {
   friend class AstTreeStorage;
+
  public:
   int uid() const { return uid_; }
 
@@ -30,6 +32,7 @@ class Expression : public Tree {
 
 class Identifier : public Expression {
   friend class AstTreeStorage;
+
  public:
   DEFVISITABLE(TreeVisitorBase);
   std::string name_;
@@ -40,6 +43,7 @@ class Identifier : public Expression {
 
 class PropertyLookup : public Expression {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -57,13 +61,13 @@ class PropertyLookup : public Expression {
   // between the two depending on Expression result
 
  protected:
-  PropertyLookup(int uid, Expression* expression,
-                 GraphDb::Property property)
+  PropertyLookup(int uid, Expression *expression, GraphDb::Property property)
       : Expression(uid), expression_(expression), property_(property) {}
 };
 
 class NamedExpression : public Tree {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -72,18 +76,20 @@ class NamedExpression : public Tree {
   }
 
   std::string name_;
-  Expression* expression_ = nullptr;
+  Expression *expression_ = nullptr;
 
  protected:
   NamedExpression(int uid) : Tree(uid) {}
-  NamedExpression(int uid, std::string name, Expression *expression) :
-      Tree(uid), name_(name), expression_(expression) {}
+  NamedExpression(int uid, std::string name, Expression *expression)
+      : Tree(uid), name_(name), expression_(expression) {}
 };
 
 class PatternAtom : public Tree {
   friend class AstTreeStorage;
+
  public:
-  Identifier* identifier_ = nullptr;
+  Identifier *identifier_ = nullptr;
+
  protected:
   PatternAtom(int uid) : Tree(uid) {}
   PatternAtom(int uid, Identifier *identifier)
@@ -92,6 +98,7 @@ class PatternAtom : public Tree {
 
 class NodeAtom : public PatternAtom {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -100,7 +107,8 @@ class NodeAtom : public PatternAtom {
   }
 
   std::vector<GraphDb::Label> labels_;
-  std::map<GraphDb::Property, Expression*> properties_;
+  // TODO: change to unordered_map
+  std::map<GraphDb::Property, Expression *> properties_;
 
  protected:
   using PatternAtom::PatternAtom;
@@ -108,6 +116,7 @@ class NodeAtom : public PatternAtom {
 
 class EdgeAtom : public PatternAtom {
   friend class AstTreeStorage;
+
  public:
   enum class Direction { LEFT, RIGHT, BOTH };
 
@@ -119,6 +128,8 @@ class EdgeAtom : public PatternAtom {
 
   Direction direction_ = Direction::BOTH;
   std::vector<GraphDb::EdgeType> types_;
+  // TODO: change to unordered_map
+  std::map<GraphDb::Property, Expression *> properties_;
 
  protected:
   using PatternAtom::PatternAtom;
@@ -126,12 +137,14 @@ class EdgeAtom : public PatternAtom {
 
 class Clause : public Tree {
   friend class AstTreeStorage;
+
  public:
   Clause(int uid) : Tree(uid) {}
 };
 
 class Pattern : public Tree {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -140,8 +153,8 @@ class Pattern : public Tree {
     }
     visitor.PostVisit(*this);
   }
-  Identifier* identifier_ = nullptr;
-  std::vector<PatternAtom*> atoms_;
+  Identifier *identifier_ = nullptr;
+  std::vector<PatternAtom *> atoms_;
 
  protected:
   Pattern(int uid) : Tree(uid) {}
@@ -149,6 +162,7 @@ class Pattern : public Tree {
 
 class Query : public Tree {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -157,16 +171,18 @@ class Query : public Tree {
     }
     visitor.PostVisit(*this);
   }
-  std::vector<Clause*> clauses_;
+  std::vector<Clause *> clauses_;
 
  protected:
   Query(int uid) : Tree(uid) {}
 };
 
 class Create : public Clause {
+  friend class AstTreeStorage;
+
  public:
   Create(int uid) : Clause(uid) {}
-  std::vector<Pattern*> patterns_;
+  std::vector<Pattern *> patterns_;
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
     for (auto &pattern : patterns_) {
@@ -178,8 +194,9 @@ class Create : public Clause {
 
 class Match : public Clause {
   friend class AstTreeStorage;
+
  public:
-  std::vector<Pattern*> patterns_;
+  std::vector<Pattern *> patterns_;
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
     for (auto &pattern : patterns_) {
@@ -194,6 +211,7 @@ class Match : public Clause {
 
 class Return : public Clause {
   friend class AstTreeStorage;
+
  public:
   void Accept(TreeVisitorBase &visitor) override {
     visitor.Visit(*this);
@@ -202,7 +220,7 @@ class Return : public Clause {
     }
     visitor.PostVisit(*this);
   }
-  std::vector<NamedExpression*> named_expressions_;
+  std::vector<NamedExpression *> named_expressions_;
 
  protected:
   Return(int uid) : Clause(uid) {}
@@ -215,14 +233,12 @@ class AstTreeStorage {
   friend class AstTreeStorage;
 
  public:
-  AstTreeStorage() {
-    storage_.emplace_back(new Query(next_uid_++));
-  }
+  AstTreeStorage() { storage_.emplace_back(new Query(next_uid_++)); }
   AstTreeStorage(const AstTreeStorage &) = delete;
   AstTreeStorage &operator=(const AstTreeStorage &) = delete;
 
-  template<typename T, typename... Args>
-  T *Create(Args&&... args) {
+  template <typename T, typename... Args>
+  T *Create(Args &&... args) {
     // Never call create for a Query. Call query() instead.
     static_assert(!std::is_same<T, Query>::value, "Call query() instead");
     // TODO: use std::forward here
@@ -231,7 +247,7 @@ class AstTreeStorage {
     return p;
   }
 
-  Query *query() { return dynamic_cast<Query*>(storage_[0].get()); }
+  Query *query() { return dynamic_cast<Query *>(storage_[0].get()); }
 
  private:
   int next_uid_ = 0;
diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp
index 1df03b15f..5fa80a7f3 100644
--- a/src/query/frontend/ast/cypher_main_visitor.cpp
+++ b/src/query/frontend/ast/cypher_main_visitor.cpp
@@ -41,8 +41,8 @@ namespace {
 
 const std::string CypherMainVisitor::kAnonPrefix = "anon";
 
-antlrcpp::Any
-CypherMainVisitor::visitSingleQuery(CypherParser::SingleQueryContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitSingleQuery(
+    CypherParser::SingleQueryContext *ctx) {
   query_ = storage_.query();
   for (auto *child : ctx->clause()) {
     query_->clauses_.push_back(child->accept(this));
@@ -68,12 +68,15 @@ antlrcpp::Any CypherMainVisitor::visitClause(CypherParser::ClauseContext *ctx) {
   if (ctx->cypherMatch()) {
     return (Clause *)ctx->cypherMatch()->accept(this).as<Match *>();
   }
+  if (ctx->create()) {
+    return (Clause *)ctx->create()->accept(this).as<Create *>();
+  }
   throw std::exception();
   return visitChildren(ctx);
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitCypherMatch(CypherParser::CypherMatchContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitCypherMatch(
+    CypherParser::CypherMatchContext *ctx) {
   auto *match = storage_.Create<Match>();
   if (ctx->OPTIONAL() || ctx->where()) {
     throw std::exception();
@@ -82,24 +85,31 @@ CypherMainVisitor::visitCypherMatch(CypherParser::CypherMatchContext *ctx) {
   return match;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitCypherReturn(CypherParser::CypherReturnContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitCreate(CypherParser::CreateContext *ctx) {
+  auto *create = storage_.Create<Create>();
+  create->patterns_ = ctx->pattern()->accept(this).as<std::vector<Pattern *>>();
+  return create;
+  ;
+}
+
+antlrcpp::Any CypherMainVisitor::visitCypherReturn(
+    CypherParser::CypherReturnContext *ctx) {
   if (ctx->DISTINCT()) {
     throw std::exception();
   }
   return visitChildren(ctx);
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitReturnBody(CypherParser::ReturnBodyContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitReturnBody(
+    CypherParser::ReturnBodyContext *ctx) {
   if (ctx->order() || ctx->skip() || ctx->limit()) {
     throw std::exception();
   }
   return ctx->returnItems()->accept(this);
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitReturnItems(CypherParser::ReturnItemsContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitReturnItems(
+    CypherParser::ReturnItemsContext *ctx) {
   auto *return_clause = storage_.Create<Return>();
   if (ctx->getTokens(kReturnAllTokenId).size()) {
     throw std::exception();
@@ -110,8 +120,8 @@ CypherMainVisitor::visitReturnItems(CypherParser::ReturnItemsContext *ctx) {
   return return_clause;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitReturnItem(CypherParser::ReturnItemContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitReturnItem(
+    CypherParser::ReturnItemContext *ctx) {
   auto *named_expr = storage_.Create<NamedExpression>();
   if (ctx->variable()) {
     named_expr->name_ =
@@ -124,8 +134,8 @@ CypherMainVisitor::visitReturnItem(CypherParser::ReturnItemContext *ctx) {
   return named_expr;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitNodePattern(CypherParser::NodePatternContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitNodePattern(
+    CypherParser::NodePatternContext *ctx) {
   auto *node = storage_.Create<NodeAtom>();
   if (ctx->variable()) {
     std::string variable = ctx->variable()->accept(this);
@@ -139,17 +149,15 @@ CypherMainVisitor::visitNodePattern(CypherParser::NodePatternContext *ctx) {
         ctx->nodeLabels()->accept(this).as<std::vector<GraphDb::Label>>();
   }
   if (ctx->properties()) {
-    throw std::exception();
-    //    node.properties = ctx->properties()
-    //                          ->accept(this)
-    //                          .as<std::unordered_map<std::string,
-    //                          std::string>>();
+    node->properties_ = ctx->properties()
+                            ->accept(this)
+                            .as<std::map<GraphDb::Property, Expression *>>();
   }
   return node;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitNodeLabels(CypherParser::NodeLabelsContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitNodeLabels(
+    CypherParser::NodeLabelsContext *ctx) {
   std::vector<GraphDb::Label> labels;
   for (auto *node_label : ctx->nodeLabel()) {
     labels.push_back(ctx_.db_accessor_.label(node_label->accept(this)));
@@ -157,31 +165,35 @@ CypherMainVisitor::visitNodeLabels(CypherParser::NodeLabelsContext *ctx) {
   return labels;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitProperties(CypherParser::PropertiesContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitProperties(
+    CypherParser::PropertiesContext *ctx) {
   if (!ctx->mapLiteral()) {
     // If child is not mapLiteral that means child is params. At the moment
-    // memgraph doesn't support params.
+    // we don't support properties to be a param because we can generate
+    // better logical plan if we have an information about properties at
+    // compile time.
     throw std::exception();
   }
   return ctx->mapLiteral()->accept(this);
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitMapLiteral(CypherParser::MapLiteralContext *ctx) {
-  throw std::exception();
-  (void)ctx;
-  return 0;
-  //  std::unordered_map<std::string, std::string> map;
-  //  for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) {
-  //    map[ctx->propertyKeyName()[i]->accept(this).as<std::string>()] =
-  //        ctx->expression()[i]->accept(this).as<std::string>();
-  //  }
-  //  return map;
+antlrcpp::Any CypherMainVisitor::visitMapLiteral(
+    CypherParser::MapLiteralContext *ctx) {
+  std::map<GraphDb::Property, Expression *> map;
+  for (int i = 0; i < (int)ctx->propertyKeyName().size(); ++i) {
+    map[ctx->propertyKeyName()[i]->accept(this)] =
+        ctx->expression()[i]->accept(this);
+  }
+  return map;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitSymbolicName(CypherParser::SymbolicNameContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitPropertyKeyName(
+    CypherParser::PropertyKeyNameContext *ctx) {
+  return ctx_.db_accessor_.property(visitChildren(ctx));
+}
+
+antlrcpp::Any CypherMainVisitor::visitSymbolicName(
+    CypherParser::SymbolicNameContext *ctx) {
   if (ctx->EscapedSymbolicName()) {
     // We don't allow at this point for variable to be EscapedSymbolicName
     // because we would have t ofigure out how escaping works since same
@@ -192,8 +204,8 @@ CypherMainVisitor::visitSymbolicName(CypherParser::SymbolicNameContext *ctx) {
   return std::string(ctx->getText());
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitPattern(CypherParser::PatternContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitPattern(
+    CypherParser::PatternContext *ctx) {
   std::vector<Pattern *> patterns;
   for (auto *pattern_part : ctx->patternPart()) {
     patterns.push_back(pattern_part->accept(this));
@@ -201,8 +213,8 @@ CypherMainVisitor::visitPattern(CypherParser::PatternContext *ctx) {
   return patterns;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitPatternPart(CypherParser::PatternPartContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitPatternPart(
+    CypherParser::PatternPartContext *ctx) {
   Pattern *pattern = ctx->anonymousPatternPart()->accept(this);
   if (ctx->variable()) {
     std::string variable = ctx->variable()->accept(this);
@@ -298,8 +310,8 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipTypes(
   return types;
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitRangeLiteral(
+    CypherParser::RangeLiteralContext *ctx) {
   if (ctx->integerLiteral().size() == 0U) {
     // -[*]-
     return std::pair<int64_t, int64_t>(1LL, LLONG_MAX);
@@ -326,8 +338,8 @@ CypherMainVisitor::visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) {
   }
 }
 
-antlrcpp::Any
-CypherMainVisitor::visitExpression(CypherParser::ExpressionContext *ctx) {
+antlrcpp::Any CypherMainVisitor::visitExpression(
+    CypherParser::ExpressionContext *ctx) {
   return visitChildren(ctx);
 }
 
@@ -504,23 +516,22 @@ CypherMainVisitor::visitExpression(CypherParser::ExpressionContext *ctx) {
 //  }
 //  return visitChildren(ctx);
 //}
-//
-// antlrcpp::Any
-// CypherMainVisitor::visitExpression2(CypherParser::Expression2Context *ctx) {
-//  if (ctx->nodeLabels().size()) {
-//    // TODO: Implement this. We don't currently support label checking in
-//    // expresssion.
-//    throw SemanticException();
-//  }
-//  auto operand = ctx->atom()->accept(this).as<std::string>();
-//  for (int i = 0; i < (int)ctx->propertyLookup().size(); ++i) {
-//    auto lhs_id = new_id();
-//    symbol_table_[lhs_id] =
-//        SimpleExpression{Function::PROPERTY_GETTER, {operand}};
-//    operand = lhs_id;
-//  }
-//  return operand;
-//}
+
+antlrcpp::Any CypherMainVisitor::visitExpression2(
+    CypherParser::Expression2Context *ctx) {
+  if (ctx->nodeLabels().size()) {
+    // TODO: Implement this. We don't currently support label checking in
+    // expresssion.
+    throw std::exception();
+  }
+  Expression *expression = ctx->atom()->accept(this);
+  for (auto *lookup : ctx->propertyLookup()) {
+    auto property_lookup =
+        storage_.Create<PropertyLookup>(expression, lookup->accept(this));
+    expression = property_lookup;
+  }
+  return expression;
+}
 
 antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
   if (ctx->literal()) {
@@ -555,7 +566,7 @@ antlrcpp::Any CypherMainVisitor::visitAtom(CypherParser::AtomContext *ctx) {
   } else if (ctx->variable()) {
     std::string variable = ctx->variable()->accept(this);
     users_identifiers.insert(variable);
-    return storage_.Create<Identifier>(variable);
+    return (Expression *)storage_.Create<Identifier>(variable);
   }
   // TODO: Implement this. We don't support comprehensions, functions,
   // filtering... at the moment.
diff --git a/src/query/frontend/ast/cypher_main_visitor.hpp b/src/query/frontend/ast/cypher_main_visitor.hpp
index 29515a990..f38dfe181 100644
--- a/src/query/frontend/ast/cypher_main_visitor.hpp
+++ b/src/query/frontend/ast/cypher_main_visitor.hpp
@@ -15,10 +15,10 @@ using query::Context;
 using antlropencypher::CypherParser;
 
 class CypherMainVisitor : public antlropencypher::CypherBaseVisitor {
-public:
+ public:
   CypherMainVisitor(Context &ctx) : ctx_(ctx) {}
 
-private:
+ private:
   //  template <typename TExpression>
   //  antlrcpp::Any
   //  LeftAssociativeOperatorExpression(std::vector<TExpression *> children,
@@ -49,90 +49,109 @@ private:
   //        children, std::vector<Function>((int)children.size() - 1, op));
   //  }
 
-  antlrcpp::Any
-  visitSingleQuery(CypherParser::SingleQueryContext *ctx) override;
+  /**
+   * @return Query*
+   */
+  antlrcpp::Any visitSingleQuery(
+      CypherParser::SingleQueryContext *ctx) override;
 
+  /**
+   * @return Clause*
+   */
   antlrcpp::Any visitClause(CypherParser::ClauseContext *ctx) override;
 
-  antlrcpp::Any
-  visitCypherMatch(CypherParser::CypherMatchContext *ctx) override;
+  /**
+   * @return Match*
+   */
+  antlrcpp::Any visitCypherMatch(
+      CypherParser::CypherMatchContext *ctx) override;
 
-  antlrcpp::Any
-  visitCypherReturn(CypherParser::CypherReturnContext *ctx) override;
+  /**
+   * @return Create*
+   */
+  antlrcpp::Any visitCreate(CypherParser::CreateContext *ctx) override;
 
+  /**
+   * @return Return*
+   */
+  antlrcpp::Any visitCypherReturn(
+      CypherParser::CypherReturnContext *ctx) override;
+
+  /**
+   * @return Return*
+   */
   antlrcpp::Any visitReturnBody(CypherParser::ReturnBodyContext *ctx) override;
 
-  antlrcpp::Any
-  visitReturnItems(CypherParser::ReturnItemsContext *ctx) override;
+  /**
+   * @return Return*
+   */
+  antlrcpp::Any visitReturnItems(
+      CypherParser::ReturnItemsContext *ctx) override;
 
+  /**
+   * @return NamedExpression*
+   */
   antlrcpp::Any visitReturnItem(CypherParser::ReturnItemContext *ctx) override;
 
   /**
-  * Creates Node and stores it in symbol_table_. If variable is defined it is
-  * stored in ids_map_.
-  *
-  * @return string - node id.
-  */
-  antlrcpp::Any
-  visitNodePattern(CypherParser::NodePatternContext *ctx) override;
+   * @return NodeAtom*
+   */
+  antlrcpp::Any visitNodePattern(
+      CypherParser::NodePatternContext *ctx) override;
 
   /**
-  * @return vector<string> labels.
-  */
+   * @return vector<GraphDb::Label>
+   */
   antlrcpp::Any visitNodeLabels(CypherParser::NodeLabelsContext *ctx) override;
 
   /**
-  * @return unordered_map<string, string> properties - property key to
-  * expression id.
-  */
+   * @return unordered_map<GraphDb::Property, Expression*>
+   */
   antlrcpp::Any visitProperties(CypherParser::PropertiesContext *ctx) override;
 
   /**
-  * @return unordered_map<string, string> map - key to expression id.
-  */
+   * @return unordered_map<GraphDb::Property, Expression*>
+   */
   antlrcpp::Any visitMapLiteral(CypherParser::MapLiteralContext *ctx) override;
 
   /**
-  * @return string.
-  */
-  antlrcpp::Any
-  visitSymbolicName(CypherParser::SymbolicNameContext *ctx) override;
+   * @return GraphDb::Property
+   */
+  antlrcpp::Any visitPropertyKeyName(
+      CypherParser::PropertyKeyNameContext *ctx) override;
 
   /**
-  * @return vector<PatternPart> pattern.
-  */
+   * @return string
+   */
+  antlrcpp::Any visitSymbolicName(
+      CypherParser::SymbolicNameContext *ctx) override;
+
+  /**
+   * @return vector<Pattern*>
+   */
   antlrcpp::Any visitPattern(CypherParser::PatternContext *ctx) override;
 
   /**
-  * Stores PatternPart in symbol_table_. If variable is defined it is stored
-  *in
-  * ids_map_.
-  *
-  * @return string - pattern part id.
-  */
-  antlrcpp::Any
-  visitPatternPart(CypherParser::PatternPartContext *ctx) override;
+   * @return Pattern*
+   */
+  antlrcpp::Any visitPatternPart(
+      CypherParser::PatternPartContext *ctx) override;
 
   /**
-  * Creates PatternPart.
-  *
-  * @return PatternPart.
-  */
-  antlrcpp::Any
-  visitPatternElement(CypherParser::PatternElementContext *ctx) override;
+   * @return Pattern*
+   */
+  antlrcpp::Any visitPatternElement(
+      CypherParser::PatternElementContext *ctx) override;
 
   /**
-  * @return pair<string, string> - node and relationship ids.
-  */
+   * @return vector<pair<EdgeAtom*, NodeAtom*>>
+   */
   antlrcpp::Any visitPatternElementChain(
       CypherParser::PatternElementChainContext *ctx) override;
 
   /**
-  * Creates Relationship and stores it in symbol_table_. If variable is defined
-  * it is stored in symbol_table_.
-  *
-  * @return string - relationship id.
-  */
+   *@return EdgeAtom*
+   */
   antlrcpp::Any visitRelationshipPattern(
       CypherParser::RelationshipPatternContext *ctx) override;
 
@@ -142,23 +161,24 @@ private:
   */
   antlrcpp::Any visitRelationshipDetail(
       CypherParser::RelationshipDetailContext *ctx) override;
+
   /**
-  * @return vector<string>.
-  */
-  antlrcpp::Any
-  visitRelationshipTypes(CypherParser::RelationshipTypesContext *ctx) override;
+   * @return vector<GraphDb::EdgeType>
+   */
+  antlrcpp::Any visitRelationshipTypes(
+      CypherParser::RelationshipTypesContext *ctx) override;
 
   /**
   * @return pair<int64_t, int64_t>.
   */
-  antlrcpp::Any
-  visitRangeLiteral(CypherParser::RangeLiteralContext *ctx) override;
+  antlrcpp::Any visitRangeLiteral(
+      CypherParser::RangeLiteralContext *ctx) override;
 
   /**
-  * Top level expression.
-  *
-  * @return string - expression id.
-  */
+   * Top level expression, does nothing.
+   *
+   * @return Expression*
+   */
   antlrcpp::Any visitExpression(CypherParser::ExpressionContext *ctx) override;
 
   ///**
@@ -248,18 +268,18 @@ private:
   // antlrcpp::Any
   // visitExpression3(CypherParser::Expression3Context *ctx) override;
 
-  ///**
-  //* Property lookup, test for node labels existence...
-  //*
-  //* @return string - expression id.
-  //*/
-  // antlrcpp::Any
-  // visitExpression2(CypherParser::Expression2Context *ctx) override;
+  /**
+  * Property lookup, test for node labels existence...
+  *
+  * @return Expression*
+  */
+  antlrcpp::Any visitExpression2(
+      CypherParser::Expression2Context *ctx) override;
 
   /**
   * Literals, params, list comprehension...
   *
-  * @return string - expression id.
+  * @return Expression*
   */
   antlrcpp::Any visitAtom(CypherParser::AtomContext *ctx) override;
 
@@ -285,14 +305,14 @@ private:
   /**
   * @return int64_t.
   */
-  antlrcpp::Any
-  visitIntegerLiteral(CypherParser::IntegerLiteralContext *ctx) override;
+  antlrcpp::Any visitIntegerLiteral(
+      CypherParser::IntegerLiteralContext *ctx) override;
 
-public:
+ public:
   Query *query() { return query_; }
   const static std::string kAnonPrefix;
 
-private:
+ private:
   Context &ctx_;
   // Set of identifiers from queries.
   std::unordered_set<std::string> users_identifiers;
diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp
index 65ae3463e..d12f7a1d7 100644
--- a/tests/unit/cypher_main_visitor.cpp
+++ b/tests/unit/cypher_main_visitor.cpp
@@ -7,10 +7,10 @@
 #include "antlr4-runtime.h"
 #include "dbms/dbms.hpp"
 #include "gmock/gmock.h"
+#include "gtest/gtest.h"
 #include "query/context.hpp"
 #include "query/frontend/ast/cypher_main_visitor.hpp"
 #include "query/frontend/opencypher/parser.hpp"
-#include "gtest/gtest.h"
 
 namespace {
 
@@ -19,11 +19,15 @@ using namespace query::frontend;
 using testing::UnorderedElementsAre;
 
 class AstGenerator {
-public:
+ public:
   AstGenerator(const std::string &query)
-      : dbms_(), db_accessor_(dbms_.active()),
-        context_(Config{}, *db_accessor_), query_string_(query), parser_(query),
-        visitor_(context_), query_([&]() {
+      : dbms_(),
+        db_accessor_(dbms_.active()),
+        context_(Config{}, *db_accessor_),
+        query_string_(query),
+        parser_(query),
+        visitor_(context_),
+        query_([&]() {
           visitor_.visit(parser_.tree());
           return visitor_.query();
         }()) {}
@@ -37,11 +41,11 @@ public:
   Query *query_;
 };
 
-TEST(CompilerStructuresTest, SyntaxException) {
+TEST(CypherMainVisitorTest, SyntaxException) {
   ASSERT_THROW(AstGenerator("CREATE ()-[*1...2]-()"), std::exception);
 }
 
-TEST(CompilerStructuresTest, NodePattern) {
+TEST(CypherMainVisitorTest, NodePattern) {
   AstGenerator ast_generator("MATCH (:label1:label2:label3)");
   auto *query = ast_generator.query_;
   ASSERT_EQ(query->clauses_.size(), 1U);
@@ -62,7 +66,7 @@ TEST(CompilerStructuresTest, NodePattern) {
   // TODO: add test for properties.
 }
 
-TEST(CompilerStructuresTest, NodePatternIdentifier) {
+TEST(CypherMainVisitorTest, NodePatternIdentifier) {
   AstGenerator ast_generator("MATCH (var)");
   auto *query = ast_generator.query_;
   auto *match = dynamic_cast<Match *>(query->clauses_[0]);
@@ -73,7 +77,7 @@ TEST(CompilerStructuresTest, NodePatternIdentifier) {
   // TODO: add test for properties.
 }
 
-TEST(CompilerStructuresTest, RelationshipPatternNoDetails) {
+TEST(CypherMainVisitorTest, RelationshipPatternNoDetails) {
   AstGenerator ast_generator("MATCH ()--()");
   auto *query = ast_generator.query_;
   auto *match = dynamic_cast<Match *>(query->clauses_[0]);
@@ -92,7 +96,7 @@ TEST(CompilerStructuresTest, RelationshipPatternNoDetails) {
               CypherMainVisitor::kAnonPrefix + std::to_string(2));
 }
 
-TEST(CompilerStructuresTest, RelationshipPatternDetails) {
+TEST(CypherMainVisitorTest, RelationshipPatternDetails) {
   AstGenerator ast_generator("MATCH ()<-[:type1|type2]-()");
   auto *query = ast_generator.query_;
   auto *match = dynamic_cast<Match *>(query->clauses_[0]);
@@ -105,7 +109,7 @@ TEST(CompilerStructuresTest, RelationshipPatternDetails) {
   // TODO: test properties
 }
 
-TEST(CompilerStructuresTest, RelationshipPatternVariable) {
+TEST(CypherMainVisitorTest, RelationshipPatternVariable) {
   AstGenerator ast_generator("MATCH ()-[var]->()");
   auto *query = ast_generator.query_;
   auto *match = dynamic_cast<Match *>(query->clauses_[0]);
@@ -116,7 +120,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 }
 
 // // Relationship with unbounded variable range.
-// TEST(CompilerStructuresTest, RelationshipPatternUnbounded) {
+// TEST(CypherMainVisitorTest, RelationshipPatternUnbounded) {
 //   ParserTables parser("CREATE ()-[*]-()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.relationships_.size(), 1U);
@@ -126,7 +130,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Relationship with lower bounded variable range.
-// TEST(CompilerStructuresTest, RelationshipPatternLowerBounded) {
+// TEST(CypherMainVisitorTest, RelationshipPatternLowerBounded) {
 //   ParserTables parser("CREATE ()-[*5..]-()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.relationships_.size(), 1U);
@@ -136,7 +140,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Relationship with upper bounded variable range.
-// TEST(CompilerStructuresTest, RelationshipPatternUpperBounded) {
+// TEST(CypherMainVisitorTest, RelationshipPatternUpperBounded) {
 //   ParserTables parser("CREATE ()-[*..10]-()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.relationships_.size(), 1U);
@@ -145,7 +149,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Relationship with lower and upper bounded variable range.
-// TEST(CompilerStructuresTest, RelationshipPatternLowerUpperBounded) {
+// TEST(CypherMainVisitorTest, RelationshipPatternLowerUpperBounded) {
 //   ParserTables parser("CREATE ()-[*5..10]-()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.relationships_.size(), 1U);
@@ -154,7 +158,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Relationship with fixed number of edges.
-// TEST(CompilerStructuresTest, RelationshipPatternFixedRange) {
+// TEST(CypherMainVisitorTest, RelationshipPatternFixedRange) {
 //   ParserTables parser("CREATE ()-[*10]-()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.relationships_.size(), 1U);
@@ -163,15 +167,14 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Relationship with invalid bound (larger than long long).
-// TEST(CompilerStructuresTest, RelationshipPatternInvalidBound) {
+// TEST(CypherMainVisitorTest, RelationshipPatternInvalidBound) {
 //   ASSERT_THROW(
 //       ParserTables parser("CREATE ()-[*100000000000000000000000000]-()"),
 //       SemanticException);
 // }
 //
-//
 // // PatternPart.
-// TEST(CompilerStructuresTest, PatternPart) {
+// TEST(CypherMainVisitorTest, PatternPart) {
 //   ParserTables parser("CREATE ()--()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.pattern_parts_.size(), 1U);
@@ -182,7 +185,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // PatternPart in braces.
-// TEST(CompilerStructuresTest, PatternPartBraces) {
+// TEST(CypherMainVisitorTest, PatternPartBraces) {
 //   ParserTables parser("CREATE ((()--()))");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 0U);
 //   ASSERT_EQ(parser.pattern_parts_.size(), 1U);
@@ -193,7 +196,7 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // PatternPart with variable.
-// TEST(CompilerStructuresTest, PatternPartVariable) {
+// TEST(CypherMainVisitorTest, PatternPartVariable) {
 //   ParserTables parser("CREATE var=()--()");
 //   ASSERT_EQ(parser.identifiers_map_.size(), 1U);
 //   ASSERT_EQ(parser.pattern_parts_.size(), 1U);
@@ -209,8 +212,49 @@ TEST(CompilerStructuresTest, RelationshipPatternVariable) {
 // }
 //
 // // Multiple nodes with same variable and properties.
-// TEST(CompilerStructuresTest, MultipleNodesWithVariableAndProperties) {
+// TEST(CypherMainVisitorTest, MultipleNodesWithVariableAndProperties) {
 //   ASSERT_THROW(ParserTables parser("CREATE (a {b: 5})-[]-(a {c: 5})"),
 //                SemanticException);
 // }
+
+TEST(CypherMainVisitorTest, ReturnUnanemdIdentifier) {
+  AstGenerator ast_generator("RETURN var");
+  auto *query = ast_generator.query_;
+  ASSERT_EQ(query->clauses_.size(), 1U);
+  auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
+  ASSERT_TRUE(return_clause);
+  ASSERT_EQ(return_clause->named_expressions_.size(), 1U);
+  auto *named_expr = return_clause->named_expressions_[0];
+  ASSERT_TRUE(named_expr);
+  ASSERT_EQ(named_expr->name_, "var");
+  auto *identifier = dynamic_cast<Identifier *>(named_expr->expression_);
+  ASSERT_TRUE(identifier);
+  ASSERT_EQ(identifier->name_, "var");
+}
+
+TEST(CypherMainVisitorTest, ReturnNamedIdentifier) {
+  AstGenerator ast_generator("RETURN var AS var5");
+  auto *query = ast_generator.query_;
+  auto *return_clause = dynamic_cast<Return *>(query->clauses_[0]);
+  auto *named_expr = return_clause->named_expressions_[0];
+  ASSERT_EQ(named_expr->name_, "var5");
+  auto *identifier = dynamic_cast<Identifier *>(named_expr->expression_);
+  ASSERT_EQ(identifier->name_, "var");
+}
+
+TEST(CypherMainVisitorTest, Create) {
+  AstGenerator ast_generator("CREATE (n)");
+  auto *query = ast_generator.query_;
+  ASSERT_EQ(query->clauses_.size(), 1U);
+  auto *create = dynamic_cast<Create *>(query->clauses_[0]);
+  ASSERT_TRUE(create);
+  ASSERT_EQ(create->patterns_.size(), 1U);
+  ASSERT_TRUE(create->patterns_[0]);
+  ASSERT_EQ(create->patterns_[0]->atoms_.size(), 1U);
+  auto node = dynamic_cast<NodeAtom *>(create->patterns_[0]->atoms_[0]);
+  ASSERT_TRUE(node);
+  ASSERT_TRUE(node->identifier_);
+  ASSERT_EQ(node->identifier_->name_, "n");
+  // TODO: add test for properties.
+}
 }