From d77800b2f97d271c7fe9a49336f7547b018eae64 Mon Sep 17 00:00:00 2001
From: Marko Budiselic <mbudiselicbuda@gmail.com>
Date: Sun, 21 Feb 2016 22:21:15 +0100
Subject: [PATCH] Basic CREATE query works (e.g. CREATE (n {age:25,
 name:"test"}) RETURN n)

---
 dc/dynamic_lib.hpp                            | 22 ++++---
 query_engine/code_compiler.hpp                | 12 ++--
 query_engine/code_executor.hpp                | 27 --------
 query_engine/code_generator.hpp               | 13 ----
 query_engine/compile.sh                       |  1 +
 query_engine/debug.hpp                        | 23 +++++++
 query_engine/i_code_cpu.hpp                   |  3 +-
 query_engine/main.cpp                         | 14 +++-
 query_engine/program_executor.hpp             | 33 ++++++++++
 .../{code_loader.hpp => program_loader.hpp}   | 31 ++++-----
 query_engine/query_engine.hpp                 | 20 +++---
 query_engine/query_program.hpp                | 17 +++++
 query_engine/query_result.hpp                 | 35 ++++++++--
 query_engine/query_stripped.hpp               | 20 ++++++
 query_engine/query_stripper.hpp               | 66 +++++++++++++++----
 query_engine/template/template_code_cpu.cpp   |  3 +-
 query_engine/template/template_code_cpu.hpp   |  9 ---
 query_engine/traverser/create_traverser.hpp   | 40 +++++++----
 query_engine/traverser/node_traverser.hpp     | 49 --------------
 query_engine/util.hpp                         |  8 +++
 storage/edge.hpp                              |  2 +-
 storage/model/properties/properties.hpp       |  6 +-
 storage/record_accessor.hpp                   |  5 ++
 storage/vertex.hpp                            |  2 +-
 utils/time/timer.hpp                          | 19 ++++++
 25 files changed, 302 insertions(+), 178 deletions(-)
 delete mode 100644 query_engine/code_executor.hpp
 create mode 100644 query_engine/debug.hpp
 create mode 100644 query_engine/program_executor.hpp
 rename query_engine/{code_loader.hpp => program_loader.hpp} (75%)
 create mode 100644 query_engine/query_program.hpp
 create mode 100644 query_engine/query_stripped.hpp
 delete mode 100644 query_engine/template/template_code_cpu.hpp
 delete mode 100644 query_engine/traverser/node_traverser.hpp
 create mode 100644 query_engine/util.hpp
 create mode 100644 utils/time/timer.hpp

diff --git a/dc/dynamic_lib.hpp b/dc/dynamic_lib.hpp
index c63b3f64f..7b99b3938 100644
--- a/dc/dynamic_lib.hpp
+++ b/dc/dynamic_lib.hpp
@@ -3,6 +3,11 @@
 #include <string>
 #include <stdexcept>
 #include <dlfcn.h>
+#include <atomic>
+
+#include <iostream>
+using std::cout;
+using std::endl;
 
 template<typename T>
 class DynamicLib
@@ -10,26 +15,25 @@ class DynamicLib
 private:
     using produce_t = typename T::produce;
     using destruct_t = typename T::destruct;
+    std::atomic<uint8_t> counter;
 
 public:
     produce_t produce_method;
     destruct_t destruct_method;
 
     DynamicLib(const std::string& lib_path) :
-        lib_path(lib_path)
+        lib_path(lib_path),
+        lib_object(nullptr)
     {
+        load();
     }
 
-    //  TODO debug why this doesn't work
     typename T::lib_object* instance()
     {
-        //  TODO singleton, lazy, concurrency
-        //  ifs are uncommented -> SEGMENTATION FAULT
-        //  TODO debug
-        // if (dynamic_lib == nullptr)
-            this->load();
-        // if (dynamic_lib == nullptr)
+        //  TODO singleton, concurrency
+        if (lib_object == nullptr) {
             lib_object = this->produce_method();
+        }
         return lib_object;
     }
 
@@ -50,7 +54,7 @@ public:
 private:
     std::string lib_path;
     void *dynamic_lib;
-    typename T::lib_object* lib_object;
+    typename T::lib_object *lib_object;
 
     void load_lib()
     {
diff --git a/query_engine/code_compiler.hpp b/query_engine/code_compiler.hpp
index a2491bdbc..27a4e8c78 100644
--- a/query_engine/code_compiler.hpp
+++ b/query_engine/code_compiler.hpp
@@ -7,15 +7,17 @@
 class CodeCompiler
 {
 public:
+
     void compile(const std::string& in_file, const std::string& out_file)
     {
         auto compile_command = utils::prints(
             "clang++",
-            "-std=c++1y",
-            in_file,
-            "-o", out_file,
-            "-I../",
-            "-shared -fPIC"
+            // "-std=c++1y -O2 -DNDEBUG",     // compile flags
+            "-std=c++1y",     // compile flags
+            in_file,          // input file
+            "-o", out_file,   // ouput file
+            "-I../",          // include paths
+            "-shared -fPIC"   // shared library flags
         );
 
         // synchronous call
diff --git a/query_engine/code_executor.hpp b/query_engine/code_executor.hpp
deleted file mode 100644
index 592e8197e..000000000
--- a/query_engine/code_executor.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-
-#include <string>
-
-#include "i_code_cpu.hpp"
-#include "database/db.hpp"
-#include "utils/log/logger.hpp"
-
-//  preparations before execution
-//  execution
-//  postprocess the results
-
-using std::cout;
-using std::endl;
-
-class CodeExecutor
-{
-public:
-    void execute(ICodeCPU *code_cpu)
-    {
-        auto result = code_cpu->run(db);
-        cout << result->result << endl;
-    }
-
-private:
-    Db db;
-};
diff --git a/query_engine/code_generator.hpp b/query_engine/code_generator.hpp
index 9badb469c..d196e7b8c 100644
--- a/query_engine/code_generator.hpp
+++ b/query_engine/code_generator.hpp
@@ -10,19 +10,6 @@ using std::string;
 class CodeGenerator
 {
 public:
-    void generate_hpp(const uint64_t stripped_hash, const std::string& path)
-    {
-        string template_path = CONFIG(config::TEMPLATE_CPU_HPP_PATH);
-        string template_file = utils::read_file(template_path.c_str());
-        string generated = template_engine.render(
-            template_file,
-            {
-                {"stripped_hash", std::to_string(stripped_hash)},
-                {"data_type", "int"}
-            }
-        );
-        utils::write_file(generated, path);
-    }
 
     void generate_cpp(const std::string& query, 
                       const uint64_t stripped_hash,
diff --git a/query_engine/compile.sh b/query_engine/compile.sh
index e17021c50..21a2d6860 100755
--- a/query_engine/compile.sh
+++ b/query_engine/compile.sh
@@ -5,4 +5,5 @@
 # clang++ -std=c++1y create_return.cpp -o create_return.so -I../../../ -shared -fPIC
 # cd ../..
 # clang++ -std=c++1y -g -I../ -I ../lib/yaml-cpp/include main.cpp ../cypher/cypher.cpp -o engine -L ../lib/yaml-cpp/build -l yaml-cpp -l dl
+# clang++ -std=c++1y -g -O2 -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread
 clang++ -std=c++1y -g -I../ main.cpp ../cypher/cypher.cpp -o engine.out -l dl -pthread
diff --git a/query_engine/debug.hpp b/query_engine/debug.hpp
new file mode 100644
index 000000000..87209a9db
--- /dev/null
+++ b/query_engine/debug.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <iostream>
+
+#include "storage/model/properties/traversers/jsonwriter.hpp"
+#include "storage/model/properties/properties.hpp"
+
+using std::cout;
+using std::endl;
+
+void print_props(const Properties& properties)
+{       
+    StringBuffer buffer;
+    JsonWriter<StringBuffer> writer(buffer);
+    properties.accept(writer);
+    cout << buffer.str() << endl;
+}
+
+#ifdef DEBUG
+#   define PRINT_PROPS(_PROPS_) print_props(_PROPS_);
+#else
+#   define PRINT_PROPS(_)
+#endif
diff --git a/query_engine/i_code_cpu.hpp b/query_engine/i_code_cpu.hpp
index 11bf85b9f..585f98404 100644
--- a/query_engine/i_code_cpu.hpp
+++ b/query_engine/i_code_cpu.hpp
@@ -1,12 +1,13 @@
 #pragma once
 
 #include "query_engine/query_result.hpp"
+#include "query_stripped.hpp"
 #include "database/db.hpp"
 
 class ICodeCPU
 {
 public:
-    virtual QueryResult::sptr run(Db& db) = 0;
+    virtual QueryResult::sptr run(Db& db, code_args_t& args) = 0;
     virtual ~ICodeCPU() {}
 };
 
diff --git a/query_engine/main.cpp b/query_engine/main.cpp
index c44f426b7..2708ad900 100644
--- a/query_engine/main.cpp
+++ b/query_engine/main.cpp
@@ -3,6 +3,7 @@
 #include "utils/command_line/arguments.hpp"
 #include "cypher/common.hpp"
 #include "query_engine.hpp"
+#include "utils/time/timer.hpp"
 
 using std::cout;
 using std::endl;
@@ -17,7 +18,18 @@ int main(int argc, char** argv)
     cout << "QUERY: " << cypher_query << endl;
 
     QueryEngine engine;
-    engine.execute(cypher_query);
+    // engine.execute(cypher_query);
+
+    using std::placeholders::_1;
+    auto f = std::bind(&QueryEngine::execute, &engine, _1);
+
+    cout << std::fixed << timer(f, cypher_query) << endl;
+
+    // double counter = 0;
+    // for (int i = 0; i < 1000000; ++i) {
+    //     counter += timer(f, cypher_query);
+    // }
+    // cout << 1000000 / (counter / 1000000000) << "create_transactions per sec" << endl;
 
     return 0;
 }
diff --git a/query_engine/program_executor.hpp b/query_engine/program_executor.hpp
new file mode 100644
index 000000000..1085a4078
--- /dev/null
+++ b/query_engine/program_executor.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <string>
+
+#include "query_program.hpp"
+#include "database/db.hpp"
+#include "utils/log/logger.hpp"
+
+//  preparations before execution
+//  execution
+//  postprocess the results
+
+#define DEBUG 1
+#include "query_engine/debug.hpp"
+
+using std::cout;
+using std::endl;
+
+class ProgramExecutor
+{
+public:
+
+    auto execute(QueryProgram& program)
+    {
+        auto result = program.code->run(db, program.stripped.arguments);
+        print_props(*result->data["n"]->data[0]);
+        return result;
+    }
+
+public:
+
+    Db db;
+};
diff --git a/query_engine/code_loader.hpp b/query_engine/program_loader.hpp
similarity index 75%
rename from query_engine/code_loader.hpp
rename to query_engine/program_loader.hpp
index bb0be8346..ea924418a 100644
--- a/query_engine/code_loader.hpp
+++ b/query_engine/program_loader.hpp
@@ -4,7 +4,7 @@
 #include <unordered_map>
 #include <memory>
 
-// #define NOT_LOG_INFO
+#define NOT_LOG_INFO
 
 #include "memgraph_dynamic_lib.hpp"
 #include "query_stripper.hpp"
@@ -13,28 +13,27 @@
 #include "utils/hashing/fnv.hpp"
 #include "config/config.hpp"
 #include "utils/log/logger.hpp"
+#include "query_program.hpp"
 
 using std::string;
 using std::cout;
 using std::endl;
 
-
-class CodeLoader
+class ProgramLoader
 {    
 public:
 
     using sptr_code_lib = std::shared_ptr<CodeLib>;
 
-    CodeLoader()
-        : stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR))
-    {
-    }
+    ProgramLoader()
+        : stripper(make_query_stripper(TK_INT, TK_FLOAT, TK_STR, TK_BOOL)) {}
 
-    ICodeCPU* load_code_cpu(const string& query)
+    auto load(const string& query)
     {
         auto stripped = stripper.strip(query);
-        LOG_INFO("stripped_query=" + stripped);
-        auto stripped_hash = fnv(stripped);
+        LOG_INFO("stripped_query=" + stripped.query);
+
+        auto stripped_hash = fnv(stripped.query);
         auto hash_string = std::to_string(stripped_hash);
         LOG_INFO("query_hash=" + hash_string);
 
@@ -42,9 +41,10 @@ public:
 
         // code is already compiled and loaded, just return runnable
         // instance
-        if (code_lib_iter != code_libs.end())
-            //  TODO also return extracted arguments
-            return code_lib_iter->second->instance();
+        if (code_lib_iter != code_libs.end()) {
+            auto code = code_lib_iter->second->instance();
+            return QueryProgram(code, std::move(stripped));
+        }
 
         // code has to be generated, compiled and loaded
         //  TODO load output path from config
@@ -61,12 +61,13 @@ public:
         code_libs.insert({{stripped_hash, code_lib}});
 
         // return instance of runnable code (ICodeCPU)
-        return code_lib->instance();
+        return QueryProgram(code_lib->instance(), std::move(stripped));
     }
 
 private:
+
     //  TODO somehow remove int.. from here
-    QueryStripper<int, int, int> stripper;
+    QueryStripper<int, int, int, int> stripper;
     // TODO ifdef MEMGRAPH64 problem, how to use this kind
     // of ifdef functions?
     // uint64_t depends on fnv function
diff --git a/query_engine/query_engine.hpp b/query_engine/query_engine.hpp
index 086ac8702..3bb8bc84e 100644
--- a/query_engine/query_engine.hpp
+++ b/query_engine/query_engine.hpp
@@ -1,30 +1,26 @@
 #pragma once
 
-#include "code_loader.hpp"
-#include "code_executor.hpp"
+#include "program_loader.hpp"
+#include "program_executor.hpp"
 #include "query_result.hpp"
 
 //
 // Current arhitecture:
 // query -> code_loader -> query_stripper -> [code_generator]
 // -> [code_compiler] -> code_executor
-//
 
 class QueryEngine
 {
 public:
-    QueryEngine()
-    {
-    }
 
-    QueryResult* execute(const std::string& query)
+    auto execute(const std::string& query)
     {
-        executor.execute(loader.load_code_cpu(query));
-
-        throw std::runtime_error("implement me");
+        auto program = program_loader.load(query);
+        auto result = program_executor.execute(program);
+        return result;
     }
 
 private:
-    CodeLoader loader;
-    CodeExecutor executor;
+    ProgramExecutor program_executor;
+    ProgramLoader program_loader;
 };
diff --git a/query_engine/query_program.hpp b/query_engine/query_program.hpp
new file mode 100644
index 000000000..bc9079218
--- /dev/null
+++ b/query_engine/query_program.hpp
@@ -0,0 +1,17 @@
+#pragma once
+
+#include "i_code_cpu.hpp"
+#include "query_stripped.hpp"
+
+struct QueryProgram
+{
+    QueryProgram(ICodeCPU* code, QueryStripped&& stripped) :
+        code(code),
+        stripped(std::forward<QueryStripped>(stripped)) {}
+
+    QueryProgram(QueryProgram& other) = delete;
+    QueryProgram(QueryProgram&& other) = default;
+
+    ICodeCPU *code;
+    QueryStripped stripped;
+};
diff --git a/query_engine/query_result.hpp b/query_engine/query_result.hpp
index c19dbb7b3..8c61ef8dd 100644
--- a/query_engine/query_result.hpp
+++ b/query_engine/query_result.hpp
@@ -1,15 +1,38 @@
 #pragma once
 
 #include <string>
+#include <vector>
 #include <memory>
+#include <unordered_map>
 
-class QueryResult
+#include "storage/model/properties/properties.hpp"
+
+struct ResultList
 {
-public:
-    using sptr = std::shared_ptr<QueryResult>;
+    using sptr = std::shared_ptr<ResultList>;
+    using data_t = std::vector<const Properties*>;
 
-    QueryResult(std::string result) :
-        result(result) {}
+    ResultList() = delete;
+    ResultList(ResultList& other) = delete;
+    ResultList(ResultList&& other) = default;
 
-    std::string result;
+    ResultList(data_t&& data) :
+        data(std::forward<data_t>(data)) {}
+
+    std::vector<const Properties*> data;
+};
+
+struct QueryResult
+{
+    using sptr = std::shared_ptr<QueryResult>;
+    using data_t = std::unordered_map<std::string, ResultList::sptr>;
+
+    QueryResult() = delete;
+    QueryResult(QueryResult& other) = delete;
+    QueryResult(QueryResult&& other) = default;
+
+    QueryResult(data_t&& data) :
+        data(std::forward<data_t>(data)) {}
+
+    data_t data;
 };
diff --git a/query_engine/query_stripped.hpp b/query_engine/query_stripped.hpp
new file mode 100644
index 000000000..fe3bf5557
--- /dev/null
+++ b/query_engine/query_stripped.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <vector>
+
+#include "storage/model/properties/property.hpp"
+
+using code_args_t = std::vector<Property::sptr>;
+
+struct QueryStripped
+{
+    QueryStripped(const std::string&& query, code_args_t&& arguments) :
+        query(std::forward<const std::string>(query)),
+        arguments(std::forward<code_args_t>(arguments)) {}
+
+    QueryStripped(QueryStripped& other) = delete;
+    QueryStripped(QueryStripped&& other) = default;
+
+    std::string query;
+    code_args_t arguments;
+};
diff --git a/query_engine/query_stripper.hpp b/query_engine/query_stripper.hpp
index 087ab2001..e1900b974 100644
--- a/query_engine/query_stripper.hpp
+++ b/query_engine/query_stripper.hpp
@@ -3,13 +3,22 @@
 #include <string>
 #include <tuple>
 #include <utility>
+#include <unordered_map>
 
 #include "cypher/cypher.h"
 #include "cypher/tokenizer/cypher_lexer.hpp"
 #include "utils/variadic/variadic.hpp"
+#include "storage/model/properties/all.hpp"
+#include "query_stripped.hpp"
 
 #include <iostream>
 
+template<class T, class V>
+void store_query_param(code_args_t& arguments, V&& v)
+{
+    arguments.emplace_back(std::make_shared<T>(std::forward<V>(v)));
+}
+
 template<typename ...Ts>
 class QueryStripper
 {
@@ -17,32 +26,63 @@ public:
 
     QueryStripper(Ts&&... strip_types) :
         lexer(std::make_unique<CypherLexer>()),
-        strip_types(std::make_tuple(std::forward<Ts>(strip_types)...))
-    {
-    }
+        strip_types(std::make_tuple(std::forward<Ts>(strip_types)...)) {}
+
     QueryStripper(QueryStripper& other) = delete;
+
     QueryStripper(QueryStripper&& other) : 
         strip_types(std::move(other.strip_types)),
-        lexer(std::move(other.lexer))
-    {
-    }
+        lexer(std::move(other.lexer)) {}
 
-    decltype(auto) strip(const std::string& query)
+    auto strip(const std::string& query)
     {
-        //  TODO return hash and arguments
+        //  TODO write this more optimal (resplace string 
+        //  concatenation with something smarter)
+        //  TODO: in place substring replacement
+
         auto tokenizer = lexer->tokenize(query);
-        std::string stripped = "";
-        int counter = 0;
+
+        // TMP size of supported token types
         constexpr auto size = std::tuple_size<decltype(strip_types)>::value;
+
+        int counter = 0;
+        code_args_t stripped_arguments;
+        std::string stripped_query;
+        stripped_query.reserve(query.size());
+
         while (auto token = tokenizer.lookup())
         {
+            // TODO: better implementation
             if (_or(token.id, strip_types, std::make_index_sequence<size>{})) {
-                stripped += "@" + std::to_string(counter++);
+                auto index = counter++;
+                switch (token.id) {
+                    case TK_INT:
+                        store_query_param<Int32>(stripped_arguments,
+                                                 std::stoi(token.value));
+                        break;
+                    case TK_STR:
+                        store_query_param<String>(stripped_arguments,
+                                                  token.value);
+                        break;
+                    case TK_BOOL: {
+                        bool value = token.value[0] == 'T' ||
+                                     token.value[0] == 't';
+                        store_query_param<Bool>(stripped_arguments, value);
+                        break;
+                    }
+                    case TK_FLOAT:
+                        store_query_param<Float>(stripped_arguments,
+                                                 std::stof(token.value));
+                        break;
+                }
+                stripped_query += std::to_string(index);
             } else {
-                stripped += token.value;
+                stripped_query += token.value;
             }
         }
-        return stripped;
+
+        return QueryStripped(std::move(stripped_query),
+                             std::move(stripped_arguments));
     }
 
 private:
diff --git a/query_engine/template/template_code_cpu.cpp b/query_engine/template/template_code_cpu.cpp
index 0d5001e44..b8159adea 100644
--- a/query_engine/template/template_code_cpu.cpp
+++ b/query_engine/template/template_code_cpu.cpp
@@ -2,6 +2,7 @@
 #include <string>
 
 #include "query_engine/i_code_cpu.hpp"
+#include "storage/model/properties/all.hpp"
 
 //  TODO generate with the template engine 
 // #include "storage/model/properties/jsonwriter.hpp"
@@ -15,7 +16,7 @@ class {{class_name}} : public ICodeCPU
 {
 public:
 
-    QueryResult::sptr run(Db& db) override
+    QueryResult::sptr run(Db& db, code_args_t& args) override
     {
 {{code}}    }
 
diff --git a/query_engine/template/template_code_cpu.hpp b/query_engine/template/template_code_cpu.hpp
deleted file mode 100644
index 2a823ccc0..000000000
--- a/query_engine/template/template_code_cpu.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include "query_engine/i_code_cpu.hpp"
-
-class ICodeCPUData : ICodeCPU
-{
-public:
-    {{data_type}} data;
-};
diff --git a/query_engine/traverser/create_traverser.hpp b/query_engine/traverser/create_traverser.hpp
index a1197cadb..f14128b39 100644
--- a/query_engine/traverser/create_traverser.hpp
+++ b/query_engine/traverser/create_traverser.hpp
@@ -4,38 +4,52 @@
 #include <typeinfo>
 #include <map>
 
-#include "storage/model/properties/properties.hpp"
-#include "storage/model/properties/jsonwriter.hpp"
 #include "cypher/visitor/traverser.hpp"
-#include "node_traverser.hpp"
+#include "query_engine/util.hpp"
 
 using std::cout;
 using std::endl;
 
 class CreateTraverser : public Traverser
 {
+private:
+    std::string key;
+    uint32_t index{0};
+
 public:
     std::string code;
-    Properties properties;
 
     void visit(ast::Create& create) override
     {
-        code = "\t\tauto& t = db.tx_engine.begin();\n"; 
-        code += "\t\tauto vertex_accessor = db.graph.vertices.insert(t);\n";
+        code += line("auto& t = db.tx_engine.begin();");
+        code += line("auto vertex_accessor = db.graph.vertices.insert(t);");
+
         Traverser::visit(create);
     };
 
-    void visit(ast::Node& node) override
+    void visit(ast::Property& property) override
     {
-        Traverser::visit(node);
+        key = property.idn->name;
+
+        Traverser::visit(property);
+
+        code += line("vertex_accessor.property(");
+        code += line("\t\"" + key + "\", args[" + std::to_string(index) + "]");
+        code += line(");");
+
+        ++index;
     }
 
     void visit(ast::Return& ret) override
     {
-        code += "\t\tauto properties = vertex_accessor.properties();\n";
-        code += "\t\tStringBuffer buffer;\n";
-        code += "\t\tJsonWriter<StringBuffer> writer(buffer);\n";
-        code += "\t\tproperties.accept(writer);\n";
-        code += "\t\treturn std::make_shared<QueryResult>(buffer.str());\n";
+        code += line("t.commit();");
+        code += line("auto &properties = vertex_accessor.properties();");
+        code += line("ResultList::data_t data = {&properties};");
+        code += line("auto result_data = "
+                     "std::make_shared<ResultList>(std::move(data));");
+        code += line("QueryResult::data_t query_data = {{\"" +
+                     ret.return_list->value->name + "\", result_data}};");
+        code += line("return std::make_shared<QueryResult>"
+                     "(std::move(query_data));");
     }
 };
diff --git a/query_engine/traverser/node_traverser.hpp b/query_engine/traverser/node_traverser.hpp
deleted file mode 100644
index 80942488f..000000000
--- a/query_engine/traverser/node_traverser.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#pragma once
-
-#include "cypher/visitor/traverser.hpp"
-#include "storage/model/properties/properties.hpp"
-
-struct NodeTraverser : public Traverser
-{
-    Properties traverse(ast::Node& node)
-    {
-        Traverser::visit(node);
-        return properties;
-    }
-
-    void visit(ast::Property& property) override
-    {
-        key = property.idn->name;
-        Traverser::visit(property);
-        if (flag == Property::Flags::Int32) {
-            properties.set<Int32>(key, *int_value);
-        }
-        if (flag == Property::Flags::String) {
-            properties.set<String>(key, *string_value);
-        }
-    }
-
-    void visit(ast::String& string) override
-    {
-        flag = Property::Flags::String;
-        string_value = std::make_shared<String>(string.value);
-    }
-
-    void visit(ast::Integer& integer) override
-    {
-        flag = Property::Flags::Int32;
-        int_value = std::make_shared<Int32>(integer.value);
-    }
-
-    void visit(ast::Node& node) override
-    {
-        Traverser::visit(node);
-    }
-
-private:
-    std::string key;
-    Property::Flags flag;
-    std::shared_ptr<Int32> int_value;
-    std::shared_ptr<String> string_value;
-    Properties properties;
-};
diff --git a/query_engine/util.hpp b/query_engine/util.hpp
new file mode 100644
index 000000000..74cac8f5b
--- /dev/null
+++ b/query_engine/util.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <string>
+
+std::string line(std::string line)
+{
+    return "\t\t" + line + "\n";
+}
diff --git a/storage/edge.hpp b/storage/edge.hpp
index 5cc4e76d4..507af2925 100644
--- a/storage/edge.hpp
+++ b/storage/edge.hpp
@@ -2,7 +2,7 @@
 
 #include "mvcc/record.hpp"
 #include "model/edge_model.hpp"
-#include "model/properties/jsonwriter.hpp"
+#include "model/properties/traversers/jsonwriter.hpp"
 
 class Edge : public mvcc::Record<Edge>
 {
diff --git a/storage/model/properties/properties.hpp b/storage/model/properties/properties.hpp
index 4452804c9..37a088ee4 100644
--- a/storage/model/properties/properties.hpp
+++ b/storage/model/properties/properties.hpp
@@ -7,9 +7,9 @@
 
 class Properties
 {
-    using props_t = std::map<std::string, Property::sptr>;
-
 public:
+    using sptr = std::shared_ptr<Properties>;
+
     const Property& at(const std::string& key) const
     {
         auto it = props.find(key);
@@ -56,6 +56,8 @@ public:
     }
 
 private:
+    using props_t = std::map<std::string, Property::sptr>;
+
     props_t props;
 };
 
diff --git a/storage/record_accessor.hpp b/storage/record_accessor.hpp
index 92b4cc3f4..b61bb8b34 100644
--- a/storage/record_accessor.hpp
+++ b/storage/record_accessor.hpp
@@ -60,6 +60,11 @@ public:
         record->data.props.template set<V>(key, std::forward<Args>(args)...);
     }
 
+    void property(const std::string& key, Property::sptr value)
+    {
+        record->data.props.set(key, std::move(value));
+    }
+
     Properties& properties() const
     {
         return record->data.props;
diff --git a/storage/vertex.hpp b/storage/vertex.hpp
index 4d534cd78..da4d571f8 100644
--- a/storage/vertex.hpp
+++ b/storage/vertex.hpp
@@ -2,7 +2,7 @@
 
 #include "mvcc/record.hpp"
 #include "model/vertex_model.hpp"
-#include "model/properties/jsonwriter.hpp"
+#include "model/properties/traversers/jsonwriter.hpp"
 
 class Vertex : public mvcc::Record<Vertex>
 {
diff --git a/utils/time/timer.hpp b/utils/time/timer.hpp
new file mode 100644
index 000000000..28e337ac2
--- /dev/null
+++ b/utils/time/timer.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include <utility>
+#include <chrono>
+
+using time_point_t = std::chrono::high_resolution_clock::time_point;
+
+#define duration(a) \
+    std::chrono::duration_cast<std::chrono::nanoseconds>(a).count()
+
+#define time_now() std::chrono::high_resolution_clock::now()
+
+template<typename F, typename... Args>
+double timer(F func, Args&&... args)
+{
+    time_point_t start_time = time_now();
+    func(std::forward<Args>(args)...);
+    return duration(time_now() - start_time);
+}