diff --git a/CMakeLists.txt b/CMakeLists.txt index 9efec42c3..a4cbc58b6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -213,10 +213,12 @@ FILE(COPY ${include_dir}/storage/model/properties/string.hpp DESTINATION ${build 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) +FILE(COPY ${include_dir}/storage/model/properties/property_family.hpp DESTINATION ${build_include_dir}/storage/model/properties) FILE(COPY ${include_dir}/storage/model/properties/utils/math_operations.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) FILE(COPY ${include_dir}/storage/model/properties/utils/unary_negation.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) FILE(COPY ${include_dir}/storage/model/properties/utils/modulo.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils) + FILE(COPY ${include_dir}/storage/model/edge_model.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/model/property_model.hpp DESTINATION ${build_include_dir}/storage/model) FILE(COPY ${include_dir}/storage/model/vertex_model.hpp DESTINATION ${build_include_dir}/storage/model) @@ -401,6 +403,8 @@ set(memgraph_src_files ${src_dir}/storage/model/properties/bool.cpp ${src_dir}/storage/model/properties/string.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 ${src_dir}/storage/locking/record_lock.cpp ${src_dir}/storage/vertex_accessor.cpp ${src_dir}/transactions/transaction.cpp @@ -416,6 +420,8 @@ set(memgraph_src_files ${src_dir}/database/db_accessor.cpp ${src_dir}/database/db_transaction.cpp ${src_dir}/storage/edge_accessor.cpp + ${src_dir}/storage/record_accessor.cpp + ) # STATIC library used by memgraph executables diff --git a/include/data_structures/bitset/dynamic_bitset.hpp b/include/data_structures/bitset/dynamic_bitset.hpp index c228e9571..43024fade 100644 --- a/include/data_structures/bitset/dynamic_bitset.hpp +++ b/include/data_structures/bitset/dynamic_bitset.hpp @@ -1,7 +1,7 @@ #pragma once -#include <cassert> #include <atomic> +#include <cassert> #include "threading/sync/lockable.hpp" #include "threading/sync/spinlock.hpp" @@ -13,8 +13,8 @@ class DynamicBitset : Lockable<SpinLock> { Block() = default; - Block(Block&) = delete; - Block(Block&&) = delete; + Block(Block &) = delete; + Block(Block &&) = delete; static constexpr size_t size = sizeof(block_t) * 8; @@ -41,7 +41,7 @@ class DynamicBitset : Lockable<SpinLock> block.fetch_and(~(bitmask(n) << k), order); } - std::atomic<block_t> block {0}; + std::atomic<block_t> block{0}; }; struct Chunk @@ -49,16 +49,13 @@ class DynamicBitset : Lockable<SpinLock> Chunk() : next(nullptr) { static_assert(chunk_size % sizeof(block_t) == 0, - "chunk size not divisible by block size"); + "chunk size not divisible by block size"); } - Chunk(Chunk&) = delete; - Chunk(Chunk&&) = delete; + Chunk(Chunk &) = delete; + Chunk(Chunk &&) = delete; - ~Chunk() - { - delete next; - } + ~Chunk() { delete next; } static constexpr size_t size = chunk_size * Block::size; static constexpr size_t n_blocks = chunk_size / sizeof(block_t); @@ -79,54 +76,62 @@ class DynamicBitset : Lockable<SpinLock> } Block blocks[n_blocks]; - std::atomic<Chunk*> next; + std::atomic<Chunk *> next; }; public: DynamicBitset() : head(new Chunk()) {} - DynamicBitset(DynamicBitset&) = delete; - DynamicBitset(DynamicBitset&&) = delete; + DynamicBitset(DynamicBitset &) = delete; + DynamicBitset(DynamicBitset &&) = delete; + + ~DynamicBitset() + { + auto now = head.load(); + while (now != nullptr) { + auto next = now->next.load(); + delete now; + now = next; + } + } block_t at(size_t k, size_t n) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.at(k, n, std::memory_order_seq_cst); } bool at(size_t k) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.at(k, 1, std::memory_order_seq_cst); } void set(size_t k, size_t n = 1) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.set(k, n, std::memory_order_seq_cst); } void clear(size_t k, size_t n = 1) { - auto& chunk = find_chunk(k); + auto &chunk = find_chunk(k); return chunk.clear(k, n, std::memory_order_seq_cst); } private: - Chunk& find_chunk(size_t& k) + Chunk &find_chunk(size_t &k) { - Chunk* chunk = head.load(), *next = nullptr; + Chunk *chunk = head.load(), *next = nullptr; // while i'm not in the right chunk // (my index is bigger than the size of this chunk) - while(k >= Chunk::size) - { + while (k >= Chunk::size) { next = chunk->next.load(); // if a next chunk exists, switch to it and decrement my // pointer by the size of the current chunk - if(next != nullptr) - { + if (next != nullptr) { chunk = next; k -= Chunk::size; continue; @@ -139,8 +144,7 @@ private: // double-check locking. if the chunk exists now, some other thread // has just created it, continue searching for my chunk - if(chunk->next.load() != nullptr) - continue; + if (chunk->next.load() != nullptr) continue; chunk->next.store(new Chunk()); } @@ -149,5 +153,5 @@ private: return *chunk; } - std::atomic<Chunk*> head; + std::atomic<Chunk *> head; }; diff --git a/include/data_structures/concurrent/concurrent_list.hpp b/include/data_structures/concurrent/concurrent_list.hpp new file mode 100644 index 000000000..0e364a325 --- /dev/null +++ b/include/data_structures/concurrent/concurrent_list.hpp @@ -0,0 +1,284 @@ +#pragma once + +#include <atomic> +#include <cassert> +#include "utils/crtp.hpp" + +template <class T> +class List +{ + + template <class V> + static V load(std::atomic<V> &atomic) + { + return atomic.load(std::memory_order_acquire); + } + + template <class V> + static void store(std::atomic<V> &atomic, V desired) + { // Maybe could be relaxed + atomic.store(desired, std::memory_order_release); + } + + template <class V> + static bool cas(std::atomic<V> &atomic, V expected, V desired) + { // Could be relaxed must be atleast Release. + return atomic.compare_exchange_strong(expected, desired, + std::memory_order_seq_cst); + } + + template <class V> + static V *swap(std::atomic<V *> &atomic, V *desired) + { // Could be relaxed + return atomic.exchange(desired, std::memory_order_seq_cst); + } + +private: + class Node + { + public: + Node(const T &data) : data(data) {} + Node(T &&data) : data(std::move(data)) {} + + T data; + std::atomic<Node *> next{nullptr}; + std::atomic<Node *> next_rem{nullptr}; + std::atomic<bool> removed{false}; + }; + + template <class It> + class IteratorBase : public Crtp<It> + { + friend class List; + + protected: + IteratorBase() : list(nullptr), curr(nullptr) {} + + IteratorBase(List *list) : list(list) + { + assert(list != nullptr); + list->count++; + reset(); + } + + public: + IteratorBase(const IteratorBase &) = delete; + + IteratorBase(IteratorBase &&other) + : list(other.list), curr(other.curr), prev(other.prev) + { + other.list = nullptr; + other.curr = nullptr; + } + + ~IteratorBase() + { + if (list == nullptr) { + return; + } + + auto head_rem = load(list->removed); + // Fetch could be relaxed + // There exist possibility that no one will delete garbage at this + // time. + if (list->count.fetch_sub(1) == 1 && head_rem != nullptr && + cas<Node *>( + list->removed, head_rem, + nullptr)) { // I am the last one and there is garbage to be + // removed. + auto now = head_rem; + do { + auto next = load(now->next_rem); + delete now; + now = next; + } while (now != nullptr); + } + } + + T &operator*() const + { + assert(valid()); + return curr->data; + } + T *operator->() const + { + assert(valid()); + return &(curr->data); + } + + bool valid() const { return curr != nullptr; } + + // Iterating is wait free. + It &operator++() + { + assert(valid()); + do { + prev = curr; + curr = load(curr->next); + } while (valid() && is_removed()); + return this->derived(); + } + It &operator++(int) { return operator++(); } + + bool is_removed() + { + assert(valid()); + return load(curr->removed); + } + + // Returns IteratorBase to begining + void reset() + { + prev = nullptr; + curr = load(list->head); + while (valid() && is_removed()) { + operator++(); + } + } + + // Adds to the begining of list + // It is lock free but it isn't wait free. + void push(T &&data) + { + auto node = new Node(data); + Node *next = nullptr; + do { + next = load(list->head); + store(node->next, next); + } while (!cas(list->head, next, node)); + } + + // True only if this call removed the element. Only reason for fail is + // if + // the element is already removed. + // Remove has deadlock if another thread dies between marking node for + // removal + // and the disconnection. + // This can be improved with combinig the removed flag with prev.next or + // curr.next + bool remove() + { + assert(valid()); + if (cas(curr->removed, false, true)) { + if (!disconnect()) { + find_and_disconnect(); + } + store(curr->next_rem, swap(list->removed, curr)); + return true; + } + return false; + } + + friend bool operator==(const It &a, const It &b) + { + return a.curr == b.curr; + } + + friend bool operator!=(const It &a, const It &b) { return !(a == b); } + + private: + void find_and_disconnect() + { + auto it = It(list); + auto next = load(curr->next); + while (it.valid()) { + if (it.curr == curr) { + if (it.disconnect()) { + return; + } + it.reset(); + } else if (it.curr == next) { // Comparison with next is + // optimization for early return. + return; + } else { + it++; + } + } + } + + bool disconnect() + { + auto next = load(curr->next); + if (prev != nullptr) { + store(prev->next, next); + if (load(prev->removed)) { + return false; + } + } else if (!cas(list->head, curr, next)) { + return false; + } + return true; + } + + List *list; + Node *prev{nullptr}; + Node *curr; + }; + +public: + class ConstIterator : public IteratorBase<ConstIterator> + { + friend class List; + + public: + using IteratorBase<ConstIterator>::IteratorBase; + + const T &operator*() const + { + return IteratorBase<ConstIterator>::operator*(); + } + + const T *operator->() const + { + return IteratorBase<ConstIterator>::operator->(); + } + + operator const T &() const + { + return IteratorBase<ConstIterator>::operator T &(); + } + }; + + class Iterator : public IteratorBase<Iterator> + { + friend class List; + + public: + using IteratorBase<Iterator>::IteratorBase; + }; + +public: + List() = default; + + List(List &) = delete; + List(List &&) = delete; + + ~List() + { + auto now = head.load(); + while (now != nullptr) { + auto next = now->next.load(); + delete now; + now = next; + } + } + + void operator=(List &) = delete; + + Iterator begin() { return Iterator(this); } + + // ConstIterator begin() { return ConstIterator(this); } + + ConstIterator cbegin() { return ConstIterator(this); } + + Iterator end() { return Iterator(); } + + // ConstIterator end() { return ConstIterator(); } + + ConstIterator cend() { return ConstIterator(); } + +private: + std::atomic<std::size_t> count{0}; + std::atomic<Node *> head{nullptr}; + std::atomic<Node *> removed{nullptr}; +}; diff --git a/include/data_structures/concurrent/concurrent_set.hpp b/include/data_structures/concurrent/concurrent_set.hpp index c550c081b..036463ad2 100644 --- a/include/data_structures/concurrent/concurrent_set.hpp +++ b/include/data_structures/concurrent/concurrent_set.hpp @@ -30,7 +30,7 @@ public: std::pair<list_it, bool> insert(T &&item) { - return accessor.insert(std::forward<T>(item)); + return accessor.insert(std::move(item)); } list_it_con find(const T &item) const { return accessor.find(item); } diff --git a/include/data_structures/concurrent/skiplist.hpp b/include/data_structures/concurrent/skiplist.hpp index a0367a57c..2722cc343 100644 --- a/include/data_structures/concurrent/skiplist.hpp +++ b/include/data_structures/concurrent/skiplist.hpp @@ -157,7 +157,7 @@ public: // we have raw memory and we need to construct an object // of type Node on it - return new (node) Node(std::forward<T>(item), height); + return new (node) Node(std::move(item), height); } static void destroy(Node *node) @@ -182,7 +182,7 @@ public: Node(T &&data, uint8_t height) : Node(height) { - this->data.set(std::forward<T>(data)); + this->data.set(std::move(data)); } ~Node() @@ -522,7 +522,7 @@ public: std::pair<Iterator, bool> insert(T &&item) { - return skiplist->insert(std::forward<T>(item), preds, succs); + return skiplist->insert(std::move(item), preds, succs); } Iterator insert_non_unique(const T &item) @@ -683,7 +683,7 @@ private: static bool lock_nodes(uint8_t height, guard_t guards[], Node *preds[], Node *succs[]) { - Node *prepred, *pred, *succ = nullptr; + Node *prepred = nullptr, *pred = nullptr, *succ = nullptr; bool valid = true; for (int level = 0; valid && level < height; ++level) { @@ -790,8 +790,7 @@ private: // has the locks if (!lock_nodes<true>(height, guards, preds, succs)) continue; - return {insert_here(std::forward<T>(data), preds, succs, height, - guards), + return {insert_here(std::move(data), preds, succs, height, guards), true}; } } @@ -801,7 +800,7 @@ private: guard_t guards[]) { // you have the locks, create a new node - auto new_node = Node::create(std::forward<T>(data), height); + auto new_node = Node::create(std::move(data), height); // link the predecessors and successors, e.g. // diff --git a/include/database/db_accessor.hpp b/include/database/db_accessor.hpp index e4cbd86a6..be19ba520 100644 --- a/include/database/db_accessor.hpp +++ b/include/database/db_accessor.hpp @@ -1,15 +1,20 @@ #pragma once -#include "database/db.hpp" -#include "database/db_accessor.hpp" -#include "storage/record_accessor.hpp" -#include "storage/vertex.hpp" +#include "database/db_transaction.hpp" +// #include "storage/record_accessor.hpp" +// #include "storage/vertex.hpp" #include "storage/vertex_accessor.hpp" -#include "storage/vertices.hpp" -#include "transactions/transaction.hpp" -#include "utils/iterator/iterator.hpp" +// #include "storage/vertices.hpp" +// #include "transactions/transaction.hpp" +// #include "utils/iterator/iterator.hpp" +#include "utils/border.hpp" #include "utils/option.hpp" +namespace tx +{ +class Transaction; +} + /* * DbAccessor * -Guarantees that access to Vertex and Edge is possible only through @@ -65,21 +70,31 @@ public: bool label_contains(const std::string &name); - VertexIndexRecordCollection &label_find_index(const Label &label); - //********************TYPE METHODS const EdgeType &type_find_or_create(const std::string &name); bool type_contains(const std::string &name); + //********************PROPERTY METHODS + // Vertices::prop_familys_t::Accessor vertex_property_family_access(); + // + // auto edge_property_family_access(); + + PropertyFamily &vertex_property_family_get(const std::string &name); + + PropertyFamily &edge_property_family_get(const std::string &name); + //********************TRANSACTION METHODS void commit(); void abort(); private: - DbTransaction db; + template <class T, class K> + friend class NonUniqueUnorderedIndex; + + DbTransaction db_transaction; }; //**********************CONVENIENT FUNCTIONS diff --git a/include/database/db_transaction.hpp b/include/database/db_transaction.hpp index a7b862c82..a36dd1d18 100644 --- a/include/database/db_transaction.hpp +++ b/include/database/db_transaction.hpp @@ -1,7 +1,5 @@ #pragma once -#include "storage/indexes/index_record.hpp" -#include "storage/label/label.hpp" #include "transactions/transaction.hpp" class Db; @@ -9,6 +7,8 @@ class DbAccessor; // Inner structures local to transaction can hold ref to this structure and use // its methods. +// Also serves as a barrier for calling methods defined public but meant for +// internal use. That kind of method should request DbTransaction&. class DbTransaction { friend DbAccessor; @@ -16,8 +16,10 @@ class DbTransaction public: DbTransaction(Db &db, tx::Transaction &trans) : db(db), trans(trans) {} - void update_label_index(const Label &label, - VertexIndexRecord &&index_record); + // Global transactional algorithms,operations and general methods meant for + // internal use should be here or should be routed through this object. + // This should provide cleaner hierarchy of operations on database. + // For example cleaner. tx::Transaction &trans; diff --git a/include/query_engine/hardcode/queries.hpp b/include/query_engine/hardcode/queries.hpp index 5ef8dbfe5..f413ae9bd 100644 --- a/include/query_engine/hardcode/queries.hpp +++ b/include/query_engine/hardcode/queries.hpp @@ -5,8 +5,11 @@ #include "database/db_accessor.hpp" #include "query_engine/query_stripper.hpp" #include "query_engine/util.hpp" +#include "storage/indexes/impl/nonunique_unordered_index.cpp" #include "storage/model/properties/property.hpp" +#include "storage/model/properties/property_family.hpp" #include "utils/command_line/arguments.hpp" +#include "utils/iterator/iterator.hpp" auto load_queries(Db &db) { @@ -15,8 +18,12 @@ auto load_queries(Db &db) // CREATE (n {prop: 0}) RETURN n) auto create_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_key = t.vertex_property_family_get("prop") + .get(args[0]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("prop", args[0]); + vertex_accessor.set(prop_key, args[0]); t.commit(); return true; }; @@ -24,8 +31,12 @@ auto load_queries(Db &db) auto create_labeled_and_named_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_key = t.vertex_property_family_get("name") + .get(args[0]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("name", args[0]); + vertex_accessor.set(prop_key, args[0]); auto &label = t.label_find_or_create("LABEL"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); @@ -35,11 +46,23 @@ auto load_queries(Db &db) auto create_account = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_id = + t.vertex_property_family_get("id").get(args[0]->flags).family_key(); + auto prop_name = t.vertex_property_family_get("name") + .get(args[1]->flags) + .family_key(); + auto prop_country = t.vertex_property_family_get("country") + .get(args[2]->flags) + .family_key(); + auto prop_created = t.vertex_property_family_get("created_at") + .get(args[3]->flags) + .family_key(); + auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", args[0]); - vertex_accessor.property("name", args[1]); - vertex_accessor.property("country", args[2]); - vertex_accessor.property("created_at", args[3]); + vertex_accessor.set(prop_id, args[0]); + vertex_accessor.set(prop_name, args[1]); + vertex_accessor.set(prop_country, args[2]); + vertex_accessor.set(prop_created, args[3]); auto &label = t.label_find_or_create("ACCOUNT"); vertex_accessor.add_label(label); cout_properties(vertex_accessor.properties()); @@ -49,8 +72,7 @@ auto load_queries(Db &db) auto find_node_by_internal_id = [&db](const properties_t &args) { DbAccessor t(db); - auto id = static_cast<Int32 &>(*args[0]); - auto maybe_va = t.vertex_find(Id(id.value)); + auto maybe_va = t.vertex_find(Id(args[0]->as<Int32>().value)); if (!option_fill(maybe_va)) { cout << "vertex doesn't exist" << endl; t.commit(); @@ -117,12 +139,15 @@ auto load_queries(Db &db) auto update_node = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_name = t.vertex_property_family_get("name") + .get(args[1]->flags) + .family_key(); auto maybe_v = t.vertex_find(args[0]->as<Int32>().value); if (!option_fill(maybe_v)) return t.commit(), false; auto v = maybe_v.get(); - v.property("name", args[1]); + v.set(prop_name, args[1]); cout_properties(v.properties()); t.commit(); @@ -134,13 +159,19 @@ auto load_queries(Db &db) // weight: 70}]-(n2) RETURN r auto create_edge_v2 = [&db](const properties_t &args) { DbAccessor t(db); + auto prop_age = + t.edge_property_family_get("age").get(args[2]->flags).family_key(); + auto prop_weight = t.edge_property_family_get("weight") + .get(args[3]->flags) + .family_key(); + auto n1 = t.vertex_find(args[0]->as<Int64>().value); if (!option_fill(n1)) return t.commit(), false; auto n2 = t.vertex_find(args[1]->as<Int64>().value); if (!option_fill(n2)) return t.commit(), false; auto r = t.edge_insert(n2.get(), n1.get()); - r.property("age", args[2]); - r.property("weight", args[3]); + r.set(prop_age, args[2]); + r.set(prop_weight, args[3]); auto &IS = t.type_find_or_create("IS"); r.edge_type(IS); @@ -173,13 +204,12 @@ auto load_queries(Db &db) DbAccessor t(db); auto &label = t.label_find_or_create("LABEL"); + auto prop_key = + t.vertex_property_family_get("name").get(Type::String).family_key(); - auto &index_record_collection = t.label_find_index(label); - auto accessor = index_record_collection.access(); cout << "VERTICES" << endl; - for (auto &v : accessor) { - cout << v.record->data.props.at("name").as<String>().value << endl; - } + iter::for_all(label.index->for_range_exact(t), + [&](auto a) { cout << a.at(prop_key) << endl; }); // TODO // db.graph.vertices.fileter("LABEL").all(t, handler); diff --git a/include/query_engine/util.hpp b/include/query_engine/util.hpp index 76bf1e5a6..40b5b5a57 100644 --- a/include/query_engine/util.hpp +++ b/include/query_engine/util.hpp @@ -40,5 +40,4 @@ std::string code_line(const std::string &format_str, const Args &... args) { return "\t" + format(format_str, args...) + "\n"; } - } diff --git a/include/storage/edges.hpp b/include/storage/edges.hpp index 660962be8..e980675b6 100644 --- a/include/storage/edges.hpp +++ b/include/storage/edges.hpp @@ -1,13 +1,17 @@ #pragma once +#include <string> #include "data_structures/concurrent/concurrent_map.hpp" #include "mvcc/version_list.hpp" #include "storage/common.hpp" #include "storage/edge_accessor.hpp" +#include "storage/model/properties/property_family.hpp" #include "utils/option.hpp" class Edges { + using prop_familys_t = ConcurrentMap<std::string, PropertyFamily *>; + public: Option<const Edge::Accessor> find(DbTransaction &t, const Id &id); @@ -15,7 +19,15 @@ public: Edge::Accessor insert(DbTransaction &t, VertexRecord *from, VertexRecord *to); + // auto property_family_access(); + + PropertyFamily &property_family_find_or_create(const std::string &name); + private: ConcurrentMap<uint64_t, EdgeRecord> edges; + // TODO: Because familys wont be removed this could be done with more + // efficent + // data structure. + prop_familys_t prop_familys; AtomicCounter<uint64_t> counter; }; diff --git a/include/storage/indexes/impl/nonunique_unordered_index.hpp b/include/storage/indexes/impl/nonunique_unordered_index.hpp new file mode 100644 index 000000000..01bebb1c5 --- /dev/null +++ b/include/storage/indexes/impl/nonunique_unordered_index.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "storage/indexes/index_base.hpp" +#include "storage/indexes/index_record.hpp" + +#include "data_structures/concurrent/concurrent_list.hpp" + +template <class T, class K> +class NonUniqueUnorderedIndex : public IndexBase<T, K> +{ +public: + typedef T value_type; + typedef K key_type; + + NonUniqueUnorderedIndex(); + + // Insert's value. + // nonunique => always succeds. + bool insert(IndexRecord<T, K> &&value) final; + + // Returns iterator which returns valid records in range. + // ordered==None => doesn't guarantee any order of submitting records. + std::unique_ptr<IteratorBase<const typename T::Accessor>> + for_range(DbAccessor &t, Border<K> from = Border<K>(), + Border<K> to = Border<K>()) final; + + // Same as for_range just whit known returned iterator. + auto for_range_exact(DbAccessor &t, Border<K> from = Border<K>(), + Border<K> to = Border<K>()); + + // Removes for all transactions obsolete Records. + // Cleaner has to call this method when he decideds that it is time for + // cleaning. + void clean(DbTransaction &) final; + +private: + List<IndexRecord<T, K>> list; +}; diff --git a/include/storage/indexes/index.hpp b/include/storage/indexes/index.hpp index 4b24e65a3..87007f0a7 100644 --- a/include/storage/indexes/index.hpp +++ b/include/storage/indexes/index.hpp @@ -1,44 +1,44 @@ -#pragma once - -#include <memory> - -#include "data_structures/concurrent/concurrent_map.hpp" -#include "storage/indexes/index_record.hpp" -#include "storage/indexes/index_record_collection.hpp" -#include "storage/label/label.hpp" - -template <class Key, class Item> -class Index -{ - public: - using container_t = ConcurrentMap<Key, Item>; - - Index() : index(std::make_unique<container_t>()) {} - - auto update(const Label &label, VertexIndexRecord &&index_record) - { - auto accessor = index->access(); - auto label_ref = label_ref_t(label); - - // create Index Record Collection if it doesn't exist - if (!accessor.contains(label_ref)) { - accessor.insert(label_ref, std::move(VertexIndexRecordCollection())); - } - - // add Vertex Index Record to the Record Collection - auto &record_collection = (*accessor.find(label_ref)).second; - record_collection.add(std::forward<VertexIndexRecord>(index_record)); - } - - VertexIndexRecordCollection &find(const Label &label) - { - // TODO: accessor should be outside? - // bacause otherwise GC could delete record that has just be returned - auto label_ref = label_ref_t(label); - auto accessor = index->access(); - return (*accessor.find(label_ref)).second; - } - - private: - std::unique_ptr<container_t> index; -}; +// #pragma once +// +// #include <memory> +// +// #include "data_structures/concurrent/concurrent_map.hpp" +// #include "storage/indexes/index_record.hpp" +// #include "storage/indexes/index_record_collection.hpp" +// #include "storage/label/label.hpp" +// +// template <class Key, class Item> +// class Index +// { +// public: +// using container_t = ConcurrentMap<Key, Item>; +// +// Index() : index(std::make_unique<container_t>()) {} +// +// auto update(const Label &label, VertexIndexRecord &&index_record) +// { +// auto accessor = index->access(); +// auto label_ref = label_ref_t(label); +// +// // create Index Record Collection if it doesn't exist +// if (!accessor.contains(label_ref)) { +// accessor.insert(label_ref, std::move(VertexIndexRecordCollection())); +// } +// +// // add Vertex Index Record to the Record Collection +// auto &record_collection = (*accessor.find(label_ref)).second; +// record_collection.add(std::forward<VertexIndexRecord>(index_record)); +// } +// +// VertexIndexRecordCollection &find(const Label &label) +// { +// // TODO: accessor should be outside? +// // bacause otherwise GC could delete record that has just be returned +// auto label_ref = label_ref_t(label); +// auto accessor = index->access(); +// return (*accessor.find(label_ref)).second; +// } +// +// private: +// std::unique_ptr<container_t> index; +// }; diff --git a/include/storage/indexes/index_base.hpp b/include/storage/indexes/index_base.hpp new file mode 100644 index 000000000..aa7603848 --- /dev/null +++ b/include/storage/indexes/index_base.hpp @@ -0,0 +1,60 @@ +#pragma once + +// #include "storage/indexes/index_record.hpp" +#include <functional> +#include <memory> +#include "utils/border.hpp" +#include "utils/iterator/iterator_base.hpp" + +class DbTransaction; +class DbAccessor; + +template <class T, class K> +class IndexRecord; + +// Defines ordering of data +enum Order +{ + None = 0, + Ascending = 1, + Descending = 2, +}; + +// Interface for all indexes. +// T type of record. +// K type of key on which records are ordered +template <class T, class K> +class IndexBase +{ +public: + typedef T value_type; + typedef K key_type; + + IndexBase(bool unique, Order order) : unique(unique), order(order) {} + + // Insert's value. + // unique => returns false if there is already valid equal value. + // nonunique => always succeds. + virtual bool insert(IndexRecord<T, K> &&value) = 0; + + // Returns iterator which returns valid 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. + // order==Descending => guarantees order of returned records will be from + // largest to smallest. + // Range must be from<=to + virtual std::unique_ptr<IteratorBase<const typename T::Accessor>> + for_range(DbAccessor &, Border<K> from = Border<K>(), + Border<K> to = Border<K>()) = 0; + + // Removes for all transactions obsolete Records. + // Cleaner has to call this method when he decideds that it is time for + // cleaning. + virtual void clean(DbTransaction &) = 0; + + // Are the records unique + const bool unique; + // Ordering of the records. + const Order order; +}; diff --git a/include/storage/indexes/index_record.hpp b/include/storage/indexes/index_record.hpp index b7e63efaa..70f05af71 100644 --- a/include/storage/indexes/index_record.hpp +++ b/include/storage/indexes/index_record.hpp @@ -1,46 +1,67 @@ #pragma once +#include "database/db_transaction.hpp" #include "mvcc/version_list.hpp" #include "utils/total_ordering.hpp" -template <class T> -class IndexRecord : TotalOrdering<IndexRecord<T>> +// class DbTransaction; +// namespace tx +// { +// class Transaction; +// } + +// T type of record. +// K key on which record is ordered. +template <class T, class K> +class IndexRecord : public TotalOrdering<IndexRecord<T, K>> { public: using vlist_t = mvcc::VersionList<T>; IndexRecord() = default; - IndexRecord(T *record, vlist_t *vlist) : record(record), vlist(vlist) + IndexRecord(K key, T *record, vlist_t *vlist) + : key(std::move(key)), record(record), vlist(vlist) { assert(record != nullptr); assert(vlist != nullptr); } - friend bool operator<(const IndexRecord& lhs, const IndexRecord& rhs) + friend bool operator<(const IndexRecord &lhs, const IndexRecord &rhs) { - return lhs.record < rhs.record; + return lhs.key < rhs.key || + (lhs.key == rhs.key && lhs.vlist == rhs.vlist && + lhs.record < rhs.record); } - friend bool operator==(const IndexRecord& lhs, const IndexRecord& rhs) + friend bool operator==(const IndexRecord &lhs, const IndexRecord &rhs) { - return lhs.record == rhs.record; + return lhs.key == rhs.key && + (lhs.vlist != rhs.vlist || lhs.record == rhs.record); } bool empty() const { return record == nullptr; } - // const typename T::Accessor get() - // { - // // TODO: if somebody wants to read T content - // // const T::Accessor has to be returned from here - // // the problem is that here we don't have pointer to store - // // TODO: figure it out - // } + bool is_valid(tx::Transaction &t) const + { + assert(!empty()); + return record == vlist->find(t); + } -// private: + const auto access(DbTransaction &db) const + { + return T::Accessor::create(record, vlist, db); + } + + const K key; + +private: T *const record{nullptr}; vlist_t *const vlist{nullptr}; }; -using VertexIndexRecord = IndexRecord<Vertex>; -using EdgeIndexRecord = IndexRecord<Edge>; +template <class K> +using VertexIndexRecord = IndexRecord<Vertex, K>; + +template <class K> +using EdgeIndexRecord = IndexRecord<Edge, K>; diff --git a/include/storage/indexes/index_record_collection.hpp b/include/storage/indexes/index_record_collection.hpp index 0d06e2a91..53d4a1ba3 100644 --- a/include/storage/indexes/index_record_collection.hpp +++ b/include/storage/indexes/index_record_collection.hpp @@ -1,38 +1,38 @@ -#pragma once - -#include <memory> - -#include "data_structures/concurrent/concurrent_set.hpp" -#include "storage/indexes/index_record.hpp" - -template <class T> -class IndexRecordCollection -{ -public: - using index_record_t = IndexRecord<T>; - using index_record_collection_t = ConcurrentSet<index_record_t>; - - IndexRecordCollection() - : records(std::make_unique<index_record_collection_t>()) - { - } - - void add(index_record_t &&record) - { - auto accessor = records->access(); - accessor.insert(std::forward<index_record_t>(record)); - } - - auto access() - { - return records->access(); - } - - // TODO: iterator and proxy - -private: - std::unique_ptr<index_record_collection_t> records; -}; - -using VertexIndexRecordCollection = IndexRecordCollection<Vertex>; -using EdgeIndexRecordCollection = IndexRecordCollection<Edge>; +// #pragma once +// +// #include <memory> +// +// #include "data_structures/concurrent/concurrent_set.hpp" +// #include "storage/indexes/index_record.hpp" +// +// template <class T> +// class IndexRecordCollection +// { +// public: +// using index_record_t = IndexRecord<T>; +// using index_record_collection_t = ConcurrentSet<index_record_t>; +// +// IndexRecordCollection() +// : records(std::make_unique<index_record_collection_t>()) +// { +// } +// +// void add(index_record_t &&record) +// { +// auto accessor = records->access(); +// accessor.insert(std::forward<index_record_t>(record)); +// } +// +// auto access() +// { +// return records->access(); +// } +// +// // TODO: iterator and proxy +// +// private: +// std::unique_ptr<index_record_collection_t> records; +// }; +// +// using VertexIndexRecordCollection = IndexRecordCollection<Vertex>; +// using EdgeIndexRecordCollection = IndexRecordCollection<Edge>; diff --git a/include/storage/indexes/sort_order.hpp b/include/storage/indexes/sort_order.hpp index 82a60f61d..5d9348601 100644 --- a/include/storage/indexes/sort_order.hpp +++ b/include/storage/indexes/sort_order.hpp @@ -3,7 +3,7 @@ template <class T> struct Ascending { - constexpr bool operator()(const T& lhs, const T& rhs) const + constexpr bool operator()(const T &lhs, const T &rhs) const { return lhs < rhs; } @@ -12,7 +12,7 @@ struct Ascending template <class T> struct Descending { - constexpr bool operator()(const T& lhs, const T& rhs) const + constexpr bool operator()(const T &lhs, const T &rhs) const { return lhs > rhs; } diff --git a/include/storage/label/label.hpp b/include/storage/label/label.hpp index 97430fe5a..bcb478921 100644 --- a/include/storage/label/label.hpp +++ b/include/storage/label/label.hpp @@ -1,27 +1,39 @@ #pragma once -#include <stdint.h> #include <ostream> +#include <stdint.h> -#include "utils/total_ordering.hpp" +#include "storage/indexes/impl/nonunique_unordered_index.hpp" +#include "storage/vertex.hpp" +#include "storage/vertex_accessor.hpp" #include "utils/reference_wrapper.hpp" +#include "utils/total_ordering.hpp" +#include "utils/void.hpp" + +using LabelIndexRecord = VertexIndexRecord<std::nullptr_t>; class Label : public TotalOrdering<Label> { public: - Label(const std::string& name); - Label(std::string&& name); + using label_index_t = NonUniqueUnorderedIndex<Vertex, std::nullptr_t>; - Label(const Label&) = default; - Label(Label&&) = default; + Label() = delete; - friend bool operator<(const Label& lhs, const Label& rhs); + Label(const std::string &name); + Label(std::string &&name); - friend bool operator==(const Label& lhs, const Label& rhs); + Label(const Label &) = delete; + Label(Label &&other) = default; - friend std::ostream& operator<<(std::ostream& stream, const Label& label); + friend bool operator<(const Label &lhs, const Label &rhs); - operator const std::string&() const; + friend bool operator==(const Label &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; private: std::string name; diff --git a/include/storage/label/label_collection.hpp b/include/storage/label/label_collection.hpp index e52f5d7cb..c8ab924d9 100644 --- a/include/storage/label/label_collection.hpp +++ b/include/storage/label/label_collection.hpp @@ -2,7 +2,11 @@ #include <set> -#include "storage/label/label.hpp" +// #include "storage/label/label.hpp" +#include "utils/reference_wrapper.hpp" + +class Label; +using label_ref_t = ReferenceWrapper<const Label>; class LabelCollection { @@ -15,12 +19,12 @@ public: auto end() const; auto cend() const; - bool add(const Label& label); - bool has(const Label& label) const; + bool add(const Label &label); + bool has(const Label &label) const; size_t count() const; - bool remove(const Label& label); + bool remove(const Label &label); void clear(); - const std::set<label_ref_t>& operator()() const; + const std::set<label_ref_t> &operator()() const; private: std::set<label_ref_t> _labels; diff --git a/include/storage/model/properties/all.hpp b/include/storage/model/properties/all.hpp index 2bb5bb493..3d09e84d8 100644 --- a/include/storage/model/properties/all.hpp +++ b/include/storage/model/properties/all.hpp @@ -6,4 +6,3 @@ #include "storage/model/properties/int32.hpp" #include "storage/model/properties/int64.hpp" #include "storage/model/properties/string.hpp" - diff --git a/include/storage/model/properties/flags.hpp b/include/storage/model/properties/flags.hpp new file mode 100644 index 000000000..0997b0685 --- /dev/null +++ b/include/storage/model/properties/flags.hpp @@ -0,0 +1,45 @@ +#pragma once + +enum class Flags : unsigned +{ + // Type | Mask + // -----------+---------------------------------------- + // Null | 0000 0000 0000 0000 0000 0000 0000 0000 + // -----------+---------------------------------------- + // Bool | 0000 0000 0000 0000 0000 0000 0000 0001 + // + True | 0000 0000 0000 0000 0000 0000 0000 0011 + // + False | 0000 0000 0000 0000 0000 0000 0000 0101 + // -----------+---------------------------------------- + // String | 0000 0000 0000 0000 0000 0000 0000 1000 + // -----------+---------------------------------------- + // Number | 0000 0000 0000 0000 0000 0000 0001 0000 + // + Integral | 0000 0000 0000 0000 0000 0000 0011 0000 + // + Int32 | 0000 0000 0000 0000 0000 0000 0111 0000 + // + Int64 | 0000 0000 0000 0000 0000 0000 1011 0000 + // + Floating | 0000 0000 0000 0000 0000 0001 0001 0000 + // + Float | 0000 0000 0000 0000 0000 0011 0001 0000 + // + Double | 0000 0000 0000 0000 0000 0101 0001 0000 + // -----------+---------------------------------------- + // Array | 0000 0000 0000 0000 0001 0000 0000 0000 + // -----------+---------------------------------------- + + Null = 0x0, + Bool = 0x1, + True = 0x2 | Bool, + False = 0x4 | Bool, + + String = 0x8, + + Number = 0x10, + Integral = 0x20 | Number, + Int32 = 0x40 | Integral, + Int64 = 0x80 | Integral, + + Floating = 0x100 | Number, + Float = 0x200 | Floating, + Double = 0x400 | Floating, + + Array = 0x1000, + + type_mask = 0xFFF +}; diff --git a/include/storage/model/properties/handler.hpp b/include/storage/model/properties/handler.hpp index 2fab3abae..0a7bf790e 100644 --- a/include/storage/model/properties/handler.hpp +++ b/include/storage/model/properties/handler.hpp @@ -1,35 +1,35 @@ #pragma once -#include "storage/model/properties/property.hpp" #include "storage/model/properties/all.hpp" +#include "storage/model/properties/property.hpp" template <class Handler> void accept(const Property &property, Handler &h) { switch (property.flags) { - case Property::Flags::True: - return h.handle(static_cast<const Bool &>(property)); + case Flags::True: + return h.handle(static_cast<const Bool &>(property)); - case Property::Flags::False: - return h.handle(static_cast<const Bool &>(property)); + case Flags::False: + return h.handle(static_cast<const Bool &>(property)); - case Property::Flags::String: - return h.handle(static_cast<const String &>(property)); + case Flags::String: + return h.handle(static_cast<const String &>(property)); - case Property::Flags::Int32: - return h.handle(static_cast<const Int32 &>(property)); + case Flags::Int32: + return h.handle(static_cast<const Int32 &>(property)); - case Property::Flags::Int64: - return h.handle(static_cast<const Int64 &>(property)); + case Flags::Int64: + return h.handle(static_cast<const Int64 &>(property)); - case Property::Flags::Float: - return h.handle(static_cast<const Float &>(property)); + case Flags::Float: + return h.handle(static_cast<const Float &>(property)); - case Property::Flags::Double: - return h.handle(static_cast<const Double &>(property)); + case Flags::Double: + return h.handle(static_cast<const Double &>(property)); - default: - return; + default: + return; } } diff --git a/include/storage/model/properties/properties.hpp b/include/storage/model/properties/properties.hpp index b2c023af2..3165169bc 100644 --- a/include/storage/model/properties/properties.hpp +++ b/include/storage/model/properties/properties.hpp @@ -3,42 +3,49 @@ #include <map> #include "storage/model/properties/property.hpp" +#include "storage/model/properties/property_family.hpp" +#include "utils/option.hpp" + +using prop_key_t = PropertyFamily::PropertyType::PropertyFamilyKey; + +template <class T> +using type_key_t = PropertyFamily::PropertyType::PropertyTypeKey<T>; class Properties { public: using sptr = std::shared_ptr<Properties>; - auto begin() const { return props.begin(); } + auto begin() const { return props.begin(); } auto cbegin() const { return props.cbegin(); } - auto end() const { return props.end(); } + auto end() const { return props.end(); } auto cend() const { return props.cend(); } - size_t size() const - { - return props.size(); - } + size_t size() const { return props.size(); } - const Property& at(const std::string& key) const; + const Property &at(prop_key_t &key) const; + + template <class T> + auto at(type_key_t<T> &key) const; template <class T, class... Args> - void set(const std::string& key, Args&&... args); + void set(type_key_t<T> &key, Args &&... args); - void set(const std::string& key, Property::sptr value); + void set(prop_key_t &key, Property::sptr value); - void clear(const std::string& key); + void clear(prop_key_t &key); template <class Handler> - void accept(Handler& handler) const + void accept(Handler &handler) const { - for(auto& kv : props) + for (auto &kv : props) handler.handle(kv.first, *kv.second); handler.finish(); } private: - using props_t = std::map<std::string, Property::sptr>; + using props_t = std::map<prop_key_t, Property::sptr>; props_t props; }; diff --git a/include/storage/model/properties/property.hpp b/include/storage/model/properties/property.hpp index 43c818239..2299be164 100644 --- a/include/storage/model/properties/property.hpp +++ b/include/storage/model/properties/property.hpp @@ -1,11 +1,12 @@ #pragma once -#include <memory> -#include <string> #include <cassert> +#include <memory> #include <ostream> +#include <string> #include <vector> +#include "storage/model/properties/flags.hpp" #include "utils/underlying_cast.hpp" class Null; @@ -15,57 +16,13 @@ class Property public: using sptr = std::shared_ptr<Property>; - enum class Flags : unsigned - { - // Type | Mask - // -----------+---------------------------------------- - // Null | 0000 0000 0000 0000 0000 0000 0000 0000 - // -----------+---------------------------------------- - // Bool | 0000 0000 0000 0000 0000 0000 0000 0001 - // + True | 0000 0000 0000 0000 0000 0000 0000 0011 - // + False | 0000 0000 0000 0000 0000 0000 0000 0101 - // -----------+---------------------------------------- - // String | 0000 0000 0000 0000 0000 0000 0000 1000 - // -----------+---------------------------------------- - // Number | 0000 0000 0000 0000 0000 0000 0001 0000 - // + Integral | 0000 0000 0000 0000 0000 0000 0011 0000 - // + Int32 | 0000 0000 0000 0000 0000 0000 0111 0000 - // + Int64 | 0000 0000 0000 0000 0000 0000 1011 0000 - // + Floating | 0000 0000 0000 0000 0000 0001 0001 0000 - // + Float | 0000 0000 0000 0000 0000 0011 0001 0000 - // + Double | 0000 0000 0000 0000 0000 0101 0001 0000 - // -----------+---------------------------------------- - // Array | 0000 0000 0000 0000 0001 0000 0000 0000 - // -----------+---------------------------------------- - - Null = 0x0, - Bool = 0x1, - True = 0x2 | Bool, - False = 0x4 | Bool, - - String = 0x8, - - Number = 0x10, - Integral = 0x20 | Number, - Int32 = 0x40 | Integral, - Int64 = 0x80 | Integral, - - Floating = 0x100 | Number, - Float = 0x200 | Floating, - Double = 0x400 | Floating, - - Array = 0x1000, - - type_mask = 0xFFF - }; - static const Null Null; Property(Flags flags); - virtual bool operator==(const Property& other) const = 0; + virtual bool operator==(const Property &other) const = 0; - bool operator!=(const Property& other) const; + bool operator!=(const Property &other) const; template <class T> bool is() const @@ -74,22 +31,22 @@ public: } template <class T> - T& as() + T &as() { assert(this->is<T>()); - return *static_cast<T*>(this); + return *static_cast<T *>(this); } template <class T> - const T& as() const + const T &as() const { assert(this->is<T>()); - return *static_cast<const T*>(this); + return *static_cast<const T *>(this); } - virtual std::ostream& print(std::ostream& stream) const = 0; + virtual std::ostream &print(std::ostream &stream) const = 0; - friend std::ostream& operator<<(std::ostream& stream, const Property& prop); + friend std::ostream &operator<<(std::ostream &stream, const Property &prop); const Flags flags; }; diff --git a/include/storage/model/properties/property_family.hpp b/include/storage/model/properties/property_family.hpp new file mode 100644 index 000000000..cdb979ee9 --- /dev/null +++ b/include/storage/model/properties/property_family.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include <memory> +#include "data_structures/concurrent/concurrent_map.hpp" +#include "storage/model/properties/flags.hpp" +#include "utils/option.hpp" +#include "utils/total_ordering.hpp" +#include "utils/underlying_cast.hpp" + +typedef Flags Type; + +// Family of properties with the same name but different types. +// Ordered on name. +class PropertyFamily : public TotalOrdering<PropertyFamily> +{ + friend class PropertyType; + friend class PropertyFamilyKey; + friend class PropertyTypeKey; + +public: + // Type of property defined with his family and his type. + // Ordered on PropertyFamily and Type. + class PropertyType : public TotalOrdering<PropertyType> + { + friend class PropertyFamilyKey; + friend class PropertyTypeKey; + friend class PropertyFamily; + + public: + // Ordered on POINTERS to PropertyFamily + class PropertyFamilyKey : public TotalOrdering<PropertyFamilyKey> + { + friend class PropertyType; + friend class PropertyTypeKey; + + PropertyFamilyKey(const PropertyType &type) : type(&type) {} + + public: + friend bool operator==(const PropertyFamilyKey &lhs, + const PropertyFamilyKey &rhs) + { + return &(lhs.type->family) == &(rhs.type->family); + } + + friend bool operator<(const PropertyFamilyKey &lhs, + const PropertyFamilyKey &rhs) + { + return &(lhs.type->family) < &(rhs.type->family); + } + + Type prop_type() const { return type->type; } + + std::string const &family_name() const + { + return type->family.name(); + } + + private: + const PropertyType *type; + }; + + // Ordered on POINTERS to PropertyType. + // When compared with PropertyFamilyKey behaves as PropertyFamilyKey. + template <class T> + class PropertyTypeKey + : public TotalOrdering<PropertyTypeKey<T>>, + public TotalOrdering<PropertyFamilyKey, PropertyTypeKey<T>>, + public TotalOrdering<PropertyTypeKey<T>, PropertyFamilyKey> + { + friend class PropertyType; + + PropertyTypeKey(const PropertyType &type) : type(type) {} + public: + PropertyFamilyKey family_key() { return PropertyFamilyKey(type); } + + Type prop_type() const { return type.type; } + + friend bool operator==(const PropertyTypeKey &lhs, + const PropertyTypeKey &rhs) + { + return &(lhs.type) == &(rhs.type); + } + + friend bool operator<(const PropertyTypeKey &lhs, + const PropertyTypeKey &rhs) + { + return &(lhs.type) < &(rhs.type); + } + + friend bool operator==(const PropertyFamilyKey &lhs, + const PropertyTypeKey &rhs) + { + return &(lhs.type->family) == &(rhs.type.family); + } + + friend bool operator<(const PropertyFamilyKey &lhs, + const PropertyTypeKey &rhs) + { + return &(lhs.type->family) < &(rhs.type.family); + } + + friend bool operator==(const PropertyTypeKey &lhs, + const PropertyFamilyKey &rhs) + { + return &(lhs.type.family) == &(rhs.type->family); + } + + friend bool operator<(const PropertyTypeKey &lhs, + const PropertyFamilyKey &rhs) + { + return &(lhs.type.family) < &(rhs.type->family); + } + + private: + const PropertyType &type; + }; + + private: + PropertyType(PropertyFamily &family, Type type); + PropertyType(PropertyFamily &other) = delete; + PropertyType(PropertyFamily &&other) = delete; + + public: + template <class T> + bool is() const + { + return underlying_cast(type) & underlying_cast(T::type); + } + + bool is(Type &t) const; + + // Returns key ordered on POINTERS to PropertyType. + // When compared with PropertyFamilyKey behaves as PropertyFamilyKey. + template <class T> + PropertyTypeKey<T> type_key() + { + assert(this->is<T>()); + return PropertyTypeKey<T>(*this); + } + + // Returns key ordered on POINTERS to PropertyFamily + PropertyFamilyKey family_key(); + + friend bool operator<(const PropertyType &lhs, const PropertyType &rhs) + { + return lhs.family < rhs.family || + (lhs.family == rhs.family && lhs.type < rhs.type); + } + + friend bool operator==(const PropertyType &lhs, const PropertyType &rhs) + { + return lhs.family == rhs.family && lhs.type == rhs.type; + } + + private: + const PropertyFamily &family; + const Type type; + }; + + PropertyFamily(std::string const &name_v); + PropertyFamily(std::string &&name_v); + PropertyFamily(PropertyFamily &other) = delete; + PropertyFamily(PropertyFamily &&other) = delete; + + std::string const &name() const; + + // Returns type if it exists otherwise creates it. + PropertyType &get(Type type); + + friend bool operator<(const PropertyFamily &lhs, const PropertyFamily &rhs); + + friend bool operator==(const PropertyFamily &lhs, + const PropertyFamily &rhs); + +private: + const std::string name_v; + + // TODO: Because types wont be removed this could be done with more efficent + // data structure. + ConcurrentMap<Type, std::unique_ptr<PropertyType>> types; +}; diff --git a/include/storage/model/properties/traversers/consolewriter.hpp b/include/storage/model/properties/traversers/consolewriter.hpp index 8562fb2bb..dedc5eb4d 100644 --- a/include/storage/model/properties/traversers/consolewriter.hpp +++ b/include/storage/model/properties/traversers/consolewriter.hpp @@ -2,8 +2,8 @@ #include <iostream> -#include "storage/model/properties/properties.hpp" #include "storage/model/properties/handler.hpp" +#include "storage/model/properties/properties.hpp" using std::cout; using std::endl; @@ -13,12 +13,12 @@ class ConsoleWriter public: ConsoleWriter() {} - void handle(const std::string &key, const Property &value) + void handle(const prop_key_t &key, const Property &value) { - cout << "KEY: " << key << "; VALUE: "; + cout << "KEY: " << key.family_name() << "; VALUE: "; accept(value, *this); - + // value.accept(*this); cout << endl; diff --git a/include/storage/model/properties/traversers/jsonwriter.hpp b/include/storage/model/properties/traversers/jsonwriter.hpp index 28d411ea3..3daabcbef 100644 --- a/include/storage/model/properties/traversers/jsonwriter.hpp +++ b/include/storage/model/properties/traversers/jsonwriter.hpp @@ -1,7 +1,7 @@ #pragma once -#include "storage/model/properties/properties.hpp" #include "storage/model/properties/handler.hpp" +#include "storage/model/properties/properties.hpp" template <class Buffer> struct JsonWriter @@ -9,13 +9,13 @@ struct JsonWriter public: JsonWriter(Buffer &buffer) : buffer(buffer) { buffer << '{'; }; - void handle(const std::string &key, const Property &value) + void handle(const prop_key_t &key, const Property &value) { if (!first) buffer << ','; if (first) first = false; - buffer << '"' << key << "\":"; + buffer << '"' << key.family_name() << "\":"; // value.accept(*this); accept(value, *this); } diff --git a/include/storage/record_accessor.hpp b/include/storage/record_accessor.hpp index cb7350c1a..8928b90ad 100644 --- a/include/storage/record_accessor.hpp +++ b/include/storage/record_accessor.hpp @@ -2,8 +2,10 @@ #include "database/db_transaction.hpp" #include "mvcc/version_list.hpp" +#include "storage/indexes/index_record.hpp" #include "storage/model/properties/properties.hpp" #include "storage/model/properties/property.hpp" +#include "storage/model/properties/property_family.hpp" #include "transactions/transaction.hpp" template <class T, class Derived, class vlist_t = mvcc::VersionList<T>> @@ -57,30 +59,32 @@ public: return vlist->remove(record, db.trans); } - const Property &property(const std::string &key) const - { - return record->data.props.at(key); - } + const Property &at(prop_key_t &key) const { return properties().at(key); } + + template <class V> + auto at(type_key_t<V> &key) const; template <class V, class... Args> - void property(const std::string &key, Args &&... args) + void set(type_key_t<V> &key, Args &&... args) { - record->data.props.template set<V>(key, std::forward<Args>(args)...); + properties().template set<V>(key, std::forward<Args>(args)...); } - void property(const std::string &key, Property::sptr value) + void set(prop_key_t &key, Property::sptr value) { - record->data.props.set(key, std::move(value)); + properties().set(key, std::move(value)); + } + + void clear(prop_key_t &key) { properties().clear(key); } + + template <class Handler> + void accept(Handler &handler) const + { + properties().template accept<Handler>(handler); } Properties &properties() const { return record->data.props; } - template <class V> - auto at(const std::string &key) const - { - return properties().at(key).template as<V>().value_ref(); - } - explicit operator bool() const { return record != nullptr; } T const *operator->() const { return record; } @@ -99,6 +103,12 @@ public: } protected: + template <class K> + IndexRecord<T, K> create_ir(K &&key) + { + return IndexRecord<T, K>(std::move(key), record, vlist); + } + T *record{nullptr}; vlist_t *const vlist; DbTransaction &db; diff --git a/include/storage/vertex_accessor.hpp b/include/storage/vertex_accessor.hpp index 0222413ed..71b65d0c1 100644 --- a/include/storage/vertex_accessor.hpp +++ b/include/storage/vertex_accessor.hpp @@ -10,13 +10,23 @@ class Vertex::Accessor : public RecordAccessor<Vertex, Vertex::Accessor> public: using RecordAccessor::RecordAccessor; + static Vertex::Accessor create(Vertex *t, mvcc::VersionList<Vertex> *vlist, + DbTransaction &db) + { + return Vertex::Accessor(t, vlist, db); + } + size_t out_degree() const; size_t in_degree() const; size_t degree() const; - void add_label(const Label &label); + // False if it's label with it already. + bool add_label(const Label &label); + + // False if it doesn't have label. + bool remove_label(const Label &label); bool has_label(const Label &label) const; diff --git a/include/storage/vertices.hpp b/include/storage/vertices.hpp index 8f5b78299..616f63ed9 100644 --- a/include/storage/vertices.hpp +++ b/include/storage/vertices.hpp @@ -1,17 +1,24 @@ #pragma once +#include <memory> +#include <string> #include "data_structures/concurrent/concurrent_map.hpp" -#include "database/db_transaction.hpp" +// #include "database/db_transaction.hpp" #include "storage/common.hpp" -#include "storage/indexes/index.hpp" -#include "storage/indexes/index_record_collection.hpp" +// #include "storage/indexes/index.hpp" +// #include "storage/indexes/index_record_collection.hpp" +#include "storage/model/properties/property_family.hpp" #include "storage/vertex_accessor.hpp" #include "utils/option.hpp" +class DbTransaction; + class Vertices { public: using vertices_t = ConcurrentMap<uint64_t, VertexRecord>; + using prop_familys_t = + ConcurrentMap<std::string, std::unique_ptr<PropertyFamily>>; vertices_t::Accessor access(); @@ -20,13 +27,15 @@ public: // Creates new Vertex and returns filled Vertex::Accessor. Vertex::Accessor insert(DbTransaction &t); - void update_label_index(const Label &label, - VertexIndexRecord &&index_record); + PropertyFamily &property_family_find_or_create(const std::string &name); - VertexIndexRecordCollection &find_label_index(const Label &label); + // prop_familys_t::Accessor property_family_access(); private: vertices_t vertices; - Index<label_ref_t, VertexIndexRecordCollection> label_index; + // TODO: Because familys wont be removed this could be done with more + // efficent + // data structure. + prop_familys_t prop_familys; AtomicCounter<uint64_t> counter; }; diff --git a/include/utils/border.hpp b/include/utils/border.hpp new file mode 100644 index 000000000..94e56a712 --- /dev/null +++ b/include/utils/border.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "utils/option.hpp" + +// Defines Including as [ and Excluding < for ranges. +enum BorderType +{ + Including = 0, + Excluding = 1, +}; + +template <class T> +class Border +{ + +public: + Border() : key(Option<T>()), type(Including) {} + Border(T Tey, BorderType type) : key(Option<T>(std::move(key))), type(type) + { + } + + // Border(Border &other) = default; + Border(Border &other) = default; + Border(Border &&other) = default; + + Border &operator=(Border &&other) = default; + Border &operator=(Border &other) = default; + + // true if no border or this>key or this>=key depends on border type. + bool operator>(const T &other) const + { + return !key.is_present() || key.get() > other || + (type == Including && key.get() == other); + } + + // true if no border or this<key or this<=key depends on border type. + bool operator<(const T &other) const + { + return !key.is_present() || key.get() < other || + (type == Including && key.get() == other); + } + + Option<T> key; + const BorderType type; +}; + +template <class T> +auto make_inf_border() +{ + return Border<T>(); +} diff --git a/include/utils/iterator/accessor.hpp b/include/utils/iterator/accessor.hpp index 88d5f32b6..55dbddcc9 100644 --- a/include/utils/iterator/accessor.hpp +++ b/include/utils/iterator/accessor.hpp @@ -1,28 +1,35 @@ #pragma once -#include "utils/iterator/wrap.hpp" +#include "utils/iterator/range_iterator.hpp" #include "utils/option.hpp" namespace iter { +// Class which turns ranged iterator with next() into accessor. +// T - type of return value +// I - iterator type template <class T, class I> class OneTimeAccessor { public: - OneTimeAccessor() : it(Option<Wrap<T, I>>()) {} - OneTimeAccessor(I &&it) : it(Wrap<T, I>(std::move(it))) {} + OneTimeAccessor() : it(Option<RangeIterator<T, I>>()) {} + OneTimeAccessor(I &&it) : it(RangeIterator<T, I>(std::move(it))) {} - Wrap<T, I> begin() { return it.take(); } + RangeIterator<T, I> begin() { return it.take(); } - Wrap<T, I> end() { return Wrap<T, I>(); } + RangeIterator<T, I> end() { return RangeIterator<T, I>(); } private: - Option<Wrap<T, I>> it; + Option<RangeIterator<T, I>> it; }; template <class I> auto make_one_time_accessor(I &&iter) { + // Because function isn't receving or in any way using type T from + // OneTimeAccessor compiler can't deduce it thats way there is decltype in + // construction of OneTimeAccessor. Resoulting type of iter.next().take() is + // T. return OneTimeAccessor<decltype(iter.next().take()), I>(std::move(iter)); } } diff --git a/include/utils/iterator/for_all.hpp b/include/utils/iterator/for_all.hpp index ba5b8f2dc..b001cab7c 100644 --- a/include/utils/iterator/for_all.hpp +++ b/include/utils/iterator/for_all.hpp @@ -14,4 +14,14 @@ void for_all(I &&iter, C &&consumer) e = iter.next(); } } + +template <class I, class C> +void for_all(std::unique_ptr<I> &&iter, C &&consumer) +{ + auto e = iter->next(); + while (e.is_present()) { + consumer(e.take()); + e = iter->next(); + } +} } diff --git a/include/utils/iterator/func_iterator.hpp b/include/utils/iterator/func_iterator.hpp new file mode 100644 index 000000000..c420d9d8d --- /dev/null +++ b/include/utils/iterator/func_iterator.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "utils/iterator/iterator_base.hpp" + +namespace iter +{ +// Wraps function into interator with next(). +// T - type of return value +// F - type of wraped function +template <class T, class F> +class FunctionIterator : public IteratorBase<T> +{ +public: + FunctionIterator(F &&f) : func(std::move(f)) {} + + Option<T> next() final { return func(); } + +private: + F func; +}; + +// Wraps function which returns options as an iterator. +template <class F> +auto make_iterator(F &&f) +{ + // Because function isn't receving or in any way using type T from + // FunctionIterator compiler can't deduce it thats way there is decltype in + // construction of FunctionIterator. Resoulting type of iter.next().take() + // is T. + return FunctionIterator<decltype(f().take()), F>(std::move(f)); +} +} diff --git a/include/utils/iterator/iter.hpp b/include/utils/iterator/iter.hpp deleted file mode 100644 index cfb3c974a..000000000 --- a/include/utils/iterator/iter.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#pragma once - -#include "utils/option.hpp" - -namespace iter -{ -template <class T, class I, class A> -class Iter -{ -public: - Iter() = delete; - - Iter(A &&acc) : begin(std::move(acc.begin())), acc(std::forward<A>(acc)) {} - // Iter(const Iter &other) = delete; - // Iter(Iter &&other) : - // begin(std::move(other.begin)),end(std::move(other.end)) {}; - - auto next() - { - if (begin != acc.end()) { - auto ret = Option<T>(&(*(begin.operator->()))); - begin++; - return ret; - } else { - return Option<T>(); - } - } - -private: - I begin; - A acc; -}; - -// TODO: Join to make functions into one -template <class A> -auto make_iter(A &&acc) -{ - return Iter<decltype(&(*(acc.begin().operator->()))), decltype(acc.begin()), - A>(std::move(acc)); -} - -template <class A> -auto make_iter_ref(A &acc) -{ - return Iter<decltype(&(*(acc.begin().operator->()))), decltype(acc.begin()), - A &>(acc); -} -} diff --git a/include/utils/iterator/iterator.hpp b/include/utils/iterator/iterator.hpp index e3e8c1cd1..f91d4e9e2 100644 --- a/include/utils/iterator/iterator.hpp +++ b/include/utils/iterator/iterator.hpp @@ -2,6 +2,8 @@ #include "utils/iterator/accessor.hpp" #include "utils/iterator/for_all.hpp" -#include "utils/iterator/iter.hpp" +#include "utils/iterator/func_iterator.hpp" +#include "utils/iterator/iterator_accessor.hpp" +#include "utils/iterator/iterator_base.hpp" #include "utils/iterator/map.hpp" -#include "utils/iterator/wrap.hpp" +#include "utils/iterator/range_iterator.hpp" diff --git a/include/utils/iterator/iterator_accessor.hpp b/include/utils/iterator/iterator_accessor.hpp new file mode 100644 index 000000000..794a9cf4a --- /dev/null +++ b/include/utils/iterator/iterator_accessor.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include "utils/iterator/iterator_base.hpp" +#include "utils/option.hpp" + +namespace iter +{ + +// Class which turns accessor int next() based iterator. +// T - type of return value +// I - iterator type gotten from accessor +// A - accessor type +template <class T, class I, class A> +class IteratorAccessor : public IteratorBase<T> +{ +public: + IteratorAccessor() = delete; + + IteratorAccessor(A &&acc) + : begin(std::move(acc.begin())), acc(std::forward<A>(acc)) + { + } + // Iter(const Iter &other) = delete; + // Iter(Iter &&other) : + // begin(std::move(other.begin)),end(std::move(other.end)) {}; + + Option<T> next() final + { + if (begin != acc.end()) { + auto ret = Option<T>(&(*(begin.operator->()))); + begin++; + return ret; + } else { + return Option<T>(); + } + } + +private: + I begin; + A acc; +}; + +// TODO: Join to make functions into one +template <class A> +auto make_iter(A &&acc) +{ + // Compiler cant deduce types T and I. decltype are here to help with it. + return IteratorAccessor<decltype(&(*(acc.begin().operator->()))), + decltype(acc.begin()), A>(std::move(acc)); +} + +template <class A> +auto make_iter_ref(A &acc) +{ + // Compiler cant deduce types T and I. decltype are here to help with it. + return IteratorAccessor<decltype(&(*(acc.begin().operator->()))), + decltype(acc.begin()), A &>(acc); +} +} diff --git a/include/utils/iterator/iterator_base.hpp b/include/utils/iterator/iterator_base.hpp new file mode 100644 index 000000000..0d805edfa --- /dev/null +++ b/include/utils/iterator/iterator_base.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "utils/option.hpp" + +// Base iterator for next() kind iterator. +// T - type of return value +template <class T> +class IteratorBase +{ +public: + virtual Option<T> next() = 0; +}; diff --git a/include/utils/iterator/map.hpp b/include/utils/iterator/map.hpp index 7bc8ba474..2f4543bcb 100644 --- a/include/utils/iterator/map.hpp +++ b/include/utils/iterator/map.hpp @@ -1,38 +1,47 @@ #pragma once +#include "utils/iterator/iterator_base.hpp" #include "utils/option.hpp" namespace iter { -template <class U, class I, class MapOperator> -class Map + +// Class which maps values returned by I iterator into value of type T with OP +// function. +// T - type of return value +// I - iterator type +// OP - type of mapper function +template <class T, class I, class OP> +class Map : public IteratorBase<T> { public: Map() = delete; - template <class IT, class OP> - Map(IT &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) - { - } - auto next() + // Map operation is designed to be used in chained calls which operate on a + // iterator. Map will in that usecase receive other iterator by value and + // std::move is a optimization for it. + Map(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {} + + Option<T> next() final { auto item = iter.next(); if (item.is_present()) { - return Option<U>(op(item.take())); + return Option<T>(op(item.take())); } else { - return Option<U>(); + return Option<T>(); } } private: I iter; - MapOperator op; + OP op; }; template <class I, class OP> auto make_map(I &&iter, OP &&op) { + // Compiler cant deduce type T. decltype is here to help with it. return Map<decltype(op(iter.next().take())), I, OP>(std::move(iter), std::move(op)); } diff --git a/include/utils/iterator/range_iterator.hpp b/include/utils/iterator/range_iterator.hpp new file mode 100644 index 000000000..923e9525c --- /dev/null +++ b/include/utils/iterator/range_iterator.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "utils/option.hpp" + +namespace iter +{ + +// Class which wraps iterator with next() into c++ iterator. +// T - type of return value +// I - iterator type +template <class T, class I> +class RangeIterator +{ + +public: + RangeIterator() : iter(Option<I>()), value(Option<T>()){}; + + RangeIterator(I &&iter) + : value(iter.next()), iter(Option<I>(std::move(iter))) + { + } + + T &operator*() + { + assert(value.is_present()); + return value.get(); + } + + T *operator->() + { + assert(value.is_present()); + return &value.get(); + } + + operator T &() + { + assert(value.is_present()); + return value.get(); + } + + RangeIterator &operator++() + { + assert(iter.is_present()); + value = iter.get().next(); + return (*this); + } + + RangeIterator &operator++(int) { return operator++(); } + + friend bool operator==(const RangeIterator &a, const RangeIterator &b) + { + return a.value.is_present() == b.value.is_present(); + } + + friend bool operator!=(const RangeIterator &a, const RangeIterator &b) + { + return !(a == b); + } + +private: + Option<I> iter; + Option<T> value; +}; + +template <class I> +auto make_range_iterator(I &&iter) +{ + // Because function isn't receving or in any way using type T from + // RangeIterator compiler can't deduce it thats way there is decltype in + // construction of RangeIterator. Resoulting type of iter.next().take() is + // T. + return RangeIterator<decltype(iter.next().take()), I>(std::move(iter)); +} +} diff --git a/include/utils/iterator/wrap.hpp b/include/utils/iterator/wrap.hpp deleted file mode 100644 index a39fe6d2c..000000000 --- a/include/utils/iterator/wrap.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "utils/option.hpp" - -namespace iter -{ -template <class T, class I> -class Wrap -{ - -public: - Wrap() : iter(Option<I>()), value(Option<T>()){}; - - Wrap(I &&iter) : value(iter.next()), iter(Option<I>(std::move(iter))) {} - - T &operator*() - { - assert(value.is_present()); - return value.get(); - } - - T *operator->() - { - assert(value.is_present()); - return &value.get(); - } - - operator T &() - { - assert(value.is_present()); - return value.get(); - } - - Wrap &operator++() - { - assert(iter.is_present()); - value = iter.get().next(); - return (*this); - } - - Wrap &operator++(int) { return operator++(); } - - friend bool operator==(const Wrap &a, const Wrap &b) - { - return a.value.is_present() == b.value.is_present(); - } - - friend bool operator!=(const Wrap &a, const Wrap &b) { return !(a == b); } - -private: - Option<I> iter; - Option<T> value; -}; - -template <class I> -auto make_wrap(I &&iter) -{ - return Wrap<decltype(iter.next().take()), I>(std::move(iter)); -} -} diff --git a/include/utils/option.hpp b/include/utils/option.hpp index 8e1b66d44..a3aae3c5f 100644 --- a/include/utils/option.hpp +++ b/include/utils/option.hpp @@ -1,5 +1,6 @@ #pragma once +#include <cassert> #include <ext/aligned_buffer.h> #include <utility> @@ -8,6 +9,12 @@ class Option { public: Option() {} + // + // Option(T item) + // { + // new (data._M_addr()) T(std::forward(item)); + // initialized = true; + // } Option(T const &item) { @@ -22,6 +29,9 @@ public: } Option(Option &other) = default; + // 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) { @@ -36,7 +46,8 @@ public: if (initialized) get().~T(); } - Option<T> &operator=(Option<T> &&other) + Option &operator=(Option &other) = default; + Option &operator=(Option &&other) { if (initialized) { get().~T(); @@ -60,7 +71,11 @@ public: return *data._M_ptr(); } - const T &get() const noexcept { assert(initialized); } + const T &get() const noexcept + { + assert(initialized); + return *data._M_ptr(); + } T take() { @@ -72,6 +87,9 @@ public: explicit operator bool() const { return initialized; } private: + // Aligned buffer is here to ensure aligment for data of type T. It isn't + // applicable to just put T field because the field has to be able to be + // uninitialized to fulfill the semantics of Option class. __gnu_cxx::__aligned_buffer<T> data; bool initialized = false; }; diff --git a/include/utils/void.hpp b/include/utils/void.hpp new file mode 100644 index 000000000..ff36ff3b1 --- /dev/null +++ b/include/utils/void.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "utils/total_ordering.hpp" + +class Void : public TotalOrdering<Void> +{ + friend bool operator<(const Void &lhs, const Void &rhs) { return false; } + + friend bool operator==(const Void &lhs, const Void &rhs) { return true; } +}; diff --git a/poc/astar.cpp b/poc/astar.cpp index 7e9be0e03..1490efb31 100644 --- a/poc/astar.cpp +++ b/poc/astar.cpp @@ -11,8 +11,15 @@ #include "data_structures/map/rh_hashmap.hpp" #include "database/db.hpp" #include "database/db_accessor.hpp" +#include "storage/edges.cpp" +#include "storage/edges.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 "storage/vertex_accessor.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" using namespace std; typedef Vertex::Accessor VertexAccessor; @@ -23,13 +30,19 @@ class Node { public: Node *parent = {nullptr}; + type_key_t<Double> tkey; double cost; int depth = {0}; VertexAccessor vacc; - Node(VertexAccessor vacc, double cost) : cost(cost), vacc(vacc) {} - Node(VertexAccessor vacc, double cost, Node *parent) - : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1) + Node(VertexAccessor vacc, double cost, type_key_t<Double> tkey) + : cost(cost), vacc(vacc), tkey(tkey) + { + } + Node(VertexAccessor vacc, double cost, Node *parent, + type_key_t<Double> tkey) + : cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1), + tkey(tkey) { } @@ -38,7 +51,7 @@ public: auto now = this; double sum = 0; do { - sum += now->vacc.at<Double>("score"); + sum += *(now->vacc.at(tkey).get()); now = now->parent; } while (now != nullptr); return sum; @@ -105,10 +118,11 @@ void found_result(Node *res) } } -double calc_heuristic_cost_dummy(Edge::Accessor &edge, Vertex::Accessor &vertex) +double calc_heuristic_cost_dummy(type_key_t<Double> tkey, Edge::Accessor &edge, + Vertex::Accessor &vertex) { assert(!vertex.empty()); - return 1 - vertex.at<Double>("score"); + return 1 - *vertex.at(tkey).get(); } typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before); @@ -168,11 +182,16 @@ bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before) // Filtri vracaju true ako element zadovoljava uvjete. auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], VertexFilter v_filter[], - double (*calc_heuristic_cost)(Edge::Accessor &edge, + double (*calc_heuristic_cost)(type_key_t<Double> tkey, + Edge::Accessor &edge, Vertex::Accessor &vertex), int limit) { DbAccessor t(db); + type_key_t<Double> tkey = t.vertex_property_family_get("score") + .get(Type::Double) + .type_key<Double>(); + auto best_found = new std::map<Id, Score>[max_depth]; std::vector<Node *> best; @@ -182,7 +201,7 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], auto start_vr = t.vertex_find(sys_id_start); assert(start_vr); start_vr.get().fill(); - Node *start = new Node(start_vr.take(), 0); + Node *start = new Node(start_vr.take(), 0, tkey); queue.push(start); int count = 0; do { @@ -213,8 +232,8 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[], if (e_filter[now->depth](t, edge, now)) { VertexAccessor va = edge.to(); if (v_filter[now->depth](t, va, now)) { - auto cost = calc_heuristic_cost(edge, va); - Node *n = new Node(va, now->cost + cost, now); + auto cost = calc_heuristic_cost(tkey, edge, va); + Node *n = new Node(va, now->cost + cost, now, tkey); queue.push(n); } } @@ -346,6 +365,18 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) std::string line; DbAccessor t(db); + auto key_id = + t.vertex_property_family_get("id").get(Type::Int32).family_key(); + auto key_garment_id = t.vertex_property_family_get("garment_id") + .get(Type::Int32) + .family_key(); + auto key_garment_category_id = + t.vertex_property_family_get("garment_category_id") + .get(Type::Int32) + .family_key(); + auto key_score = + t.vertex_property_family_get("score").get(Type::Double).family_key(); + int max_score = 1000000; // VERTEX import @@ -356,14 +387,15 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) } auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", std::make_shared<Int32>(id)); - vertex_accessor.property("garment_id", std::make_shared<Int32>(gar_id)); - vertex_accessor.property("garment_category_id", - std::make_shared<Int32>(cat_id)); + vertex_accessor.set(key_id, std::make_shared<Int32>(id)); + vertex_accessor.set(key_garment_id, std::make_shared<Int32>(gar_id)); + vertex_accessor.set(key_garment_category_id, + std::make_shared<Int32>(cat_id)); std::srand(id ^ 0x7482616); - vertex_accessor.property( - "score", std::make_shared<Double>((std::rand() % max_score) / - (max_score + 0.0))); + vertex_accessor.set(key_score, + std::make_shared<Double>((std::rand() % max_score) / + (max_score + 0.0))); + for (auto l_name : labels) { auto &label = t.label_find_or_create(l_name); vertex_accessor.add_label(label); @@ -372,6 +404,7 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) return vertex_accessor; }; + // Skip header std::getline(file, line); vector<Vertex::Accessor> va; @@ -423,39 +456,39 @@ int load_csv(Db &db, char *file_path, char *edge_file_path) void load_graph_dummy(Db &db) { DbAccessor t(db); - auto v = [&](auto id, auto score) { - auto vertex_accessor = t.vertex_insert(); - vertex_accessor.property("id", std::make_shared<Int32>(id)); - vertex_accessor.property("score", std::make_shared<Double>(score)); - return vertex_accessor.id(); - }; - - Id va[] = { - v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8), - }; - - auto e = [&](auto from, auto type, auto to) { - auto v1 = t.vertex_find(va[from]); - - auto v2 = t.vertex_find(va[to]); - - auto edge_accessor = t.edge_insert(v1.get(), v2.get()); - - auto &edge_type = t.type_find_or_create(type); - edge_accessor.edge_type(edge_type); - }; - - e(0, "ok", 3); - e(0, "ok", 2); - e(0, "ok", 4); - e(1, "ok", 3); - e(2, "ok", 1); - e(2, "ok", 4); - e(3, "ok", 4); - e(3, "ok", 5); - e(4, "ok", 0); - e(4, "ok", 1); - e(5, "ok", 2); + // auto v = [&](auto id, auto score) { + // auto vertex_accessor = t.vertex_insert(); + // vertex_accessor.property("id", std::make_shared<Int32>(id)); + // vertex_accessor.property("score", std::make_shared<Double>(score)); + // return vertex_accessor.id(); + // }; + // + // Id va[] = { + // v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8), + // }; + // + // auto e = [&](auto from, auto type, auto to) { + // auto v1 = t.vertex_find(va[from]); + // + // auto v2 = t.vertex_find(va[to]); + // + // auto edge_accessor = t.edge_insert(v1.get(), v2.get()); + // + // auto &edge_type = t.type_find_or_create(type); + // edge_accessor.edge_type(edge_type); + // }; + // + // e(0, "ok", 3); + // e(0, "ok", 2); + // e(0, "ok", 4); + // e(1, "ok", 3); + // e(2, "ok", 1); + // e(2, "ok", 4); + // e(3, "ok", 4); + // e(3, "ok", 5); + // e(4, "ok", 0); + // e(4, "ok", 1); + // e(5, "ok", 2); t.commit(); } diff --git a/src/data_structures/map/rh_common.hpp b/src/data_structures/map/rh_common.hpp index 69593bdb0..9fa396e30 100644 --- a/src/data_structures/map/rh_common.hpp +++ b/src/data_structures/map/rh_common.hpp @@ -187,22 +187,26 @@ public: RhBase() {} - RhBase(const RhBase &other) - { - capacity = other.capacity; - count = other.count; - if (capacity > 0) { - size_t bytes = sizeof(Combined) * capacity; - array = (Combined *)malloc(bytes); - memcpy(array, other.array, bytes); + RhBase(const RhBase &other) { copy_from(other); } - } else { - array = nullptr; - } - } + RhBase(RhBase &&other) { take_from(std::move(other)); } ~RhBase() { this->clear(); } + RhBase &operator=(const RhBase &other) + { + clear(); + copy_from(other); + return *this; + } + + RhBase &operator=(RhBase &&other) + { + clear(); + take_from(std::move(other)); + return *this; + } + Iterator begin() { return Iterator(this); } ConstIterator begin() const { return ConstIterator(this); } @@ -216,6 +220,30 @@ public: ConstIterator cend() const { return ConstIterator(); } protected: + void copy_from(const RhBase &other) + { + capacity = other.capacity; + count = other.count; + if (capacity > 0) { + size_t bytes = sizeof(Combined) * capacity; + array = (Combined *)malloc(bytes); + memcpy(array, other.array, bytes); + + } else { + array = nullptr; + } + } + + void take_from(RhBase &&other) + { + capacity = other.capacity; + count = other.count; + array = other.array; + other.array = nullptr; + other.count = 0; + other.capacity = 0; + } + void init_array(size_t size) { size_t bytes = sizeof(Combined) * size; diff --git a/src/database/db.cpp b/src/database/db.cpp index 9ceb50a77..df9abaa74 100644 --- a/src/database/db.cpp +++ b/src/database/db.cpp @@ -1,4 +1,5 @@ #include "database/db.hpp" +#include "storage/model/properties/property_family.hpp" Db::Db() = default; Db::Db(const std::string &name) : name_(name) {} diff --git a/src/database/db_accessor.cpp b/src/database/db_accessor.cpp index b3972e29a..ca07dc89a 100644 --- a/src/database/db_accessor.cpp +++ b/src/database/db_accessor.cpp @@ -1,36 +1,46 @@ #include "database/db_accessor.hpp" -DbAccessor::DbAccessor(Db &db) : db(DbTransaction(db, db.tx_engine.begin())) {} +#include "database/db.hpp" + +#include "utils/iterator/iterator.hpp" + +DbAccessor::DbAccessor(Db &db) + : db_transaction(DbTransaction(db, db.tx_engine.begin())) +{ +} // VERTEX METHODS auto DbAccessor::vertex_access() { return iter::make_map( - iter::make_iter(this->db.db.graph.vertices.access()), - [&](auto e) -> auto { return Vertex::Accessor(&(e->second), db); }); + iter::make_iter(this->db_transaction.db.graph.vertices.access()), + [&](auto e) -> auto { + return Vertex::Accessor(&(e->second), db_transaction); + }); } Option<const Vertex::Accessor> DbAccessor::vertex_find(const Id &id) { - return this->db.db.graph.vertices.find(db, id); + return this->db_transaction.db.graph.vertices.find(db_transaction, id); } Vertex::Accessor DbAccessor::vertex_insert() { - return this->db.db.graph.vertices.insert(db); + return this->db_transaction.db.graph.vertices.insert(db_transaction); } // EDGE METHODS Option<const Edge::Accessor> DbAccessor::edge_find(const Id &id) { - return db.db.graph.edges.find(db, id); + return db_transaction.db.graph.edges.find(db_transaction, id); } Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from, Vertex::Accessor const &to) { - auto edge_accessor = db.db.graph.edges.insert(db, from.vlist, to.vlist); + auto edge_accessor = db_transaction.db.graph.edges.insert( + db_transaction, from.vlist, to.vlist); from.update()->data.out.add(edge_accessor.vlist); to.update()->data.in.add(edge_accessor.vlist); return edge_accessor; @@ -39,37 +49,57 @@ Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from, // LABEL METHODS const Label &DbAccessor::label_find_or_create(const std::string &name) { - return db.db.graph.label_store.find_or_create( + return db_transaction.db.graph.label_store.find_or_create( std::forward<const std::string &>(name)); } bool DbAccessor::label_contains(const std::string &name) { - return db.db.graph.label_store.contains( + return db_transaction.db.graph.label_store.contains( std::forward<const std::string &>(name)); } -VertexIndexRecordCollection &DbAccessor::label_find_index(const Label &label) -{ - return db.db.graph.vertices.find_label_index(label); -} - // TYPE METHODS const EdgeType &DbAccessor::type_find_or_create(const std::string &name) { - return db.db.graph.edge_type_store.find_or_create( + return db_transaction.db.graph.edge_type_store.find_or_create( std::forward<const std::string &>(name)); } bool DbAccessor::type_contains(const std::string &name) { - return db.db.graph.edge_type_store.contains( + return db_transaction.db.graph.edge_type_store.contains( std::forward<const std::string &>(name)); } +//********************PROPERTY METHODS +// Vertices::prop_familys_t::Accessor +// DbAccessor::vertex_property_family_access() +// { +// return db.db.graph.vertices.property_family_access(); +// } +// +// auto DbAccessor::edge_property_family_access() +// { +// return db.db.graph.edges.property_family_access(); +// } + +auto edge_property_family_access(); + +PropertyFamily &DbAccessor::vertex_property_family_get(const std::string &name) +{ + return db_transaction.db.graph.vertices.property_family_find_or_create( + name); +} + +PropertyFamily &DbAccessor::edge_property_family_get(const std::string &name) +{ + return db_transaction.db.graph.edges.property_family_find_or_create(name); +} + // TRANSACTION METHODS -void DbAccessor::commit() { db.trans.commit(); } -void DbAccessor::abort() { db.trans.abort(); } +void DbAccessor::commit() { db_transaction.trans.commit(); } +void DbAccessor::abort() { db_transaction.trans.abort(); } // // EASE OF USE METHODS // tx::Transaction &DbAccessor::operator*() { return db.trans; } diff --git a/src/database/db_transaction.cpp b/src/database/db_transaction.cpp index 3760ce479..c16be5e5f 100644 --- a/src/database/db_transaction.cpp +++ b/src/database/db_transaction.cpp @@ -1,8 +1,2 @@ -#include "database/db.hpp" -#include "database/db_transaction.hpp" - -void DbTransaction::update_label_index(const Label &label, - VertexIndexRecord &&index_record) -{ - db.graph.vertices.update_label_index(label, std::move(index_record)); -} +// #include "database/db.hpp" +// #include "database/db_transaction.hpp" diff --git a/src/query_engine/util.cpp b/src/query_engine/util.cpp index 9a980826e..2efcaf5f3 100644 --- a/src/query_engine/util.cpp +++ b/src/query_engine/util.cpp @@ -15,7 +15,7 @@ void cout_properties(const Properties &properties) cout << "----" << endl; } -void cout_property(const std::string &key, const Property &property) +void cout_property(const prop_key_t &key, const Property &property) { ConsoleWriter writer; writer.handle(key, property); diff --git a/src/storage/edges.cpp b/src/storage/edges.cpp index 5c142e9a8..3c5a3bfe1 100644 --- a/src/storage/edges.cpp +++ b/src/storage/edges.cpp @@ -1,4 +1,6 @@ #include "storage/edges.hpp" +#include "storage/model/properties/property_family.hpp" +#include "utils/iterator/iterator.hpp" Option<const Edge::Accessor> Edges::find(DbTransaction &t, const Id &id) { @@ -30,3 +32,25 @@ Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from, return Edge::Accessor(edge, &inserted_edge_record->second, t); } + +// auto Edges::property_family_access() +// { // Returnig access directly would allow extern code to remove elements. +// Which +// // would be BAD, VERY BAD. +// return iter::make_iter(prop_familys.access()); +// } + +PropertyFamily &Edges::property_family_find_or_create(const std::string &name) +{ + auto acc = prop_familys.access(); + auto it = acc.find(name); + if (it == acc.end()) { + PropertyFamily *family = new PropertyFamily(name); + auto res = acc.insert(name, family); + if (!res.second) { + delete family; + } + it = res.first; + } + return *(it->second); +} diff --git a/src/storage/indexes/impl/nonunique_unordered_index.cpp b/src/storage/indexes/impl/nonunique_unordered_index.cpp new file mode 100644 index 000000000..7278d3dc4 --- /dev/null +++ b/src/storage/indexes/impl/nonunique_unordered_index.cpp @@ -0,0 +1,61 @@ +#include "storage/indexes/impl/nonunique_unordered_index.hpp" + +#include "database/db_accessor.hpp" +#include "database/db_transaction.hpp" +#include "utils/iterator/iterator.hpp" + +template <class T, class K> +NonUniqueUnorderedIndex<T, K>::NonUniqueUnorderedIndex() + : IndexBase<T, K>(false, None) +{ +} + +template <class T, class K> +bool NonUniqueUnorderedIndex<T, K>::insert(IndexRecord<T, K> &&value) +{ + list.begin().push(std::move(value)); + return true; +} + +template <class T, class K> +std::unique_ptr<IteratorBase<const typename T::Accessor>> +NonUniqueUnorderedIndex<T, K>::for_range(DbAccessor &t, Border<K> from, + Border<K> to) +{ + return std::make_unique<decltype( + for_range_exact(t, std::move(from), std::move(to)))>( + for_range_exact(t, std::move(from), std::move(to))); +} + +template <class T, class K> +auto NonUniqueUnorderedIndex<T, K>::for_range_exact(DbAccessor &t_v, + Border<K> from_v, + Border<K> to_v) +{ + return iter::make_iterator([ + it = list.cbegin(), end = list.cend(), from = from_v, to = to_v, t = t_v + ]() mutable->auto { + while (it != end) { + const IndexRecord<T, K> &r = *it; + if (from < r.key && to > r.key && + r.is_valid(t.db_transaction.trans)) { + const typename T::Accessor acc = r.access(t.db_transaction); + it++; + return make_option(std::move(acc)); + } + it++; + } + + return Option<const typename T::Accessor>(); + }); +} + +template <class T, class K> +void NonUniqueUnorderedIndex<T, K>::clean(DbTransaction &) +{ + // TODO: Actual cleaning +} + +#include "storage/vertex.hpp" +// #include "utils/singleton.hpp" +template class NonUniqueUnorderedIndex<Vertex, std::nullptr_t>; diff --git a/src/storage/label/label.cpp b/src/storage/label/label.cpp index 9e0b8228f..b669fcc56 100644 --- a/src/storage/label/label.cpp +++ b/src/storage/label/label.cpp @@ -1,24 +1,29 @@ +// #include "storage/indexes/impl/nonunique_unordered_index.hpp" #include "storage/label/label.hpp" -Label::Label(const std::string& name) : name(name) {} -Label::Label(std::string&& name) : name(std::move(name)) {} +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)), + index(std::unique_ptr<label_index_t>(new label_index_t())) +{ +} -bool operator<(const Label& lhs, const Label& rhs) +bool operator<(const Label &lhs, const Label &rhs) { return lhs.name < rhs.name; } -bool operator==(const Label& lhs, const Label& rhs) +bool operator==(const Label &lhs, const Label &rhs) { return lhs.name == rhs.name; } -std::ostream& operator<<(std::ostream& stream, const Label& label) +std::ostream &operator<<(std::ostream &stream, const Label &label) { return stream << label.name; } -Label::operator const std::string&() const -{ - return name; -} +Label::operator const std::string &() const { return name; } diff --git a/src/storage/label/label_collection.cpp b/src/storage/label/label_collection.cpp index 3131ce5b2..22a77380a 100644 --- a/src/storage/label/label_collection.cpp +++ b/src/storage/label/label_collection.cpp @@ -1,5 +1,7 @@ #include "storage/label/label_collection.hpp" +#include "storage/label/label.hpp" + auto LabelCollection::begin() { return _labels.begin(); } auto LabelCollection::begin() const { return _labels.begin(); } auto LabelCollection::cbegin() const { return _labels.begin(); } @@ -8,36 +10,30 @@ auto LabelCollection::end() { return _labels.end(); } auto LabelCollection::end() const { return _labels.end(); } auto LabelCollection::cend() const { return _labels.end(); } -bool LabelCollection::add(const Label& label) +bool LabelCollection::add(const Label &label) { return _labels.insert(label_ref_t(label)).second; } -bool LabelCollection::has(const Label& label) const +bool LabelCollection::has(const Label &label) const { return _labels.count(label); } -size_t LabelCollection::count() const { - return _labels.size(); -} +size_t LabelCollection::count() const { return _labels.size(); } -bool LabelCollection::remove(const Label& label) +bool LabelCollection::remove(const Label &label) { auto it = _labels.find(label); - if(it == _labels.end()) - return false; + if (it == _labels.end()) return false; return _labels.erase(it), true; } -void LabelCollection::clear() -{ - _labels.clear(); -} +void LabelCollection::clear() { _labels.clear(); } -const std::set<label_ref_t>& LabelCollection::operator()() const +const std::set<label_ref_t> &LabelCollection::operator()() const { return _labels; } diff --git a/src/storage/model/properties/properties.cpp b/src/storage/model/properties/properties.cpp index c4dec594d..0f3110427 100644 --- a/src/storage/model/properties/properties.cpp +++ b/src/storage/model/properties/properties.cpp @@ -1,54 +1,76 @@ #include "storage/model/properties/properties.hpp" #include "storage/model/properties/null.hpp" +#include "storage/model/properties/property_family.hpp" +#include "utils/option.hpp" -const Property& Properties::at(const std::string& key) const +const Property &Properties::at(prop_key_t &key) const { auto it = props.find(key); - if(it == props.end()) - return Property::Null; + if (it == props.end()) return Property::Null; return *it->second.get(); } +template <class T> +auto Properties::at(type_key_t<T> &key) const +{ + auto f_key = key.family_key(); + auto it = props.find(f_key); + + if (it == props.end() || it->first.prop_type() != key.prop_type()) + return Option<decltype( + &(it->second.get()->template as<T>().value_ref()))>(); + + return make_option(&(it->second.get()->template as<T>().value_ref())); +} + template <class T, class... Args> -void Properties::set(const std::string& key, Args&&... args) +void Properties::set(type_key_t<T> &key, Args &&... args) { auto value = std::make_shared<T>(std::forward<Args>(args)...); // try to emplace the item + // TODO: There is uneccesary copying of value here. auto result = props.emplace(std::make_pair(key, value)); - // return if we succedded - if(result.second) - return; - - // the key already exists, replace the value it holds - result.first->second = std::move(value); + if (!result.second) { + // It is necessary to change key because the types from before and now + // could be different. + prop_key_t &key_ref = const_cast<prop_key_t &>(result.first->first); + key_ref = key; + result.first->second = std::move(value); + } } -void Properties::set(const std::string& key, Property::sptr value) +void Properties::set(prop_key_t &key, Property::sptr value) { - props[key] = std::move(value); + // TODO: There is uneccesary copying of value here. + auto result = props.insert(make_pair(key, value)); + if (!result.second) { + // It is necessary to change key because the types from before and now + // could be different. + prop_key_t &key_ref = const_cast<prop_key_t &>(result.first->first); + key_ref = key; + result.first->second = std::move(value); + } } -void Properties::clear(const std::string& key) -{ - props.erase(key); -} +void Properties::clear(prop_key_t &key) { props.erase(key); } // template <class Handler> // void Properties::accept(Handler& handler) const // { // for(auto& kv : props) // handler.handle(kv.first, *kv.second); -// +// // handler.finish(); // } -template<> -inline void Properties::set<Null>(const std::string& key) +template <> +inline void Properties::set<Null>(type_key_t<Null> &key) { - clear(key); + auto fk = key.family_key(); + clear(fk); } diff --git a/src/storage/model/properties/property_family.cpp b/src/storage/model/properties/property_family.cpp new file mode 100644 index 000000000..c6bc34dea --- /dev/null +++ b/src/storage/model/properties/property_family.cpp @@ -0,0 +1,39 @@ +#include "storage/model/properties/property_family.hpp" + +PropertyFamily::PropertyFamily(std::string const &name_v) + : name_v(std::forward<const std::string>(name_v)) +{ +} +PropertyFamily::PropertyFamily(std::string &&name_v) : name_v(std::move(name_v)) +{ +} + +std::string const &PropertyFamily::name() const { return name_v; } + +// Returns type if it exists otherwise creates it. +PropertyFamily::PropertyType &PropertyFamily::get(Type type) +{ + auto acc = types.access(); + auto it = acc.find(type); + if (it == acc.end()) { + auto value = + std::unique_ptr<PropertyType>(new PropertyType(*this, type)); + auto res = acc.insert(type, std::move(value)); + it = res.first; + } + return *(it->second); +} + +PropertyFamily::PropertyType::PropertyType(PropertyFamily &family, Type type) + : family(family), type(std::move(type)) +{ +} + +bool PropertyFamily::PropertyType::is(Type &t) const { return type == t; } + +// Returns key ordered on POINTERS to PropertyFamily +PropertyFamily::PropertyType::PropertyFamilyKey +PropertyFamily::PropertyType::family_key() +{ + return PropertyFamilyKey(*this); +} diff --git a/src/storage/record_accessor.cpp b/src/storage/record_accessor.cpp new file mode 100644 index 000000000..21198de4c --- /dev/null +++ b/src/storage/record_accessor.cpp @@ -0,0 +1,8 @@ +#include "storage/record_accessor.hpp" + +template <class T, class Derived, class vlist_t> +template <class V> +auto RecordAccessor<T, Derived, vlist_t>::at(type_key_t<V> &key) const +{ + return properties().template at<V>(key); +} diff --git a/src/storage/vertex_accessor.cpp b/src/storage/vertex_accessor.cpp index 250910689..ab27a5b3a 100644 --- a/src/storage/vertex_accessor.cpp +++ b/src/storage/vertex_accessor.cpp @@ -15,14 +15,20 @@ size_t Vertex::Accessor::in_degree() const size_t Vertex::Accessor::degree() const { return in_degree() + out_degree(); } -void Vertex::Accessor::add_label(const Label &label) +bool Vertex::Accessor::add_label(const Label &label) { // update vertex - this->record->data.labels.add(label); + if (this->record->data.labels.add(label)) { + label.index->insert(create_ir(std::nullptr_t())); + return true; + } + return false; +} - // update index - this->db.update_label_index(label, - VertexIndexRecord(this->record, this->vlist)); +bool Vertex::Accessor::remove_label(const Label &label) +{ + // update vertex + return this->record->data.labels.remove(label); } bool Vertex::Accessor::has_label(const Label &label) const diff --git a/src/storage/vertices.cpp b/src/storage/vertices.cpp index a6cf69c9a..84aaee3e2 100644 --- a/src/storage/vertices.cpp +++ b/src/storage/vertices.cpp @@ -1,4 +1,5 @@ #include "storage/vertices.hpp" +#include "utils/iterator/iterator.hpp" Vertices::vertices_t::Accessor Vertices::access() { return vertices.access(); } @@ -33,13 +34,21 @@ Vertex::Accessor Vertices::insert(DbTransaction &t) return Vertex::Accessor(vertex, &inserted_vertex_record->second, t); } -void Vertices::update_label_index(const Label &label, - VertexIndexRecord &&index_record) -{ - label_index.update(label, std::forward<VertexIndexRecord>(index_record)); -} +// +// Vertices::prop_familys_t::Accessor Vertices::property_family_access() +// { +// return prop_familys.access(); +// } -VertexIndexRecordCollection &Vertices::find_label_index(const Label &label) +PropertyFamily & +Vertices::property_family_find_or_create(const std::string &name) { - return label_index.find(label); + auto acc = prop_familys.access(); + auto it = acc.find(name); + if (it == acc.end()) { + auto family = std::unique_ptr<PropertyFamily>(new PropertyFamily(name)); + auto res = acc.insert(name, std::move(family)); + it = res.first; + } + return *(it->second); } diff --git a/tests/integration/queries.cpp b/tests/integration/queries.cpp index 81acd19b8..ed940dee1 100644 --- a/tests/integration/queries.cpp +++ b/tests/integration/queries.cpp @@ -1,4 +1,8 @@ #include "query_engine/hardcode/queries.hpp" +#include "storage/edges.cpp" +#include "storage/edges.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" #include "utils/assert.hpp" int main(void) @@ -14,11 +18,10 @@ int main(void) "CREATE (n:LABEL {name: \"TEST1\"}) RETURN n", "CREATE (n:LABEL {name: \"TEST2\"}) RETURN n", "CREATE (n:LABEL {name: \"TEST3\"}) RETURN n", - "CREATE (n:ACCOUNT {id: 2322, name: \"TEST\", country: \"Croatia\", created_at: 2352352}) RETURN n" , - "MATCH (n {id: 0}) RETURN n", - "MATCH (n {id: 1}) RETURN n", - "MATCH (n {id: 2}) RETURN n", - "MATCH (n {id: 3}) RETURN n", + "CREATE (n:ACCOUNT {id: 2322, name: \"TEST\", country: \"Croatia\", " + "created_at: 2352352}) RETURN n", + "MATCH (n {id: 0}) RETURN n", "MATCH (n {id: 1}) RETURN n", + "MATCH (n {id: 2}) RETURN n", "MATCH (n {id: 3}) RETURN n", "MATCH (a {id:0}), (p {id: 1}) CREATE (a)-[r:IS]->(p) RETURN r", "MATCH (a {id:1}), (p {id: 2}) CREATE (a)-[r:IS]->(p) RETURN r", "MATCH ()-[r]-() WHERE ID(r)=0 RETURN r", @@ -26,8 +29,7 @@ int main(void) "MATCH (n: {id: 0}) SET n.name = \"TEST100\" RETURN n", "MATCH (n: {id: 1}) SET n.name = \"TEST101\" RETURN n", "MATCH (n: {id: 0}) SET n.name = \"TEST102\" RETURN n", - "MATCH (n:LABEL) RETURN n" - }; + "MATCH (n:LABEL) RETURN n"}; for (auto &query : queries) { auto stripped = stripper.strip(query); diff --git a/tests/manual/queries.cpp b/tests/manual/queries.cpp index fbc0d2336..a65a9e231 100644 --- a/tests/manual/queries.cpp +++ b/tests/manual/queries.cpp @@ -1,6 +1,10 @@ #include <iostream> #include "query_engine/hardcode/queries.hpp" +#include "storage/edges.cpp" +#include "storage/edges.hpp" +#include "storage/vertices.cpp" +#include "storage/vertices.hpp" using namespace std; diff --git a/tests/unit/concurrent_list.cpp b/tests/unit/concurrent_list.cpp new file mode 100644 index 000000000..54d42a9c8 --- /dev/null +++ b/tests/unit/concurrent_list.cpp @@ -0,0 +1,75 @@ +#define CATCH_CONFIG_MAIN +#include "catch.hpp" + +#include "data_structures/concurrent/concurrent_list.hpp" + +TEST_CASE("Conncurent List insert") +{ + List<int> list; + auto it = list.begin(); + it.push(32); + it.reset(); + REQUIRE(*it == 32); +} + +TEST_CASE("Conncurent List iterate") +{ + List<int> list; + auto it = list.begin(); + it.push(32); + it.push(7); + it.push(9); + it.push(0); + it.reset(); + + REQUIRE(*it == 0); + it++; + REQUIRE(*it == 9); + it++; + REQUIRE(*it == 7); + it++; + REQUIRE(*it == 32); + it++; + REQUIRE(it == list.end()); +} + +TEST_CASE("Conncurent List head remove") +{ + List<int> list; + auto it = list.begin(); + it.push(32); + it.reset(); + + REQUIRE(it.remove()); + REQUIRE(it.is_removed()); + REQUIRE(!it.remove()); + + it.reset(); + REQUIRE(it == list.end()); +} + +TEST_CASE("Conncurent List remove") +{ + List<int> list; + auto it = list.begin(); + it.push(32); + it.push(7); + it.push(9); + it.push(0); + it.reset(); + + it++; + it++; + REQUIRE(it.remove()); + REQUIRE(it.is_removed()); + REQUIRE(!it.remove()); + + it.reset(); + REQUIRE(*it == 0); + it++; + REQUIRE(*it == 9); + it++; + REQUIRE(*it == 32); + it++; + REQUIRE(it == list.end()); +}