From fbd9ca84207394ee9e61f27f56abd8b5b2faf032 Mon Sep 17 00:00:00 2001
From: Kruno Tomola Fabro <krunotf@memgraph.io>
Date: Mon, 22 Aug 2016 19:03:45 +0100
Subject: [PATCH] First version of CSVImport tool.

Query and import works.

Properties now use unordered_map.
Reduced memory footprint of properties by more than half.
---
 CMakeLists.txt                                |   9 +
 include/data_structures/map/rh_common.hpp     |   7 +-
 include/database/db_accessor.hpp              |   9 +-
 include/storage/edge_type/edge_type.hpp       |  20 +-
 include/storage/edge_type/edge_type_store.hpp |  14 +-
 include/storage/indexes/index_base.hpp        |   2 +-
 include/storage/label/label.hpp               |  13 +-
 include/storage/label/label_store.hpp         |  10 +-
 include/storage/model/properties/all.hpp      |   1 +
 include/storage/model/properties/array.hpp    |  69 +++
 include/storage/model/properties/flags.hpp    |   8 +
 .../storage/model/properties/properties.hpp   |  12 +-
 .../model/properties/property_family.hpp      |  14 +
 include/storage/record_accessor.hpp           |  26 +-
 include/utils/char_str.hpp                    |  23 +
 include/utils/command_line/arguments.hpp      |  31 +-
 include/utils/option.hpp                      |  39 +-
 src/database/db_accessor.cpp                  |  20 +-
 src/import/base_import.hpp                    | 218 +++++++++
 src/import/csv_import.hpp                     | 313 ++++++++++++
 src/import/element_skeleton.hpp               | 118 +++++
 src/import/fillings/array.hpp                 |  50 ++
 src/import/fillings/bool.hpp                  |  30 ++
 src/import/fillings/common.hpp                |  25 +
 src/import/fillings/double.hpp                |  29 ++
 src/import/fillings/filler.hpp                |  12 +
 src/import/fillings/float.hpp                 |  29 ++
 src/import/fillings/from.hpp                  |  37 ++
 src/import/fillings/id.hpp                    |  38 ++
 src/import/fillings/int32.hpp                 |  30 ++
 src/import/fillings/int64.hpp                 |  29 ++
 src/import/fillings/label.hpp                 |  29 ++
 src/import/fillings/skip.hpp                  |  19 +
 src/import/fillings/string.hpp                |  29 ++
 src/import/fillings/to.hpp                    |  36 ++
 src/import/fillings/type.hpp                  |  25 +
 src/storage/edge_type/edge_type.cpp           |  18 +-
 src/storage/edge_type/edge_type_store.cpp     |  14 +-
 src/storage/label/label.cpp                   |  18 +-
 src/storage/label/label_store.cpp             |  14 +-
 src/storage/model/properties/array.cpp        |  70 +++
 src/storage/vertex_accessor.cpp               |   5 +-
 tools/CMakeLists.txt                          |   8 +
 tools/tool.cpp                                | 461 ++++++++++++++++++
 44 files changed, 1942 insertions(+), 89 deletions(-)
 create mode 100644 include/storage/model/properties/array.hpp
 create mode 100644 include/utils/char_str.hpp
 create mode 100644 src/import/base_import.hpp
 create mode 100644 src/import/csv_import.hpp
 create mode 100644 src/import/element_skeleton.hpp
 create mode 100644 src/import/fillings/array.hpp
 create mode 100644 src/import/fillings/bool.hpp
 create mode 100644 src/import/fillings/common.hpp
 create mode 100644 src/import/fillings/double.hpp
 create mode 100644 src/import/fillings/filler.hpp
 create mode 100644 src/import/fillings/float.hpp
 create mode 100644 src/import/fillings/from.hpp
 create mode 100644 src/import/fillings/id.hpp
 create mode 100644 src/import/fillings/int32.hpp
 create mode 100644 src/import/fillings/int64.hpp
 create mode 100644 src/import/fillings/label.hpp
 create mode 100644 src/import/fillings/skip.hpp
 create mode 100644 src/import/fillings/string.hpp
 create mode 100644 src/import/fillings/to.hpp
 create mode 100644 src/import/fillings/type.hpp
 create mode 100644 src/storage/model/properties/array.cpp
 create mode 100644 tools/CMakeLists.txt
 create mode 100644 tools/tool.cpp

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 50bd9ea68..a9a86c025 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -218,6 +218,7 @@ FILE(COPY ${include_dir}/storage/model/properties/double.hpp DESTINATION ${build
 FILE(COPY ${include_dir}/storage/model/properties/int32.hpp DESTINATION ${build_include_dir}/storage/model/properties)
 FILE(COPY ${include_dir}/storage/model/properties/int64.hpp DESTINATION ${build_include_dir}/storage/model/properties)
 FILE(COPY ${include_dir}/storage/model/properties/string.hpp DESTINATION ${build_include_dir}/storage/model/properties)
+FILE(COPY ${include_dir}/storage/model/properties/array.hpp DESTINATION ${build_include_dir}/storage/model/properties)
 FILE(COPY ${include_dir}/storage/model/properties/floating.hpp DESTINATION ${build_include_dir}/storage/model/properties)
 FILE(COPY ${include_dir}/storage/model/properties/number.hpp DESTINATION ${build_include_dir}/storage/model/properties)
 FILE(COPY ${include_dir}/storage/model/properties/integral.hpp DESTINATION ${build_include_dir}/storage/model/properties)
@@ -370,6 +371,8 @@ option(MEMGRAPH "Build memgraph binary" ON)
 message(STATUS "MEMGRAPH binary: ${MEMGRAPH}")
 option(POC "Build proof of concept binaries" ON)
 message(STATUS "POC binaries: ${POC}")
+option(TOOLS "Build tool executables" ON)
+message(STATUS "TOOLS binaries: ${TOOLS}")
 option(TESTS "Build test binaries" ON)
 message(STATUS "TESTS binaries: ${TESTS}")
 # -- binaries -----------------------------------------------------------------
@@ -423,6 +426,7 @@ set(memgraph_src_files
     ${src_dir}/storage/model/properties/null.cpp
     ${src_dir}/storage/model/properties/bool.cpp
     ${src_dir}/storage/model/properties/string.cpp
+    ${src_dir}/storage/model/properties/array.cpp
     ${src_dir}/storage/model/properties/properties.cpp
     ${src_dir}/storage/model/properties/property_family.cpp
     ${src_dir}/storage/indexes/impl/nonunique_unordered_index.cpp
@@ -461,6 +465,11 @@ if (POC)
     add_subdirectory(poc)
 endif()
 
+# proof of concepts
+if (TOOLS)
+    add_subdirectory(tools)
+endif()
+
 # memgraph build name
 execute_process(
     OUTPUT_VARIABLE COMMIT_BRANCH
diff --git a/include/data_structures/map/rh_common.hpp b/include/data_structures/map/rh_common.hpp
index 244e1ec52..c3a016943 100644
--- a/include/data_structures/map/rh_common.hpp
+++ b/include/data_structures/map/rh_common.hpp
@@ -77,16 +77,17 @@ protected:
     {
     protected:
         IteratorBase() : map(nullptr) { advanced = index = ~((size_t)0); }
-        IteratorBase(const RhBase *map) : map(map)
+        IteratorBase(const RhBase *map)
         {
             index = 0;
             while (index < map->capacity && !map->array[index].valid()) {
                 index++;
             }
-            if (index == map->capacity) {
-                map = nullptr;
+            if (index >= map->capacity) {
+                this->map = nullptr;
                 advanced = index = ~((size_t)0);
             } else {
+                this->map = map;
                 advanced = index;
             }
         }
diff --git a/include/database/db_accessor.hpp b/include/database/db_accessor.hpp
index 32cc0d075..6f6bb9fb6 100644
--- a/include/database/db_accessor.hpp
+++ b/include/database/db_accessor.hpp
@@ -3,6 +3,7 @@
 #include "database/db_transaction.hpp"
 #include "storage/vertex_accessor.hpp"
 #include "utils/border.hpp"
+// #include "utils/iterator/iterator.hpp"
 #include "utils/option.hpp"
 
 namespace tx
@@ -61,15 +62,15 @@ public:
 
     // ******************* LABEL METHODS
 
-    const Label &label_find_or_create(const std::string &name);
+    const Label &label_find_or_create(const char *name);
 
-    bool label_contains(const std::string &name);
+    bool label_contains(const char *name);
 
     // ******************** TYPE METHODS
 
-    const EdgeType &type_find_or_create(const std::string &name);
+    const EdgeType &type_find_or_create(const char *name);
 
-    bool type_contains(const std::string &name);
+    bool type_contains(const char *name);
 
     // ******************** PROPERTY METHODS
 
diff --git a/include/storage/edge_type/edge_type.hpp b/include/storage/edge_type/edge_type.hpp
index 364161cd6..2dd64262c 100644
--- a/include/storage/edge_type/edge_type.hpp
+++ b/include/storage/edge_type/edge_type.hpp
@@ -1,25 +1,29 @@
 #pragma once
 
-#include <stdint.h>
 #include <ostream>
+#include <stdint.h>
 
-#include "utils/total_ordering.hpp"
+#include "utils/char_str.hpp"
 #include "utils/reference_wrapper.hpp"
+#include "utils/total_ordering.hpp"
 
 class EdgeType : public TotalOrdering<EdgeType>
 {
 public:
     EdgeType();
-    EdgeType(const std::string& id);
-    EdgeType(std::string&& id);
+    EdgeType(const std::string &id);
+    EdgeType(const char *id);
+    EdgeType(std::string &&id);
 
-    friend bool operator<(const EdgeType& lhs, const EdgeType& rhs);
+    friend bool operator<(const EdgeType &lhs, const EdgeType &rhs);
 
-    friend bool operator==(const EdgeType& lhs, const EdgeType& rhs);
+    friend bool operator==(const EdgeType &lhs, const EdgeType &rhs);
 
-    friend std::ostream& operator<<(std::ostream& stream, const EdgeType& type);
+    friend std::ostream &operator<<(std::ostream &stream, const EdgeType &type);
 
-    operator const std::string&() const;
+    operator const std::string &() const;
+
+    CharStr char_str() { return CharStr(&id[0]); }
 
 private:
     std::string id;
diff --git a/include/storage/edge_type/edge_type_store.hpp b/include/storage/edge_type/edge_type_store.hpp
index ff0cad4d9..1268f7acf 100644
--- a/include/storage/edge_type/edge_type_store.hpp
+++ b/include/storage/edge_type/edge_type_store.hpp
@@ -2,27 +2,27 @@
 
 #include <stdexcept>
 
+#include "data_structures/concurrent/concurrent_map.hpp"
 #include "storage/edge_type/edge_type.hpp"
-#include "data_structures/concurrent/concurrent_set.hpp"
+#include "utils/char_str.hpp"
 
 class EdgeTypeStore
 {
 public:
+    const EdgeType &find_or_create(const char *name);
 
-    const EdgeType& find_or_create(const std::string& name);
-
-    bool contains(const std::string& name); // TODO: const
+    bool contains(const char *name); // TODO: const
 
     // TODO: implement find method
     //       return { EdgeType, is_found }
-    
+
     // TODO: find by reference if it is possible (should be faster)
     //       figure out the fastest way to store and find types
     //       do the same for labels
-    
+
     // TODO: EdgeTypeStore and LabelStore are almost the same
     //       templetize the two of them
 
 private:
-    ConcurrentSet<EdgeType> edge_types;
+    ConcurrentMap<CharStr, std::unique_ptr<EdgeType>> edge_types;
 };
diff --git a/include/storage/indexes/index_base.hpp b/include/storage/indexes/index_base.hpp
index aa7603848..dd5e01529 100644
--- a/include/storage/indexes/index_base.hpp
+++ b/include/storage/indexes/index_base.hpp
@@ -37,7 +37,7 @@ public:
     // nonunique => always succeds.
     virtual bool insert(IndexRecord<T, K> &&value) = 0;
 
-    // Returns iterator which returns valid records in range.
+    // Returns iterator which returns valid filled records in range.
     // order==noe => doesn't guarantee any order of returned records.
     // order==Ascending => guarantees order of returnd records will be from
     // smallest to largest.
diff --git a/include/storage/label/label.hpp b/include/storage/label/label.hpp
index 0e45795c7..441ff5ab2 100644
--- a/include/storage/label/label.hpp
+++ b/include/storage/label/label.hpp
@@ -6,19 +6,20 @@
 #include "storage/indexes/impl/nonunique_unordered_index.hpp"
 #include "storage/vertex.hpp"
 #include "storage/vertex_accessor.hpp"
+#include "utils/char_str.hpp"
 #include "utils/reference_wrapper.hpp"
 #include "utils/total_ordering.hpp"
+
 using LabelIndexRecord = VertexIndexRecord<std::nullptr_t>;
 
-class Label : public TotalOrdering<Label>
+class Label : public TotalOrdering<Label>, TotalOrdering<CharStr, Label>
 {
 public:
     using label_index_t = NonUniqueUnorderedIndex<Vertex, std::nullptr_t>;
 
     Label() = delete;
 
-    Label(const std::string &name);
-    Label(std::string &&name);
+    Label(const char *name);
 
     Label(const Label &) = delete;
     Label(Label &&other) = default;
@@ -27,12 +28,18 @@ public:
 
     friend bool operator==(const Label &lhs, const Label &rhs);
 
+    friend bool operator<(const CharStr &lhs, const Label &rhs);
+
+    friend bool operator==(const CharStr &lhs, const Label &rhs);
+
     friend std::ostream &operator<<(std::ostream &stream, const Label &label);
 
     operator const std::string &() const;
 
     std::unique_ptr<label_index_t> index;
 
+    CharStr char_str() const { return CharStr(name.c_str()); }
+
 private:
     std::string name;
 };
diff --git a/include/storage/label/label_store.hpp b/include/storage/label/label_store.hpp
index 89302f8c2..5e83ec061 100644
--- a/include/storage/label/label_store.hpp
+++ b/include/storage/label/label_store.hpp
@@ -2,20 +2,20 @@
 
 #include <stdexcept>
 
-#include "storage/label/label.hpp"
 #include "data_structures/concurrent/concurrent_set.hpp"
+#include "storage/label/label.hpp"
+#include "utils/char_str.hpp"
 
 class LabelStore
 {
 public:
+    const Label &find_or_create(const char *name);
 
-    const Label& find_or_create(const std::string& name);
-
-    bool contains(const std::string& name); // TODO: const
+    bool contains(const char *name); // TODO: const
 
     // TODO: implement find method
     //       return { Label, is_found }
 
 private:
-    ConcurrentSet<Label> labels;
+    ConcurrentMap<CharStr, std::unique_ptr<Label>> labels;
 };
diff --git a/include/storage/model/properties/all.hpp b/include/storage/model/properties/all.hpp
index 3d09e84d8..e08a90e4c 100644
--- a/include/storage/model/properties/all.hpp
+++ b/include/storage/model/properties/all.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "storage/model/properties/array.hpp"
 #include "storage/model/properties/bool.hpp"
 #include "storage/model/properties/double.hpp"
 #include "storage/model/properties/float.hpp"
diff --git a/include/storage/model/properties/array.hpp b/include/storage/model/properties/array.hpp
new file mode 100644
index 000000000..c4c9e943d
--- /dev/null
+++ b/include/storage/model/properties/array.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "storage/model/properties/property.hpp"
+
+template <class T, Flags f_type>
+class Array : public Property
+{
+public:
+    static constexpr Flags type = f_type;
+    using Arr = std::vector<T>;
+
+    Array(const Array &) = default;
+    Array(Array &&) = default;
+
+    Array(const Arr &value);
+    Array(Arr &&value);
+
+    operator const Arr &() const;
+
+    bool operator==(const Property &other) const override;
+
+    bool operator==(const Array &other) const;
+
+    bool operator==(const Arr &other) const;
+
+    friend std::ostream &operator<<(std::ostream &stream, const Array &prop);
+
+    std::ostream &print(std::ostream &stream) const override;
+
+    Arr const &value_ref() const { return value; }
+
+    Arr value;
+};
+
+class ArrayString : public Array<std::string, Flags::ArrayString>
+{
+public:
+    using Array::Array;
+};
+
+class ArrayBool : public Array<bool, Flags::ArrayBool>
+{
+public:
+    using Array::Array;
+};
+
+class ArrayInt32 : public Array<int32_t, Flags::ArrayInt32>
+{
+public:
+    using Array::Array;
+};
+
+class ArrayInt64 : public Array<int64_t, Flags::ArrayInt64>
+{
+public:
+    using Array::Array;
+};
+
+class ArrayFloat : public Array<float, Flags::ArrayFloat>
+{
+public:
+    using Array::Array;
+};
+
+class ArrayDouble : public Array<double, Flags::ArrayDouble>
+{
+public:
+    using Array::Array;
+};
diff --git a/include/storage/model/properties/flags.hpp b/include/storage/model/properties/flags.hpp
index 529e879cc..a5940ddd8 100644
--- a/include/storage/model/properties/flags.hpp
+++ b/include/storage/model/properties/flags.hpp
@@ -29,6 +29,8 @@ enum class Flags : unsigned
 
     Null = 0x0,
     Bool = 0x1,
+
+    // TODO remove this two values
     True = 0x2 | Bool,
     False = 0x4 | Bool,
 
@@ -44,6 +46,12 @@ enum class Flags : unsigned
     Double = 0x400 | Floating,
 
     Array = 0x1000,
+    ArrayBool = (Bool << 13) | Array,
+    ArrayString = (String << 13) | Array,
+    ArrayInt32 = (Int32 << 13) | Array,
+    ArrayInt64 = (Int64 << 13) | Array,
+    ArrayFloat = (Float << 13) | Array,
+    ArrayDouble = (Double << 13) | Array,
 
     type_mask = 0xFFF
 
diff --git a/include/storage/model/properties/properties.hpp b/include/storage/model/properties/properties.hpp
index 41b2dcc8c..0d2554417 100644
--- a/include/storage/model/properties/properties.hpp
+++ b/include/storage/model/properties/properties.hpp
@@ -1,6 +1,6 @@
 #pragma once
 
-#include <map>
+#include <unordered_map>
 
 #include "storage/model/properties/property.hpp"
 #include "storage/model/properties/property_family.hpp"
@@ -49,7 +49,15 @@ public:
         handler.finish();
     }
 
+    template <class Handler>
+    void for_all(Handler handler) const
+    {
+        for (auto &kv : props)
+            handler(kv.first, kv.second);
+    }
+
 private:
-    using props_t = std::map<prop_key_t, Property::sptr>;
+    using props_t =
+        std::unordered_map<prop_key_t, Property::sptr, PropertyHash>;
     props_t props;
 };
diff --git a/include/storage/model/properties/property_family.hpp b/include/storage/model/properties/property_family.hpp
index 27c71a332..653c78582 100644
--- a/include/storage/model/properties/property_family.hpp
+++ b/include/storage/model/properties/property_family.hpp
@@ -80,6 +80,8 @@ public:
                 return type->family.name();
             }
 
+            const PropertyFamily &get_family() const { return type->family; }
+
         private:
             const PropertyType *type;
         };
@@ -181,3 +183,15 @@ private:
     // data structure.
     ConcurrentMap<Type, std::unique_ptr<PropertyType>> types;
 };
+
+class PropertyHash
+{
+public:
+    size_t
+    operator()(PropertyFamily::PropertyType::PropertyFamilyKey const &key) const
+    {
+        return (std::hash<const void *>()((const void *)(&(key.get_family()))) +
+                7) *
+               UINT64_C(0xbf58476d1ce4e5b9);
+    }
+};
diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp
index 0b4176ee3..c72137406 100644
--- a/include/storage/record_accessor.hpp
+++ b/include/storage/record_accessor.hpp
@@ -26,8 +26,14 @@ public:
         assert(vlist != nullptr);
     }
 
-    RecordAccessor(RecordAccessor const &other) = default;
-    RecordAccessor(RecordAccessor &&other) = default;
+    RecordAccessor(RecordAccessor const &other)
+        : record(other.record), vlist(other.vlist), db(other.db)
+    {
+    }
+    RecordAccessor(RecordAccessor &&other)
+        : record(other.record), vlist(other.vlist), db(other.db)
+    {
+    }
 
     bool empty() const { return record == nullptr; }
 
@@ -93,6 +99,22 @@ public:
     T const *operator->() const { return record; }
     T *operator->() { return record; }
 
+    RecordAccessor &operator=(const RecordAccessor &other)
+    {
+        record = other.record;
+        vlist_t *&vl = const_cast<vlist_t *&>(vlist);
+        vl = other.vlist;
+        return *this;
+    }
+
+    RecordAccessor &operator=(RecordAccessor &&other)
+    {
+        record = other.record;
+        vlist_t *&vl = const_cast<vlist_t *&>(vlist);
+        vl = other.vlist;
+        return *this;
+    }
+
     // Assumes same transaction
     friend bool operator==(const RecordAccessor &a, const RecordAccessor &b)
     {
diff --git a/include/utils/char_str.hpp b/include/utils/char_str.hpp
new file mode 100644
index 000000000..71118989a
--- /dev/null
+++ b/include/utils/char_str.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <cstring>
+#include "utils/total_ordering.hpp"
+
+class CharStr : public TotalOrdering<CharStr>
+{
+public:
+    CharStr(const char *str) : str(str) {}
+
+    friend bool operator==(const CharStr &lhs, const CharStr &rhs)
+    {
+        return strcmp(lhs.str, rhs.str) == 0;
+    }
+
+    friend bool operator<(const CharStr &lhs, const CharStr &rhs)
+    {
+        return strcmp(lhs.str, rhs.str) < 0;
+    }
+
+private:
+    const char *str;
+};
diff --git a/include/utils/command_line/arguments.hpp b/include/utils/command_line/arguments.hpp
index 4b3979ebc..82bd6d53a 100644
--- a/include/utils/command_line/arguments.hpp
+++ b/include/utils/command_line/arguments.hpp
@@ -1,8 +1,9 @@
 #pragma once
 
+#include <algorithm>
 #include <string>
 #include <vector>
-#include <algorithm>
+#include "utils/option.hpp"
 
 namespace
 {
@@ -15,24 +16,36 @@ auto all_arguments(int argc, char *argv[])
     return std::vector<std::string>(argv + 1, argv + argc);
 }
 
-bool contains_argument(const std::vector<std::string>& all,
-                       const std::string& flag)
+bool contains_argument(const std::vector<std::string> &all,
+                       const std::string &flag)
 {
     return std::find(all.begin(), all.end(), flag) != all.end();
 }
 
-auto get_argument(const std::vector<std::string>& all,
-                  const std::string& flag,
-                  const std::string& default_value)
+auto get_argument(const std::vector<std::string> &all, const std::string &flag,
+                  const std::string &default_value)
 {
     auto it = std::find(all.begin(), all.end(), flag);
 
-    if(it == all.end())
-        return default_value;
+    if (it == all.end()) return default_value;
 
     return all[std::distance(all.begin(), it) + 1];
 }
 
-#pragma clang diagnostic pop
+Option<std::string> take_argument(std::vector<std::string> &all,
+                                  const std::string &flag)
+{
+    auto it = std::find(all.begin(), all.end(), flag);
 
+    if (it == all.end()) return make_option<std::string>();
+
+    auto s = std::string(all[std::distance(all.begin(), it) + 1]);
+    it++;
+    it++;
+    all.erase(std::find(all.begin(), all.end(), flag), it);
+
+    return make_option<std::string>(std::move(s));
+}
+
+#pragma clang diagnostic pop
 }
diff --git a/include/utils/option.hpp b/include/utils/option.hpp
index a3aae3c5f..e2f7ddecf 100644
--- a/include/utils/option.hpp
+++ b/include/utils/option.hpp
@@ -1,6 +1,7 @@
 #pragma once
 
 #include <cassert>
+#include <cstring>
 #include <ext/aligned_buffer.h>
 #include <utility>
 
@@ -8,7 +9,7 @@ template <class T>
 class Option
 {
 public:
-    Option() {}
+    Option() { std::memset(data._M_addr(), 0, sizeof(T)); }
     //
     // Option(T item)
     // {
@@ -28,25 +29,51 @@ public:
         initialized = true;
     }
 
-    Option(Option &other) = default;
+    Option(const Option &other)
+    {
+        if (other.initialized) {
+            new (data._M_addr()) T(other.get());
+            initialized = true;
+        } else {
+            std::memset(data._M_addr(), 0, sizeof(T));
+        }
+    }
     // Containers from std which have strong exception guarantees wont use move
     // constructors and operators wihtout noexcept. "Optimized C++,2016 , Kurt
     // Guntheroth, page: 142, title: Moving instances into std::vector"
     Option(Option &&other) noexcept
     {
+
         if (other.initialized) {
-            data = std::move(other.data);
+            new (data._M_addr()) T(std::move(other.get()));
             other.initialized = false;
             initialized = true;
+        } else {
+            std::memset(data._M_addr(), 0, sizeof(T));
         }
     }
 
     ~Option()
     {
-        if (initialized) get().~T();
+        if (initialized) {
+            get().~T();
+            initialized = false;
+        }
     }
 
-    Option &operator=(Option &other) = default;
+    Option &operator=(const Option &other)
+    {
+        if (initialized) {
+            get().~T();
+            initialized = false;
+        }
+        if (other.initialized) {
+            new (data._M_addr()) T(other.get());
+            initialized = true;
+        }
+
+        return *this;
+    }
     Option &operator=(Option &&other)
     {
         if (initialized) {
@@ -55,7 +82,7 @@ public:
         }
 
         if (other.initialized) {
-            data = std::move(other.data);
+            new (data._M_addr()) T(std::move(other.get()));
             other.initialized = false;
             initialized = true;
         }
diff --git a/src/database/db_accessor.cpp b/src/database/db_accessor.cpp
index b5ddd0b7c..e418a75d5 100644
--- a/src/database/db_accessor.cpp
+++ b/src/database/db_accessor.cpp
@@ -45,29 +45,25 @@ Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from,
 }
 
 // LABEL METHODS
-const Label &DbAccessor::label_find_or_create(const std::string &name)
+const Label &DbAccessor::label_find_or_create(const char *name)
 {
-    return db_transaction.db.graph.label_store.find_or_create(
-        std::forward<const std::string &>(name));
+    return db_transaction.db.graph.label_store.find_or_create(name);
 }
 
-bool DbAccessor::label_contains(const std::string &name)
+bool DbAccessor::label_contains(const char *name)
 {
-    return db_transaction.db.graph.label_store.contains(
-        std::forward<const std::string &>(name));
+    return db_transaction.db.graph.label_store.contains(name);
 }
 
 // TYPE METHODS
-const EdgeType &DbAccessor::type_find_or_create(const std::string &name)
+const EdgeType &DbAccessor::type_find_or_create(const char *name)
 {
-    return db_transaction.db.graph.edge_type_store.find_or_create(
-        std::forward<const std::string &>(name));
+    return db_transaction.db.graph.edge_type_store.find_or_create(name);
 }
 
-bool DbAccessor::type_contains(const std::string &name)
+bool DbAccessor::type_contains(const char *name)
 {
-    return db_transaction.db.graph.edge_type_store.contains(
-        std::forward<const std::string &>(name));
+    return db_transaction.db.graph.edge_type_store.contains(name);
 }
 
 // PROPERTY METHODS
diff --git a/src/import/base_import.hpp b/src/import/base_import.hpp
new file mode 100644
index 000000000..8b577405e
--- /dev/null
+++ b/src/import/base_import.hpp
@@ -0,0 +1,218 @@
+#pragma once
+
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <ctime>
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "import/element_skeleton.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/vertex_accessor.hpp"
+#include "utils/option.hpp"
+
+using namespace std;
+
+static Option<Vertex::Accessor> empty_op_vacc;
+
+// Base importer with common facilities.
+class BaseImporter
+{
+
+public:
+    BaseImporter(DbAccessor &db, ostream &err_stream)
+        : db(db), err_stream(err_stream)
+    {
+    }
+
+    template <class... Args>
+    void err(Args &... args)
+    {
+        if (error) {
+            err_stream << " Error: ";
+            out_err(args...);
+            err_stream << endl;
+        }
+    }
+
+    template <class... Args>
+    void warn(Args &... args)
+    {
+        if (warning) {
+            err_stream << "     Warning: ";
+            out_err(args...);
+            err_stream << endl;
+        }
+    }
+
+    template <class T, class... Args>
+    void out_err(T &first, Args &... args)
+    {
+        err_stream << first;
+        out_err(args...);
+    }
+
+    template <class T>
+    void out_err(T &first)
+    {
+        err_stream << first;
+    }
+
+    char *cstr(string &str) { return &str[0]; }
+
+    bool split(string &str, char mark, vector<char *> &sub_str)
+    {
+        return split(cstr(str), mark, sub_str);
+    }
+
+    // Occurances of mark are changed with '\0'. sub_str is filled with
+    // pointers to parts of str splited by mark in ascending order. Empty
+    // sub_str are included. Doesn't split inside quotations and
+    // open_bracket,closed_bracket.
+    // Returns true if it was succesfully parsed.
+    bool split(char *str, char mark, vector<char *> &sub_str)
+    {
+
+        int head = 0;
+        bool in_text = false;
+        bool in_array = false;
+
+        for (int i = 0; str[i] != '\0'; i++) {
+            char &c = str[i];
+
+            // IN TEXT check
+            if (c == quotations_mark) {
+                in_text = !in_text;
+                if (in_text && head == i) {
+                    c = '\0';
+                    head = i + 1;
+                } else if (!in_text && !in_array) {
+                    c = '\0';
+                }
+                continue;
+            } else if (in_text) {
+                continue;
+            }
+
+            // IN ARRAY check
+            if (c == open_bracket) {
+                if (in_array) {
+                    err("Nested arrays aren't supported.");
+                    return false;
+                }
+                in_array = true;
+                continue;
+            }
+            if (in_array) {
+                if (c == closed_bracket) {
+                    in_array = false;
+                }
+                continue;
+            }
+
+            // SPLIT CHECK
+            if (c == mark) {
+                c = '\0';
+                sub_str.push_back(&str[head]);
+                head = i + 1;
+            }
+        }
+
+        sub_str.push_back(&str[head]);
+
+        return true;
+    }
+
+    // Extracts parts while stripping data of array chars and qutation marks.
+    void extract(char *str, const char delimiter, vector<char *> &sub_str)
+    {
+        int head = 0;
+        bool in_text = false;
+
+        for (int i = 0; str[i] != '\0'; i++) {
+            char &c = str[i];
+
+            // IN TEXT check
+            if (c == quotations_mark) {
+                in_text = !in_text;
+                if (in_text) {
+                } else {
+                    c = '\0';
+                    sub_str.push_back(&str[head]);
+                    head = i + 1;
+                }
+                head = i + 1;
+                continue;
+            } else if (in_text) {
+                continue;
+            }
+
+            // IN ARRAY check
+            if (c == open_bracket) {
+                head = i + 1;
+                continue;
+            } else if (c == closed_bracket) {
+                c = '\0';
+                if (i > head) {
+                    sub_str.push_back(&str[head]);
+                }
+                head = i + 1;
+                continue;
+            }
+
+            // SPLIT CHECK
+            if (c == delimiter) {
+                c = '\0';
+                if (i > head) {
+                    sub_str.push_back(&str[head]);
+                }
+                head = i + 1;
+            } else if (c == ' ' && i == head) {
+                head++;
+            }
+        }
+
+        sub_str.push_back(&str[head]);
+        //
+        // for (auto s : sub_str) {
+        //     cout << "#" << s;
+        // }
+    }
+
+    Option<Vertex::Accessor> const &get_vertex(size_t id)
+    {
+        if (vertices.size() > id) {
+            return vertices[id];
+        } else {
+            cout << vertices.size() << " -> " << id << endl;
+            return empty_op_vacc;
+        }
+    }
+
+public:
+    DbAccessor &db;
+
+    char parts_mark = ',';
+    char parts_array_mark = ',';
+    char type_mark = ':';
+    char quotations_mark = '"';
+    char open_bracket = '[';
+    char closed_bracket = ']';
+
+    bool warning = true;
+    bool error = true;
+
+protected:
+    // All errors are writen to this stream.
+    ostream &err_stream;
+
+    // All created vertices which have import local id
+    vector<Option<Vertex::Accessor>> vertices;
+};
diff --git a/src/import/csv_import.hpp b/src/import/csv_import.hpp
new file mode 100644
index 000000000..0e0c0999a
--- /dev/null
+++ b/src/import/csv_import.hpp
@@ -0,0 +1,313 @@
+#pragma once
+
+#include <algorithm>
+#include <chrono>
+#include <cstring>
+#include <ctime>
+#include <fstream>
+#include <iostream>
+#include <queue>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include "import/base_import.hpp"
+#include "import/element_skeleton.hpp"
+#include "import/fillings/array.hpp"
+#include "import/fillings/bool.hpp"
+#include "import/fillings/double.hpp"
+#include "import/fillings/filler.hpp"
+#include "import/fillings/float.hpp"
+#include "import/fillings/from.hpp"
+#include "import/fillings/id.hpp"
+#include "import/fillings/int32.hpp"
+#include "import/fillings/int64.hpp"
+#include "import/fillings/label.hpp"
+#include "import/fillings/skip.hpp"
+#include "import/fillings/string.hpp"
+#include "import/fillings/to.hpp"
+#include "import/fillings/type.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/vertex_accessor.hpp"
+#include "utils/option.hpp"
+
+using namespace std;
+
+bool equal_str(const char *a, const char *b) { return strcasecmp(a, b) == 0; }
+
+// CSV importer for importing multiple files regarding same graph.
+// CSV format of file should be following:
+//
+class CSVImporter : public BaseImporter
+{
+
+public:
+    using BaseImporter::BaseImporter;
+
+    // Loads data from stream and returns number of loaded vertexes.
+    size_t import_vertices(std::fstream &file)
+    {
+        return import(file, create_vertex, true);
+    }
+
+    // Loads data from stream and returns number of loaded edges.
+    size_t import_edges(std::fstream &file)
+    {
+        return import(file, create_edge, false);
+    }
+
+private:
+    // Loads data from file and returns number of loaded name.
+    template <class F>
+    size_t import(std::fstream &file, F f, bool vertex)
+    {
+        string line;
+        vector<char *> sub_str;
+        vector<unique_ptr<Filler>> fillers;
+        vector<char *> tmp;
+
+        // HEADERS
+        if (!getline(file, line)) {
+            err("No lines");
+            return 0;
+        }
+
+        if (!split(line, parts_mark, sub_str)) {
+            err("Illegal headers");
+            return 0;
+        }
+
+        for (auto p : sub_str) {
+            auto o = get_filler(p, tmp, vertex);
+            if (o.is_present()) {
+                fillers.push_back(o.take());
+            } else {
+                return 0;
+            }
+        }
+        sub_str.clear();
+
+        // LOAD DATA LINES
+        size_t count = 0;
+        size_t line_no = 1;
+        ElementSkeleton es(db);
+        while (std::getline(file, line)) {
+            // if (line_no % 1000 == 0) {
+            //     cout << line_no << endl;
+            // }
+            // cout << line << endl;
+            sub_str.clear();
+            es.clear();
+
+            if (split(line, parts_mark, sub_str)) {
+                check_for_part_count(sub_str.size() - fillers.size(), line_no);
+
+                int n = min(sub_str.size(), fillers.size());
+                for (int i = 0; i < n; i++) {
+                    auto er = fillers[i]->fill(es, sub_str[i]);
+                    if (er.is_present()) {
+                        err(er.get(), " on line: ", line_no);
+                    }
+                }
+
+                if (f(this, es, line_no)) {
+                    count++;
+                }
+            }
+
+            line_no++;
+        }
+
+        return count;
+    }
+
+    static bool create_vertex(CSVImporter *im, ElementSkeleton &es,
+                              size_t line_no)
+    {
+        auto va = es.add_vertex();
+        auto id = es.element_id();
+        if (id.is_present()) {
+
+            if (im->vertices.size() <= id.get()) {
+                Option<Vertex::Accessor> empty =
+                    make_option<Vertex::Accessor>();
+                im->vertices.insert(im->vertices.end(),
+                                    id.get() - im->vertices.size() + 1, empty);
+            }
+            if (im->vertices[id.get()].is_present()) {
+                im->err("Vertex on line: ", line_no,
+                        " has same id with another previously loaded vertex");
+                return false;
+            } else {
+                im->vertices[id.get()] = make_option(std::move(va));
+                return true;
+            }
+        } else {
+            im->warn("Missing import local vertex id for vertex on "
+                     "line: ",
+                     line_no);
+        }
+
+        return true;
+    }
+
+    static bool create_edge(CSVImporter *im, ElementSkeleton &es,
+                            size_t line_no)
+    {
+        auto o = es.add_edge();
+        if (!o.is_present()) {
+            return true;
+        } else {
+            im->err(o.get(), " on line: ", line_no);
+            return false;
+        }
+    }
+
+    // Returns filler for name:type in header_part. None if error occured.
+    Option<unique_ptr<Filler>> get_filler(char *header_part,
+                                          vector<char *> &tmp_vec, bool vertex)
+    {
+        tmp_vec.clear();
+        split(header_part, type_mark, tmp_vec);
+        if (tmp_vec.size() > 2) {
+            err("To much sub parts in header part");
+            return make_option<unique_ptr<Filler>>();
+        } else if (tmp_vec.size() < 2) {
+            if (tmp_vec.size() == 1) {
+                warn(
+                    "Column ", tmp_vec[0],
+                    " doesn't have specified type so string type will be used");
+                tmp_vec.push_back("string");
+            } else {
+                warn("Empty colum definition, skiping column.");
+                std::unique_ptr<Filler> f(new SkipFiller());
+                return make_option(std::move(f));
+            }
+        }
+
+        const char *name = tmp_vec[0];
+        const char *type = tmp_vec[1];
+
+        // cout << name << " # " << type << endl;
+
+        auto prop_key = [&](auto name, auto type) -> auto
+        {
+            if (vertex) {
+                return db.vertex_property_key(name, Type(type));
+            } else {
+                return db.edge_property_key(name, Type(type));
+            }
+        };
+
+        if (equal_str(type, "id")) {
+            std::unique_ptr<Filler> f(
+                name[0] == '\0'
+                    ? new IdFiller()
+                    : new IdFiller(make_option(prop_key(name, Flags::Int64))));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "start_id") || equal_str(type, "from_id")) {
+            std::unique_ptr<Filler> f(new FromFiller(*this));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "label")) {
+            std::unique_ptr<Filler> f(new LabelFiller(*this));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "end_id") || equal_str(type, "to_id")) {
+            std::unique_ptr<Filler> f(new ToFiller(*this));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "type")) {
+            std::unique_ptr<Filler> f(new TypeFiller(*this));
+            return make_option(std::move(f));
+
+        } else if (name[0] == '\0') { // OTHER FILLERS REQUIRE NAME
+            warn("Unnamed column of type: ", type, " will be skipped.");
+            std::unique_ptr<Filler> f(new SkipFiller());
+            return make_option(std::move(f));
+
+            // *********************** PROPERTIES
+        } else if (equal_str(type, "bool")) {
+            std::unique_ptr<Filler> f(
+                new BoolFiller(prop_key(name, Flags::Bool)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "double")) {
+            std::unique_ptr<Filler> f(
+                new DoubleFiller(prop_key(name, Flags::Double)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "float")) {
+            std::unique_ptr<Filler> f(
+                new FloatFiller(prop_key(name, Flags::Float)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "int")) {
+            std::unique_ptr<Filler> f(
+                new Int32Filler(prop_key(name, Flags::Int32)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "long")) {
+            std::unique_ptr<Filler> f(
+                new Int64Filler(prop_key(name, Flags::Int64)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "string")) {
+            std::unique_ptr<Filler> f(
+                new StringFiller(prop_key(name, Flags::String)));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "bool[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<bool, ArrayBool>(
+                *this, prop_key(name, Flags::ArrayBool), to_bool));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "float[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<float, ArrayFloat>(
+                *this, prop_key(name, Flags::ArrayFloat), to_float));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "double[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<double, ArrayDouble>(
+                *this, prop_key(name, Flags::ArrayDouble), to_double));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "int[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<int32_t, ArrayInt32>(
+                *this, prop_key(name, Flags::ArrayInt32), to_int32));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "long[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<int64_t, ArrayInt64>(
+                *this, prop_key(name, Flags::ArrayInt64), to_int64));
+            return make_option(std::move(f));
+
+        } else if (equal_str(type, "string[]")) {
+            std::unique_ptr<Filler> f(make_array_filler<string, ArrayString>(
+                *this, prop_key(name, Flags::ArrayString), to_string));
+            return make_option(std::move(f));
+
+        } else {
+            err("Unknown type: ", type);
+            return make_option<unique_ptr<Filler>>();
+        }
+    }
+
+    void check_for_part_count(long diff, long line_no)
+    {
+        if (diff != 0) {
+            if (diff < 0) {
+                // warn("Line no: ", line_no, " has less parts then "
+                //                            "specified in header. Missing ",
+                //      diff, " parts");
+            } else {
+                warn("Line no: ", line_no,
+                     " has more parts then specified in header. Extra ", diff,
+                     " parts");
+            }
+        }
+    }
+};
diff --git a/src/import/element_skeleton.hpp b/src/import/element_skeleton.hpp
new file mode 100644
index 000000000..9e7b4f3b1
--- /dev/null
+++ b/src/import/element_skeleton.hpp
@@ -0,0 +1,118 @@
+#pragma once
+
+#include <cassert>
+#include "database/db_accessor.hpp"
+#include "storage/model/properties/property_family.hpp"
+#include "storage/vertex_accessor.hpp"
+
+// Holder for element data which he can then insert as a vertex or edge into the
+// database depending on the available data.
+class ElementSkeleton
+{
+
+    class Prop
+    {
+
+    public:
+        Prop(PropertyFamily::PropertyType::PropertyFamilyKey key,
+             Option<std::shared_ptr<Property>> &&prop)
+            : key(key), prop(std::move(prop))
+        {
+        }
+
+        PropertyFamily::PropertyType::PropertyFamilyKey key;
+        Option<std::shared_ptr<Property>> prop;
+    };
+
+public:
+    ElementSkeleton(DbAccessor &db) : db(db){};
+
+    void add_property(PropertyFamily::PropertyType::PropertyFamilyKey key,
+                      std::shared_ptr<Property> &&prop)
+    {
+        properties.push_back(Prop(key, make_option(std::move(prop))));
+    }
+
+    void set_element_id(size_t id)
+    {
+        el_id = make_option<size_t>(std::move(id));
+    }
+
+    void add_label(Label const &label) { labels.push_back(&label); }
+
+    void set_type(EdgeType const &type) { this->type = make_option(&type); }
+
+    void set_from(Vertex::Accessor &&va)
+    {
+        from_va = make_option<Vertex::Accessor>(std::move(va));
+    }
+
+    void set_to(Vertex::Accessor &&va)
+    {
+        to_va = make_option<Vertex::Accessor>(std::move(va));
+    }
+
+    Vertex::Accessor add_vertex()
+    {
+        auto va = db.vertex_insert();
+
+        for (auto l : labels) {
+            // std::cout << *l << std::endl;
+            va.add_label(*l);
+        }
+        add_propreties(va);
+
+        return va;
+    }
+
+    // Return error msg if unsuccessful
+    Option<std::string> add_edge()
+    {
+        if (!from_va.is_present()) {
+            return make_option(std::string("From field must be seted"));
+        }
+        if (!to_va.is_present()) {
+            return make_option(std::string("To field must be seted"));
+        }
+
+        auto ve = db.edge_insert(from_va.get(), to_va.get());
+        if (type.is_present()) {
+            ve.edge_type(*type.get());
+        }
+        add_propreties(ve);
+
+        return make_option<std::string>();
+    }
+
+    void clear()
+    {
+        el_id = make_option<size_t>();
+        to_va = make_option<Vertex::Accessor>();
+        from_va = make_option<Vertex::Accessor>();
+        type = make_option<EdgeType const *>();
+        labels.clear();
+        properties.clear();
+    }
+
+    // Returns import local id.
+    Option<size_t> element_id() { return el_id; }
+
+private:
+    template <class A>
+    void add_propreties(A &ra)
+    {
+        for (auto prop : properties) {
+            assert(prop.prop.is_present());
+            ra.set(prop.key, prop.prop.take());
+        }
+    }
+
+    DbAccessor &db;
+
+    Option<size_t> el_id;
+    Option<Vertex::Accessor> to_va;
+    Option<Vertex::Accessor> from_va;
+    Option<EdgeType const *> type;
+    std::vector<Label const *> labels;
+    std::vector<Prop> properties;
+};
diff --git a/src/import/fillings/array.hpp b/src/import/fillings/array.hpp
new file mode 100644
index 000000000..e8ebc2935
--- /dev/null
+++ b/src/import/fillings/array.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "database/db_accessor.hpp"
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+
+template <class T, class A>
+class ArrayFiller : public Filler
+{
+
+public:
+    ArrayFiller(BaseImporter &db,
+                PropertyFamily::PropertyType::PropertyFamilyKey key,
+                T (*f)(const char *))
+        : bim(db), key(key), f(f)
+    {
+    }
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        sub_str.clear();
+        std::vector<T> vec;
+        bim.extract(str, bim.parts_array_mark, sub_str);
+        for (auto s : sub_str) {
+            if (s[0] != '\0') {
+                vec.push_back(f(s));
+            }
+        }
+        if (vec.size() > 0) {
+            data.add_property(key, make_shared<A>(std::move(vec)));
+        }
+        return make_option<std::string>();
+    }
+
+private:
+    BaseImporter &bim;
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+    vector<char *> sub_str;
+    T (*f)(const char *);
+};
+
+template <class T, class A>
+auto make_array_filler(BaseImporter &db,
+                       PropertyFamily::PropertyType::PropertyFamilyKey key,
+                       T (*f)(const char *))
+{
+    return new ArrayFiller<T, A>(db, key, f);
+}
diff --git a/src/import/fillings/bool.hpp b/src/import/fillings/bool.hpp
new file mode 100644
index 000000000..ef68ac63f
--- /dev/null
+++ b/src/import/fillings/bool.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class BoolFiller : public Filler
+{
+
+public:
+    BoolFiller(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<Bool>(to_bool(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/common.hpp b/src/import/fillings/common.hpp
new file mode 100644
index 000000000..e1266c4f4
--- /dev/null
+++ b/src/import/fillings/common.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <cstdlib>
+#include <cstdlib>
+#include <iostream>
+#include <string>
+#include <strings.h>
+#include "storage/model/properties/all.hpp"
+
+bool string2bool(const char *v)
+{
+    return strcasecmp(v, "true") == 0 || atoi(v) != 0;
+}
+
+bool to_bool(const char *str) { return string2bool(str); }
+
+float to_float(const char *str) { return stof(str); }
+
+double to_double(const char *str) { return atof(str); }
+
+int32_t to_int32(const char *str) { return atoi(str); }
+
+int64_t to_int64(const char *str) { return atoll(str); }
+
+std::string to_string(const char *str) { return std::string(str); }
diff --git a/src/import/fillings/double.hpp b/src/import/fillings/double.hpp
new file mode 100644
index 000000000..222cdc75f
--- /dev/null
+++ b/src/import/fillings/double.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class DoubleFiller : public Filler
+{
+
+public:
+    DoubleFiller(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<Double>(to_double(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/filler.hpp b/src/import/fillings/filler.hpp
new file mode 100644
index 000000000..82f0b4dfa
--- /dev/null
+++ b/src/import/fillings/filler.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "import/element_skeleton.hpp"
+#include "utils/option.hpp"
+
+class Filler
+{
+public:
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    virtual Option<std::string> fill(ElementSkeleton &data, char *str) = 0;
+};
diff --git a/src/import/fillings/float.hpp b/src/import/fillings/float.hpp
new file mode 100644
index 000000000..b5f0c6ca8
--- /dev/null
+++ b/src/import/fillings/float.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class FloatFiller : public Filler
+{
+
+public:
+    FloatFiller(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<Float>(to_float(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/from.hpp b/src/import/fillings/from.hpp
new file mode 100644
index 000000000..92b9c6b4f
--- /dev/null
+++ b/src/import/fillings/from.hpp
@@ -0,0 +1,37 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class FromFiller : public Filler
+{
+
+public:
+    FromFiller(BaseImporter &db) : bim(db) {}
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            auto id = atol(str);
+            Option<Vertex::Accessor> const &oav = bim.get_vertex(id);
+            if (oav.is_present()) {
+                data.set_from(Vertex::Accessor(oav.get()));
+                return make_option<std::string>();
+            } else {
+                return make_option(
+                    std::string("Unknown vertex in from field with id: ") +
+                    str);
+            }
+        } else {
+            return make_option(std::string("From field must be spceified"));
+        }
+    }
+
+private:
+    BaseImporter &bim;
+};
diff --git a/src/import/fillings/id.hpp b/src/import/fillings/id.hpp
new file mode 100644
index 000000000..8b90489d4
--- /dev/null
+++ b/src/import/fillings/id.hpp
@@ -0,0 +1,38 @@
+#pragma once
+
+#include "import/fillings/filler.hpp"
+
+class IdFiller : public Filler
+{
+
+public:
+    IdFiller()
+        : key(make_option<PropertyFamily::PropertyType::PropertyFamilyKey>())
+    {
+    }
+
+    IdFiller(Option<PropertyFamily::PropertyType::PropertyFamilyKey> key)
+        : key(key)
+    {
+        assert(!key.is_present() ||
+               key.get().prop_type() == Type(Flags::Int64));
+    }
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.set_element_id(atol(str));
+            if (key.is_present()) {
+                data.add_property(key.get(),
+                                  std::make_shared<Int64>(to_int64(str)));
+            }
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    Option<PropertyFamily::PropertyType::PropertyFamilyKey> key;
+};
diff --git a/src/import/fillings/int32.hpp b/src/import/fillings/int32.hpp
new file mode 100644
index 000000000..f06cd2f8d
--- /dev/null
+++ b/src/import/fillings/int32.hpp
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class Int32Filler : public Filler
+{
+
+public:
+    Int32Filler(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<Int32>(to_int32(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/int64.hpp b/src/import/fillings/int64.hpp
new file mode 100644
index 000000000..7859d84fa
--- /dev/null
+++ b/src/import/fillings/int64.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class Int64Filler : public Filler
+{
+
+public:
+    Int64Filler(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<Int64>(to_int64(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/label.hpp b/src/import/fillings/label.hpp
new file mode 100644
index 000000000..2f5753012
--- /dev/null
+++ b/src/import/fillings/label.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "database/db_accessor.hpp"
+#include "import/fillings/filler.hpp"
+
+class LabelFiller : public Filler
+{
+
+public:
+    LabelFiller(BaseImporter &db) : bim(db) {}
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        sub_str.clear();
+        bim.extract(str, bim.parts_array_mark, sub_str);
+        for (auto s : sub_str) {
+            if (s[0] != '\0') {
+                data.add_label(bim.db.label_find_or_create(s));
+            }
+        }
+        return make_option<std::string>();
+    }
+
+private:
+    BaseImporter &bim;
+    vector<char *> sub_str;
+};
diff --git a/src/import/fillings/skip.hpp b/src/import/fillings/skip.hpp
new file mode 100644
index 000000000..7b1952833
--- /dev/null
+++ b/src/import/fillings/skip.hpp
@@ -0,0 +1,19 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class SkipFiller : public Filler
+{
+
+public:
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        return make_option<std::string>();
+    }
+};
diff --git a/src/import/fillings/string.hpp b/src/import/fillings/string.hpp
new file mode 100644
index 000000000..a665b25b0
--- /dev/null
+++ b/src/import/fillings/string.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class StringFiller : public Filler
+{
+
+public:
+    StringFiller(PropertyFamily::PropertyType::PropertyFamilyKey key) : key(key)
+    {
+    }
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.add_property(key, std::make_shared<String>(to_string(str)));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    PropertyFamily::PropertyType::PropertyFamilyKey key;
+};
diff --git a/src/import/fillings/to.hpp b/src/import/fillings/to.hpp
new file mode 100644
index 000000000..45b8ff067
--- /dev/null
+++ b/src/import/fillings/to.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "import/fillings/common.hpp"
+#include "import/fillings/filler.hpp"
+#include "storage/model/properties/all.hpp"
+#include "storage/model/properties/flags.hpp"
+#include "storage/model/properties/property_family.hpp"
+
+class ToFiller : public Filler
+{
+
+public:
+    ToFiller(BaseImporter &db) : bim(db) {}
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            auto id = atol(str);
+            Option<Vertex::Accessor> const &oav = bim.get_vertex(id);
+            if (oav.is_present()) {
+                data.set_to(Vertex::Accessor(oav.get()));
+                return make_option<std::string>();
+            } else {
+                return make_option(
+                    std::string("Unknown vertex in to field with id: ") + str);
+            }
+        } else {
+            return make_option(std::string("To field must be spceified"));
+        }
+    }
+
+private:
+    BaseImporter &bim;
+};
diff --git a/src/import/fillings/type.hpp b/src/import/fillings/type.hpp
new file mode 100644
index 000000000..577ecfbb9
--- /dev/null
+++ b/src/import/fillings/type.hpp
@@ -0,0 +1,25 @@
+#pragma once
+
+#include "database/db_accessor.hpp"
+#include "import/fillings/filler.hpp"
+
+class TypeFiller : public Filler
+{
+
+public:
+    TypeFiller(BaseImporter &db) : bim(db) {}
+
+    // Fills skeleton with data from str. Returns error description if
+    // error occurs.
+    Option<std::string> fill(ElementSkeleton &data, char *str) final
+    {
+        if (str[0] != '\0') {
+            data.set_type(bim.db.type_find_or_create(str));
+        }
+
+        return make_option<std::string>();
+    }
+
+private:
+    BaseImporter &bim;
+};
diff --git a/src/storage/edge_type/edge_type.cpp b/src/storage/edge_type/edge_type.cpp
index 8fa270ea3..6c0fd0996 100644
--- a/src/storage/edge_type/edge_type.cpp
+++ b/src/storage/edge_type/edge_type.cpp
@@ -1,25 +1,23 @@
 #include "storage/edge_type/edge_type.hpp"
 
 EdgeType::EdgeType() {}
-EdgeType::EdgeType(const std::string& id) : id(id) {}
-EdgeType::EdgeType(std::string&& id) : id(std::move(id)) {}
+EdgeType::EdgeType(const std::string &id) : id(id) {}
+EdgeType::EdgeType(const char *id) : id(std::string(id)) {}
+EdgeType::EdgeType(std::string &&id) : id(std::move(id)) {}
 
-bool operator<(const EdgeType& lhs, const EdgeType& rhs)
+bool operator<(const EdgeType &lhs, const EdgeType &rhs)
 {
     return lhs.id < rhs.id;
 }
 
-bool operator==(const EdgeType& lhs, const EdgeType& rhs)
+bool operator==(const EdgeType &lhs, const EdgeType &rhs)
 {
     return lhs.id == rhs.id;
 }
 
-std::ostream& operator<<(std::ostream& stream, const EdgeType& type)
+std::ostream &operator<<(std::ostream &stream, const EdgeType &type)
 {
-   return stream << type.id;
+    return stream << type.id;
 }
 
-EdgeType::operator const std::string&() const
-{
-    return id;
-}
+EdgeType::operator const std::string &() const { return id; }
diff --git a/src/storage/edge_type/edge_type_store.cpp b/src/storage/edge_type/edge_type_store.cpp
index 266701eb3..7dc89a83a 100644
--- a/src/storage/edge_type/edge_type_store.cpp
+++ b/src/storage/edge_type/edge_type_store.cpp
@@ -1,13 +1,19 @@
 #include "storage/edge_type/edge_type_store.hpp"
 
-const EdgeType& EdgeTypeStore::find_or_create(const std::string& name)
+const EdgeType &EdgeTypeStore::find_or_create(const char *name)
 {
     auto accessor = edge_types.access();
-    return accessor.insert(EdgeType(name)).first;
+    auto it = accessor.find(CharStr(name));
+    if (it == accessor.end()) {
+        auto l = std::make_unique<EdgeType>(name);
+        auto k = l->char_str();
+        it = accessor.insert(k, std::move(l)).first;
+    }
+    return *(it->second);
 }
 
-bool EdgeTypeStore::contains(const std::string& name) // const
+bool EdgeTypeStore::contains(const char *name) // const
 {
     auto accessor = edge_types.access();
-    return accessor.find(EdgeType(name)) != accessor.end();
+    return accessor.find(CharStr(name)) != accessor.end();
 }
diff --git a/src/storage/label/label.cpp b/src/storage/label/label.cpp
index b669fcc56..b51e9d3be 100644
--- a/src/storage/label/label.cpp
+++ b/src/storage/label/label.cpp
@@ -1,12 +1,8 @@
 // #include "storage/indexes/impl/nonunique_unordered_index.hpp"
 #include "storage/label/label.hpp"
 
-Label::Label(const std::string &name)
-    : name(name), index(std::unique_ptr<label_index_t>(new label_index_t()))
-{
-}
-Label::Label(std::string &&name)
-    : name(std::move(name)),
+Label::Label(const char *name)
+    : name(std::string(name)),
       index(std::unique_ptr<label_index_t>(new label_index_t()))
 {
 }
@@ -21,6 +17,16 @@ bool operator==(const Label &lhs, const Label &rhs)
     return lhs.name == rhs.name;
 }
 
+bool operator<(const CharStr &lhs, const Label &rhs)
+{
+    return lhs < rhs.char_str();
+}
+
+bool operator==(const CharStr &lhs, const Label &rhs)
+{
+    return lhs == rhs.char_str();
+}
+
 std::ostream &operator<<(std::ostream &stream, const Label &label)
 {
     return stream << label.name;
diff --git a/src/storage/label/label_store.cpp b/src/storage/label/label_store.cpp
index 5506ea1d1..3eb5af737 100644
--- a/src/storage/label/label_store.cpp
+++ b/src/storage/label/label_store.cpp
@@ -1,13 +1,19 @@
 #include "storage/label/label_store.hpp"
 
-const Label& LabelStore::find_or_create(const std::string& name)
+const Label &LabelStore::find_or_create(const char *name)
 {
     auto accessor = labels.access();
-    return accessor.insert(Label(name)).first;
+    auto it = accessor.find(CharStr(name));
+    if (it == accessor.end()) {
+        auto l = std::make_unique<Label>(name);
+        auto k = l->char_str();
+        it = accessor.insert(k, std::move(l)).first;
+    }
+    return *(it->second);
 }
 
-bool LabelStore::contains(const std::string& name) // const
+bool LabelStore::contains(const char *name) // const
 {
     auto accessor = labels.access();
-    return accessor.find(Label(name)) != accessor.end();
+    return accessor.find(CharStr(name)) != accessor.end();
 }
diff --git a/src/storage/model/properties/array.cpp b/src/storage/model/properties/array.cpp
new file mode 100644
index 000000000..01d7075db
--- /dev/null
+++ b/src/storage/model/properties/array.cpp
@@ -0,0 +1,70 @@
+#include "storage/model/properties/array.hpp"
+
+template <class T, Flags f_type>
+Array<T, f_type>::Array(const Arr &value) : Property(f_type), value(value)
+{
+}
+
+template <class T, Flags f_type>
+Array<T, f_type>::Array(Arr &&value) : Property(f_type), value(value)
+{
+}
+
+template <class T, Flags f_type>
+Array<T, f_type>::operator const Arr &() const
+{
+    return value;
+}
+
+template <class T, Flags f_type>
+bool Array<T, f_type>::operator==(const Property &other) const
+{
+    return other.is<Array>() && operator==(other.as<Array>());
+}
+
+template <class T, Flags f_type>
+bool Array<T, f_type>::operator==(const Array &other) const
+{
+    return this->operator==(other.value);
+}
+
+template <class T, Flags f_type>
+bool Array<T, f_type>::operator==(const Arr &other) const
+{
+    if (value.size() != other.size()) {
+        return false;
+    }
+
+    auto n = value.size();
+    for (size_t i = 0; i < n; i++) {
+        if (value[i] != other[i]) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+template <class T, Flags f_type>
+std::ostream &operator<<(std::ostream &stream, const Array<T, f_type> &prop)
+{
+    return prop.print(stream);
+}
+
+template <class T, Flags f_type>
+std::ostream &Array<T, f_type>::print(std::ostream &stream) const
+{
+    stream << "[";
+    for (auto e : value) {
+        stream << e << ",";
+    }
+    stream << "]";
+    return stream;
+}
+
+template class Array<std::string, Flags::ArrayString>;
+template class Array<bool, Flags::ArrayBool>;
+template class Array<int32_t, Flags::ArrayInt32>;
+template class Array<int64_t, Flags::ArrayInt64>;
+template class Array<float, Flags::ArrayFloat>;
+template class Array<double, Flags::ArrayDouble>;
diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp
index 6ce4e8264..e171aea10 100644
--- a/src/storage/vertex_accessor.cpp
+++ b/src/storage/vertex_accessor.cpp
@@ -54,9 +54,8 @@ auto Vertex::Accessor::out() const
 auto Vertex::Accessor::in() const
 {
     DbTransaction &t = this->db;
-    return iter::make_one_time_accessor(
-        iter::make_map(iter::make_iter_ref(record->data.in),
-                       [&](auto e) -> auto { return Edge::Accessor(e, t); }));
+    return iter::make_map(iter::make_iter_ref(record->data.in),
+                          [&](auto e) -> auto { return Edge::Accessor(e, t); });
 }
 
 bool Vertex::Accessor::in_contains(Vertex::Accessor const &other) const
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 000000000..8c909a13c
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(memgraph_tools)
+
+add_executable(import_tool tool.cpp)
+target_link_libraries(import_tool memgraph)
+target_link_libraries(import_tool Threads::Threads)
+target_link_libraries(import_tool ${fmt_static_lib})
diff --git a/tools/tool.cpp b/tools/tool.cpp
new file mode 100644
index 000000000..923ad6a3f
--- /dev/null
+++ b/tools/tool.cpp
@@ -0,0 +1,461 @@
+#include "database/db.hpp"
+#include "database/db_accessor.hpp"
+
+#include <chrono>
+#include <ctime>
+#include <strings.h>
+#include <unistd.h>
+#include <unordered_map>
+#include "database/db_accessor.cpp"
+#include "import/csv_import.hpp"
+#include "storage/indexes/impl/nonunique_unordered_index.cpp"
+#include "storage/model/properties/properties.cpp"
+#include "storage/record_accessor.cpp"
+#include "storage/vertex_accessor.cpp"
+#include "utils/command_line/arguments.hpp"
+
+using namespace std;
+
+template <class C>
+void fill_to_fill(Edge::Accessor &e, const EdgeType &type, C &&consumer)
+{
+    if (e.fill() && e.edge_type() == type) {
+        auto to = e.to();
+        if (to.fill()) {
+            consumer(to);
+        }
+    }
+}
+
+template <class C>
+void fill_from_fill(Edge::Accessor &e, const EdgeType &type, C &&consumer)
+{
+    if (e.fill() && e.edge_type() == type) {
+        auto from = e.from();
+        if (from.fill()) {
+            consumer(from);
+        }
+    }
+}
+
+template <class C>
+void fill_to_fill(Edge::Accessor &e, C &&consumer)
+{
+    if (e.fill()) {
+        auto to = e.to();
+        if (to.fill()) {
+            consumer(to);
+        }
+    }
+}
+
+template <class C>
+void to_fill(Edge::Accessor &e, C &&consumer)
+{
+    auto to = e.to();
+    if (to.fill()) {
+        consumer(to);
+    }
+}
+
+template <class C>
+void to_fill(Edge::Accessor &e, const Label &label, C &&consumer)
+{
+    auto to = e.to();
+    if (to.fill() && to.has_label(label)) {
+        consumer(to);
+    }
+}
+
+template <class C>
+void to_fill(Edge::Accessor &e, const EdgeType &type, const Label &label,
+             C &&consumer)
+{
+    if (e.edge_type() == type) {
+        auto to = e.to();
+        if (to.fill() && to.has_label(label)) {
+            consumer(to);
+        }
+    }
+}
+
+template <class C>
+void from_fill(Edge::Accessor &e, const EdgeType &type, C &&consumer)
+{
+    if (e.edge_type() == type) {
+        auto from = e.from();
+        if (from.fill()) {
+            consumer(from);
+        }
+    }
+}
+
+template <class C>
+void fill_from_fill(Edge::Accessor &e, C &&consumer)
+{
+    if (e.fill()) {
+        auto from = e.from();
+        if (from.fill()) {
+            consumer(from);
+        }
+    }
+}
+
+namespace iter
+{
+template <class I, class C>
+void for_all_fill(I iter, C &&consumer)
+{
+    auto e = iter.next();
+    while (e.is_present()) {
+        if (e.get().fill()) consumer(e.take());
+        e = iter.next();
+    }
+}
+
+template <class I, class C>
+void find(I iter, C &&consumer)
+{
+    auto e = iter.next();
+    while (e.is_present()) {
+
+        if (consumer(e.take())) {
+            return;
+        }
+        e = iter.next();
+    }
+}
+
+template <class I, class C>
+void find_fill(I iter, C &&consumer)
+{
+    auto e = iter.next();
+    while (e.is_present()) {
+        if (e.get().fill()) {
+            if (consumer(e.take())) {
+                return;
+            }
+        }
+        e = iter.next();
+    }
+}
+}
+
+void load(DbAccessor &t, vector<string> &para)
+{
+    // DbAccessor t(db);
+    CSVImporter imp(t, cerr);
+
+    imp.parts_mark = get_argument(para, "-d", ",")[0];
+    imp.parts_array_mark = get_argument(para, "-ad", ",")[0];
+    imp.warning =
+        strcasecmp(get_argument(para, "-w", "true").c_str(), "true") == 0;
+    imp.error =
+        strcasecmp(get_argument(para, "-err", "true").c_str(), "true") == 0;
+
+    // IMPORT VERTICES
+    auto o = take_argument(para, "-v");
+    while (o.is_present()) {
+        std::fstream file(o.get());
+        // cout << "Importing vertices from file: " << o.get() << endl;
+        auto n = imp.import_vertices(file);
+        cout << "Loaded " << n << " vertices from " << o.get() << endl;
+        o = take_argument(para, "-v");
+    }
+
+    // IMPORT EDGES
+    o = take_argument(para, "-e");
+    while (o.is_present()) {
+        std::fstream file(o.get());
+        // cout << "Importing edges from file: " << o.get() << endl;
+        auto n = imp.import_edges(file);
+        cout << "Loaded " << n << " edges from " << o.get() << endl;
+        o = take_argument(para, "-e");
+    }
+}
+
+void fill_with_bt(unordered_map<string, double> &values, Vertex::Accessor &com,
+                  double weight,
+                  PropertyFamily::PropertyType::PropertyTypeKey<ArrayString>
+                      &prop_vertex_business_types)
+{
+    auto bus_t = com.at(prop_vertex_business_types);
+    if (bus_t.is_present()) {
+        for (auto &bt : *bus_t.get()) {
+            values[bt] += weight;
+        }
+    }
+}
+
+void oportunity_employe_company(
+    Vertex::Accessor &va, unordered_map<string, double> &values, double weight,
+    PropertyFamily::PropertyType::PropertyTypeKey<ArrayString>
+        &prop_vertex_business_types,
+    const EdgeType &type_created, const EdgeType &type_works_in,
+    const Label &label_company)
+{
+    iter::for_all_fill(va.in(), [&](auto opp_e) {
+        // cout << "                       oec.in()" << endl;
+        from_fill(opp_e, type_created, [&](auto creator) {
+            // cout << "                           type_created" << endl;
+            iter::for_all_fill(creator.out(), [&](auto creator_e) {
+                // cout << "                               creator.out()" <<
+                // endl;
+                to_fill(creator_e, type_works_in, label_company,
+                        [&](auto end_com) {
+                            // cout << " fill_bt"
+                            //      << endl;
+                            fill_with_bt(values, end_com, weight,
+                                         prop_vertex_business_types);
+                        });
+            });
+
+        });
+    });
+}
+
+auto query(DbAccessor &t, const Id &start_id)
+{
+    // DbAccessor t(db);
+    unordered_map<string, double> values;
+
+    const Label &label_company = t.label_find_or_create("Company");
+    const Label &label_opportunuty = t.label_find_or_create("Opportunity");
+
+    auto type_works_in = t.type_find_or_create("Works_In");
+    auto type_reached_to = t.type_find_or_create("Reached_To");
+    auto type_partnered_with = t.type_find_or_create("Partnered_With");
+    auto type_interested_in = t.type_find_or_create("Interested_In");
+    auto type_viewed = t.type_find_or_create("Viewed");
+    auto type_has_match = t.type_find_or_create("Has_Match");
+    auto type_searched_and_clicked =
+        t.type_find_or_create("Searched_And_Clicked");
+    auto type_is_employee = t.type_find_or_create("Is_Employee");
+    auto type_created = t.type_find_or_create("Created");
+
+    auto prop_edge_status = t.edge_property_family_get("status")
+                                .get(Flags::String)
+                                .type_key<String>();
+    auto prop_edge_count =
+        t.edge_property_family_get("count").get(Flags::Int32).type_key<Int32>();
+    auto prop_edge_feedback = t.edge_property_family_get("feedback")
+                                  .get(Flags::String)
+                                  .type_key<String>();
+
+    auto prop_vertex_business_types =
+        t.vertex_property_family_get("business_types")
+            .get(Flags::ArrayString)
+            .type_key<ArrayString>();
+
+    auto osva = t.vertex_find(start_id);
+    if (!option_fill(osva)) {
+        cout << "Illegal start vertex" << endl;
+        return values;
+    }
+    auto start = osva.take();
+
+    // PARTNERS
+    iter::for_all_fill(start.out(), [&](auto e) {
+        // cout << "start.out()" << endl;
+        to_fill(e, type_partnered_with, label_company, [&](auto end_com) {
+            fill_with_bt(values, end_com, 0.9, prop_vertex_business_types);
+        });
+    });
+
+    // PERSONELS
+    iter::for_all(start.in(), [&](auto e) {
+        // cout << "start.in()" << endl;
+        fill_from_fill(e, type_works_in, [&](auto employ) {
+            // cout << "   type_works_in" << endl;
+            iter::for_all_fill(employ.out(), [&](auto employ_edge) {
+                // cout << "       employ.out()" << endl;
+                auto ee_type = employ_edge.edge_type();
+                // cout << "       ee_type: " << ee_type << endl;
+
+                if (ee_type == type_interested_in) {
+                    // cout << "           type_interested_in" << endl;
+                    // INTERESTED IN OPPORTUNUTIES
+                    to_fill(employ_edge, label_opportunuty, [&](auto opp) {
+                        oportunity_employe_company(
+                            opp, values, 1, prop_vertex_business_types,
+                            type_created, type_works_in, label_company);
+
+                    });
+
+                } else if (ee_type == type_created) {
+                    // cout << "           type_created" << endl;
+                    // CREATED OPPORTUNUTIES
+                    to_fill(employ_edge, label_opportunuty, [&](auto opp) {
+                        iter::for_all_fill(opp.out(), [&](auto edge) {
+                            auto feedback = edge.at(prop_edge_feedback);
+                            if (!feedback.is_present()) {
+                                return;
+                            }
+
+                            auto str = feedback.get()->c_str();
+                            double weight = 0;
+                            if (strcasecmp(str, "like") == 0) {
+                                weight = 1;
+                            } else if (strcasecmp(str, "dislike") == 0) {
+                                weight = -1;
+                            } else {
+                                return;
+                            }
+
+                            to_fill(edge, label_company, [&](auto end_com) {
+                                fill_with_bt(values, end_com, weight,
+                                             prop_vertex_business_types);
+                            });
+                        });
+                    });
+
+                } else {
+                    // cout << "           company" << endl;
+                    // COMPANY
+                    double weight = 0;
+                    if (ee_type == type_reached_to) {
+                        auto os = employ_edge.at(prop_edge_status);
+                        if (!os.is_present()) {
+                            return;
+                        }
+                        auto str = os.get()->c_str();
+
+                        if (strcasecmp(str, "pending") == 0) {
+                            weight = 0.5;
+                        } else if (strcasecmp(str, "connected") == 0) {
+                            weight = 1;
+                        } else if (strcasecmp(str, "unreachable") == 0) {
+                            weight = 0.5;
+                        } else if (strcasecmp(str, "not_a_match") == 0) {
+                            weight = -1;
+                        } else {
+                            cout << "unknown status: " << str << endl;
+                        }
+                    } else if (ee_type == type_viewed ||
+                               ee_type == type_searched_and_clicked) {
+                        auto count = employ_edge.at(prop_edge_count);
+                        if (count.is_present()) {
+                            weight = 0.01 * (*count.get());
+                        }
+                    }
+
+                    // TARGET COMPANY
+                    if (weight != 0) {
+                        to_fill(employ_edge, [&](auto t_com) {
+                            fill_with_bt(values, t_com, weight,
+                                         prop_vertex_business_types);
+                        });
+                    }
+                }
+            });
+        });
+    });
+
+    return values;
+}
+
+Option<Id> find_company(DbAccessor &t, int64_t cid)
+{
+    // DbAccessor t(db);
+
+    Option<Id> found;
+
+    auto prop_vertex_company_id = t.vertex_property_family_get("company_id")
+                                      .get(Flags::Int64)
+                                      .type_key<Int64>();
+    const Label &label_company = t.label_find_or_create("Company");
+
+    iter::find_fill(label_company.index->for_range_exact(t), [&](auto v) {
+        if (v.has_label(label_company)) {
+            auto id = v.at(prop_vertex_company_id);
+            if (id.is_present()) {
+                if ((*id.get()) == cid) {
+                    found = Option<Id>(v.id());
+                    return true;
+                }
+            }
+        }
+        return false;
+    });
+
+    return found;
+}
+
+int main(int argc, char **argv)
+{
+    auto para = all_arguments(argc, argv);
+    Db db;
+    {
+        DbAccessor t(db);
+
+        load(t, para);
+
+        t.commit();
+    }
+
+    {
+        DbAccessor t(db);
+
+        // for (int i = 0; i < 100; i++)
+        //     make_transactions(db);
+
+        // string line;
+        // while(std::getline(file, line))
+
+        int n = 300 * 1000;
+        vector<pair<Vertex::Accessor, unordered_map<string, double>>> coll;
+
+        auto begin = clock();
+        int i = 0;
+
+        iter::for_all_fill(
+            t.label_find_or_create("Company").index->for_range_exact(t),
+            [&](auto v) {
+                if (i < n) {
+                    coll.push_back(make_pair(v, query(t, v.id())));
+                }
+                i++;
+            });
+        n = i;
+        clock_t end = clock();
+        double elapsed_s = (double(end - begin) / CLOCKS_PER_SEC);
+
+        if (n == 0) {
+            cout << "No companys" << endl;
+            return 0;
+        }
+
+        cout << endl
+             << "Query duration: " << (elapsed_s / n) * 1000 * 1000 << " [us]"
+             << endl;
+        cout << "Throughput: " << 1 / (elapsed_s / n) << " [query/sec]" << endl;
+
+        auto res = coll.back(); // query(t, fid.get());
+        while (res.second.empty()) {
+            coll.pop_back();
+            res = coll.back();
+        }
+
+        auto prop_vertex_id = t.vertex_property_family_get("company_id")
+                                  .get(Flags::Int64)
+                                  .type_key<Int64>();
+        cout << endl
+             << "Example: " << *res.first.at(prop_vertex_id).get() << endl;
+        for (auto e : res.second) {
+            cout << e.first << " = " << e.second << endl;
+        }
+
+        double sum = 0;
+        for (auto r : coll) {
+            for (auto e : r.second) {
+                sum += e.second;
+            }
+        }
+
+        cout << endl << endl << "Compiler sum " << sum << endl;
+        t.commit();
+    }
+    // usleep(1000 * 1000 * 60);
+
+    return 0;
+}