merged interface branch
This commit is contained in:
commit
427a7bbe4b
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ tags
|
||||
.gdb_history
|
||||
Testing/
|
||||
ve/
|
||||
release/memgraph_*
|
||||
release/libs/
|
||||
|
@ -218,10 +218,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)
|
||||
@ -407,6 +409,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
|
||||
@ -420,8 +424,8 @@ set(memgraph_src_files
|
||||
${src_dir}/io/network/tls.cpp
|
||||
${src_dir}/database/db.cpp
|
||||
${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
|
||||
|
@ -81,9 +81,8 @@ public:
|
||||
// write the identifier for the node
|
||||
encoder.write_integer(edge.id());
|
||||
|
||||
// TODO refactor when from() and to() start returning Accessors
|
||||
encoder.write_integer(edge.from_record()->id);
|
||||
encoder.write_integer(edge.to_record()->id);
|
||||
encoder.write_integer(edge.from().id());
|
||||
encoder.write_integer(edge.to().id());
|
||||
|
||||
// write the type of the edge
|
||||
encoder.write_string(edge.edge_type());
|
||||
|
@ -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;
|
||||
};
|
||||
|
283
include/data_structures/concurrent/concurrent_list.hpp
Normal file
283
include/data_structures/concurrent/concurrent_list.hpp
Normal file
@ -0,0 +1,283 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
template <class T>
|
||||
class List
|
||||
{
|
||||
private:
|
||||
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);
|
||||
}
|
||||
|
||||
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};
|
||||
};
|
@ -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); }
|
||||
|
@ -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.
|
||||
//
|
||||
|
@ -1,12 +1,13 @@
|
||||
#pragma once
|
||||
#include "utils/crtp.hpp"
|
||||
#include "utils/option_ptr.hpp"
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include "utils/crtp.hpp"
|
||||
#include "utils/option_ptr.hpp"
|
||||
|
||||
// RobinHood base.
|
||||
// Entrys are POINTERS alligned to 8B.
|
||||
// Entrys must know thers key.
|
||||
// Entries are POINTERS alligned to 8B.
|
||||
// Entries must know thers key.
|
||||
// D must have method K& get_key()
|
||||
// K must be comparable with ==.
|
||||
template <class K, class D, size_t init_size_pow2 = 2>
|
||||
@ -186,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); }
|
||||
@ -215,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;
|
||||
@ -240,6 +269,10 @@ protected:
|
||||
}
|
||||
|
||||
Iterator create_it(size_t index) { return Iterator(this, index); }
|
||||
ConstIterator create_it(size_t index) const
|
||||
{
|
||||
return ConstIterator(this, index);
|
||||
}
|
||||
|
||||
public:
|
||||
void clear()
|
||||
|
@ -2,11 +2,11 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "data_structures/map/rh_common.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/option.hpp"
|
||||
#include "utils/option_ptr.hpp"
|
||||
#include "data_structures/map/rh_common.hpp"
|
||||
|
||||
// HashMultiMap with RobinHood collision resolution policy.
|
||||
// Single threaded.
|
||||
@ -48,9 +48,30 @@ public:
|
||||
using typename base::ConstIterator;
|
||||
using typename base::Iterator;
|
||||
|
||||
bool contains(const K &key) { return find(key) != end(); }
|
||||
bool contains(const K &key) const { return find_index(key).is_present(); }
|
||||
|
||||
Iterator find(const K &key_in)
|
||||
{
|
||||
auto index = find_index(key_in);
|
||||
if (index) {
|
||||
return create_it(index.get());
|
||||
} else {
|
||||
return end();
|
||||
}
|
||||
}
|
||||
|
||||
ConstIterator find(const K &key_in) const
|
||||
{
|
||||
auto index = find_index(key_in);
|
||||
if (index) {
|
||||
return create_it(index.get());
|
||||
} else {
|
||||
return end();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Option<size_t> find_index(const K &key_in) const
|
||||
{
|
||||
if (count > 0) {
|
||||
auto key = std::ref(key_in);
|
||||
@ -62,7 +83,7 @@ public:
|
||||
while (other.valid() && off < border) {
|
||||
auto other_off = other.off();
|
||||
if (other_off == off && key == other.ptr()->get_key()) {
|
||||
return create_it(now);
|
||||
return Option<size_t>(now);
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
break;
|
||||
@ -76,9 +97,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
return end();
|
||||
return Option<size_t>();
|
||||
}
|
||||
|
||||
public:
|
||||
// Inserts element.
|
||||
void add(D *data) { add(data->get_key(), data); }
|
||||
|
||||
@ -214,7 +236,7 @@ public:
|
||||
private:
|
||||
// Skips same key valus as other. true if whole map is full of same key
|
||||
// values.
|
||||
bool skip(size_t &now, Combined &other, size_t other_off, size_t mask)
|
||||
bool skip(size_t &now, Combined &other, size_t other_off, size_t mask) const
|
||||
{
|
||||
auto other_key = other.ptr()->get_key();
|
||||
size_t start = now;
|
||||
|
@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/graph.hpp"
|
||||
// #include "transactions/commit_log.hpp"
|
||||
#include "transactions/engine.hpp"
|
||||
|
||||
class Db
|
||||
|
@ -1,50 +1,98 @@
|
||||
#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/vertex_accessor.hpp"
|
||||
#include "storage/vertices.hpp"
|
||||
#include "transactions/transaction.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
|
||||
* Vertex::Accessor and Edge::Accessor.
|
||||
* -Guarantees that changing Vertex and Edge is possible only using
|
||||
* Vertex::Accessor returned by vertex_insert() method and
|
||||
* Edge::Accessor returned by edge_insert() method.
|
||||
* -Offers CRUD for Vertex and Edge except iterating over all edges.
|
||||
*
|
||||
* Vertex::Accessor
|
||||
* By default Vertex::accessor is empty. Caller has to call fill() method
|
||||
* to fetch valid data and check it's return value. fill() method returns
|
||||
* true if there is valid data for current transaction false otherwise.
|
||||
* Only exception to this rule is vertex_insert() method in DbAccessor
|
||||
* which returns by default filled Vertex::Accessor.
|
||||
*
|
||||
* Edge::Accessor
|
||||
* By default Edge::accessor is empty. Caller has to call fill() method
|
||||
* to
|
||||
* fetch valid data and check it's return value. fill() method returns
|
||||
* true
|
||||
* if there is valid data for current transaction false otherwise.
|
||||
* Only exception to this rule is edge_insert() method in DbAccessor
|
||||
* which
|
||||
* returns by default filled Edge::Accessor.
|
||||
*/
|
||||
class DbAccessor
|
||||
{
|
||||
|
||||
public:
|
||||
DbAccessor(Db &db);
|
||||
|
||||
// VERTEX METHODS
|
||||
Vertices::vertices_t::Accessor vertex_access();
|
||||
//*******************VERTEX METHODS
|
||||
|
||||
const Vertex::Accessor vertex_find(const Id &id);
|
||||
auto vertex_access();
|
||||
|
||||
const Vertex::Accessor vertex_first();
|
||||
Option<const Vertex::Accessor> vertex_find(const Id &id);
|
||||
|
||||
// Creates new Vertex and returns filled Vertex::Accessor.
|
||||
Vertex::Accessor vertex_insert();
|
||||
|
||||
// EDGE METHODS
|
||||
Edge::Accessor edge_find(const Id &id);
|
||||
// ******************* EDGE METHODS
|
||||
|
||||
Edge::Accessor edge_insert(VertexRecord *from, VertexRecord *to);
|
||||
Option<const Edge::Accessor> edge_find(const Id &id);
|
||||
|
||||
// Creates new Edge and returns filled Edge::Accessor.
|
||||
Edge::Accessor edge_insert(Vertex::Accessor const &from,
|
||||
Vertex::Accessor const &to);
|
||||
|
||||
// ******************* LABEL METHODS
|
||||
|
||||
// LABEL METHODS
|
||||
const Label &label_find_or_create(const std::string &name);
|
||||
|
||||
bool label_contains(const std::string &name);
|
||||
|
||||
VertexIndexRecordCollection &label_find_index(const Label &label);
|
||||
// ******************** TYPE METHODS
|
||||
|
||||
// TYPE METHODS
|
||||
const EdgeType &type_find_or_create(const std::string &name);
|
||||
|
||||
bool type_contains(const std::string &name);
|
||||
|
||||
// TRANSACTION METHODS
|
||||
// ******************** PROPERTY METHODS
|
||||
|
||||
PropertyFamily &vertex_property_family_get(const std::string &name);
|
||||
|
||||
PropertyFamily &edge_property_family_get(const std::string &name);
|
||||
|
||||
// ******************** TRANSACTION METHODS
|
||||
|
||||
void commit();
|
||||
void abort();
|
||||
|
||||
// EASE OF USE METHODS
|
||||
tx::Transaction &operator*();
|
||||
private:
|
||||
template <class T, class K>
|
||||
friend class NonUniqueUnorderedIndex;
|
||||
|
||||
DbTransaction db;
|
||||
DbTransaction db_transaction;
|
||||
};
|
||||
|
||||
// ********************** CONVENIENT FUNCTIONS
|
||||
|
||||
template <class R>
|
||||
bool option_fill(Option<R> &o)
|
||||
{
|
||||
return o.is_present() && o.get().fill();
|
||||
}
|
||||
|
@ -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,10 +16,11 @@ class DbTransaction
|
||||
public:
|
||||
DbTransaction(Db &db, tx::Transaction &trans) : db(db), trans(trans) {}
|
||||
|
||||
void update_label_index(const Label &label,
|
||||
VertexIndexRecord &&index_record);
|
||||
// protected:
|
||||
// TRANSACTION METHODS
|
||||
// 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;
|
||||
|
||||
Db &db;
|
||||
|
@ -1,11 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/db.hpp"
|
||||
#include "database/db_accessor.cpp"
|
||||
#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)
|
||||
{
|
||||
@ -14,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;
|
||||
};
|
||||
@ -23,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());
|
||||
@ -34,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());
|
||||
@ -48,13 +72,13 @@ 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 vertex_accessor = t.vertex_find(Id(id.value));
|
||||
if (!vertex_accessor) {
|
||||
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();
|
||||
return false;
|
||||
}
|
||||
auto vertex_accessor = maybe_va.get();
|
||||
cout_properties(vertex_accessor.properties());
|
||||
cout << "LABELS:" << endl;
|
||||
for (auto label_ref : vertex_accessor.labels()) {
|
||||
@ -68,12 +92,12 @@ auto load_queries(Db &db)
|
||||
DbAccessor t(db);
|
||||
|
||||
auto v1 = t.vertex_find(args[0]->as<Int32>().value);
|
||||
if (!v1) return t.commit(), false;
|
||||
if (!option_fill(v1)) return t.commit(), false;
|
||||
|
||||
auto v2 = t.vertex_find(args[1]->as<Int32>().value);
|
||||
if (!v2) return t.commit(), false;
|
||||
if (!option_fill(v2)) return t.commit(), false;
|
||||
|
||||
auto edge_accessor = t.edge_insert(v1.vlist, v2.vlist);
|
||||
auto edge_accessor = t.edge_insert(v1.get(), v2.get());
|
||||
|
||||
auto &edge_type = t.type_find_or_create("IS");
|
||||
edge_accessor.edge_type(edge_type);
|
||||
@ -89,19 +113,24 @@ auto load_queries(Db &db)
|
||||
|
||||
auto find_edge_by_internal_id = [&db](const properties_t &args) {
|
||||
DbAccessor t(db);
|
||||
auto edge_accessor = t.edge_find(args[0]->as<Int32>().value);
|
||||
if (!edge_accessor) return t.commit(), false;
|
||||
auto maybe_ea = t.edge_find(args[0]->as<Int32>().value);
|
||||
if (!option_fill(maybe_ea)) return t.commit(), false;
|
||||
auto edge_accessor = maybe_ea.get();
|
||||
|
||||
// print edge type and properties
|
||||
cout << "EDGE_TYPE: " << edge_accessor.edge_type() << endl;
|
||||
|
||||
auto from = edge_accessor.from();
|
||||
if (!from.fill()) return t.commit(), false;
|
||||
|
||||
cout << "FROM:" << endl;
|
||||
cout_properties(from.record->data.props);
|
||||
cout_properties(from->data.props);
|
||||
|
||||
auto to = edge_accessor.to();
|
||||
if (!to.fill()) return t.commit(), false;
|
||||
|
||||
cout << "TO:" << endl;
|
||||
cout_properties(to.record->data.props);
|
||||
cout_properties(to->data.props);
|
||||
|
||||
t.commit();
|
||||
|
||||
@ -110,11 +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 v = t.vertex_find(args[0]->as<Int32>().value);
|
||||
if (!v) return t.commit(), false;
|
||||
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();
|
||||
@ -126,13 +159,20 @@ 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 (!n1) return t.commit(), false;
|
||||
if (!option_fill(n1)) return t.commit(), false;
|
||||
auto n2 = t.vertex_find(args[1]->as<Int64>().value);
|
||||
if (!n2) return t.commit(), false;
|
||||
auto r = t.edge_insert(n2.vlist, n1.vlist);
|
||||
r.property("age", args[2]);
|
||||
r.property("weight", args[3]);
|
||||
if (!option_fill(n2)) return t.commit(), false;
|
||||
auto r = t.edge_insert(n2.get(), n1.get());
|
||||
r.set(prop_age, args[2]);
|
||||
r.set(prop_weight, args[3]);
|
||||
auto &IS = t.type_find_or_create("IS");
|
||||
r.edge_type(IS);
|
||||
|
||||
@ -145,15 +185,11 @@ auto load_queries(Db &db)
|
||||
auto match_all_nodes = [&db](const properties_t &args) {
|
||||
DbAccessor t(db);
|
||||
|
||||
auto vertices_accessor = t.vertex_access();
|
||||
for (auto &it : vertices_accessor) {
|
||||
auto vertex = it.second.find(*t);
|
||||
if (vertex == nullptr) continue;
|
||||
cout_properties(vertex->data.props);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// db.graph.vertices.filter().all(t, handler);
|
||||
iter::for_all(t.vertex_access(), [&](auto vertex) {
|
||||
if (vertex.fill()) {
|
||||
cout_properties(vertex->data.props);
|
||||
}
|
||||
});
|
||||
|
||||
t.commit();
|
||||
|
||||
@ -166,21 +202,17 @@ 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;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// db.graph.vertices.fileter("LABEL").all(t, handler);
|
||||
iter::for_all(label.index->for_range_exact(t),
|
||||
[&](auto a) { cout << a.at(prop_key) << endl; });
|
||||
|
||||
return true;
|
||||
};
|
||||
queries[4857652843629217005u] = find_by_label;
|
||||
|
||||
queries[4857652843629217005u] = find_by_label;
|
||||
queries[10597108978382323595u] = create_account;
|
||||
queries[5397556489557792025u] = create_labeled_and_named_node;
|
||||
queries[7939106225150551899u] = create_edge;
|
||||
|
@ -40,5 +40,4 @@ std::string code_line(const std::string &format_str, const Args &... args)
|
||||
{
|
||||
return "\t" + format(format_str, args...) + "\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
class Edges;
|
||||
|
||||
// TODO: Edge, Db, Edge::Accessor
|
||||
class Edge::Accessor : public RecordAccessor<Edge, Edge::Accessor, EdgeRecord>
|
||||
{
|
||||
public:
|
||||
@ -22,8 +21,4 @@ public:
|
||||
Vertex::Accessor from() const;
|
||||
|
||||
Vertex::Accessor to() const;
|
||||
|
||||
VertexRecord *from_record() const;
|
||||
|
||||
VertexRecord *to_record() const;
|
||||
};
|
||||
|
@ -27,7 +27,7 @@ public:
|
||||
|
||||
auto to() const { return this->to_v; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
VertexRecord *from_v;
|
||||
VertexRecord *to_v;
|
||||
};
|
||||
|
@ -1,18 +1,31 @@
|
||||
#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:
|
||||
Edge::Accessor find(DbTransaction &t, const Id &id);
|
||||
Option<const Edge::Accessor> find(DbTransaction &t, const Id &id);
|
||||
|
||||
// Creates new Edge and returns filled Edge::Accessor.
|
||||
Edge::Accessor insert(DbTransaction &t, VertexRecord *from,
|
||||
VertexRecord *to);
|
||||
|
||||
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;
|
||||
};
|
||||
|
38
include/storage/indexes/impl/nonunique_unordered_index.hpp
Normal file
38
include/storage/indexes/impl/nonunique_unordered_index.hpp
Normal file
@ -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;
|
||||
};
|
@ -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;
|
||||
// };
|
||||
|
60
include/storage/indexes/index_base.hpp
Normal file
60
include/storage/indexes/index_base.hpp
Normal file
@ -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;
|
||||
};
|
@ -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>;
|
||||
|
@ -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>;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -23,7 +23,7 @@ public:
|
||||
edges.remove(edge); // Currently the return is ignored
|
||||
}
|
||||
|
||||
bool contains(VertexRecord *vr) { return edges.contains(vr); }
|
||||
bool contains(VertexRecord *vr) const { return edges.contains(vr); }
|
||||
|
||||
void clear() { edges.clear(); }
|
||||
|
||||
|
@ -6,4 +6,3 @@
|
||||
#include "storage/model/properties/int32.hpp"
|
||||
#include "storage/model/properties/int64.hpp"
|
||||
#include "storage/model/properties/string.hpp"
|
||||
|
||||
|
@ -8,20 +8,21 @@ public:
|
||||
static constexpr Flags type = Flags::Bool;
|
||||
|
||||
Bool(bool value);
|
||||
Bool(const Bool& other) = default;
|
||||
Bool(const Bool &other) = default;
|
||||
|
||||
bool value() const;
|
||||
|
||||
bool const &value_ref() const;
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
bool operator==(const Property& other) const override;
|
||||
bool operator==(const Property &other) const override;
|
||||
|
||||
bool operator==(const Bool& other) const;
|
||||
bool operator==(const Bool &other) const;
|
||||
|
||||
bool operator==(bool v) const;
|
||||
|
||||
std::ostream& print(std::ostream& stream) const override;
|
||||
std::ostream &print(std::ostream &stream) const override;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, const Bool& prop);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Bool &prop);
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@ struct Double : public Floating<Double>
|
||||
|
||||
Double(double value) : Floating(Flags::Double), value(value) {}
|
||||
|
||||
double const &value_ref() const { return value; }
|
||||
|
||||
double value;
|
||||
};
|
||||
|
||||
|
45
include/storage/model/properties/flags.hpp
Normal file
45
include/storage/model/properties/flags.hpp
Normal file
@ -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
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/model/properties/floating.hpp"
|
||||
#include "storage/model/properties/double.hpp"
|
||||
#include "storage/model/properties/floating.hpp"
|
||||
|
||||
class Float : public Floating<Float>
|
||||
{
|
||||
@ -10,10 +10,9 @@ public:
|
||||
|
||||
Float(float value) : Floating(Flags::Float), value(value) {}
|
||||
|
||||
operator Double() const
|
||||
{
|
||||
return Double(value);
|
||||
}
|
||||
operator Double() const { return Double(value); }
|
||||
|
||||
float const &value_ref() const { return value; }
|
||||
|
||||
float value;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "storage/model/properties/integral.hpp"
|
||||
#include "storage/model/properties/int64.hpp"
|
||||
#include "storage/model/properties/integral.hpp"
|
||||
|
||||
class Int32 : public Integral<Int32>
|
||||
{
|
||||
@ -10,11 +10,9 @@ public:
|
||||
|
||||
Int32(int32_t value) : Integral(Flags::Int32), value(value) {}
|
||||
|
||||
operator Int64() const
|
||||
{
|
||||
return Int64(value);
|
||||
}
|
||||
operator Int64() const { return Int64(value); }
|
||||
|
||||
int32_t const &value_ref() const { return value; }
|
||||
|
||||
int32_t value;
|
||||
};
|
||||
|
||||
|
@ -9,6 +9,7 @@ public:
|
||||
|
||||
Int64(int64_t value) : Integral(Flags::Int64), value(value) {}
|
||||
|
||||
int64_t const &value_ref() const { return value; }
|
||||
|
||||
int64_t value;
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
181
include/storage/model/properties/property_family.hpp
Normal file
181
include/storage/model/properties/property_family.hpp
Normal file
@ -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;
|
||||
};
|
@ -7,23 +7,25 @@ class String : public Property
|
||||
public:
|
||||
static constexpr Flags type = Flags::String;
|
||||
|
||||
String(const String&) = default;
|
||||
String(String&&) = default;
|
||||
String(const String &) = default;
|
||||
String(String &&) = default;
|
||||
|
||||
String(const std::string& value);
|
||||
String(std::string&& value);
|
||||
String(const std::string &value);
|
||||
String(std::string &&value);
|
||||
|
||||
operator const std::string&() const;
|
||||
operator const std::string &() const;
|
||||
|
||||
bool operator==(const Property& other) const override;
|
||||
bool operator==(const Property &other) const override;
|
||||
|
||||
bool operator==(const String& other) const;
|
||||
bool operator==(const String &other) const;
|
||||
|
||||
bool operator==(const std::string& other) const;
|
||||
bool operator==(const std::string &other) const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, const String& prop);
|
||||
friend std::ostream &operator<<(std::ostream &stream, const String &prop);
|
||||
|
||||
std::ostream& print(std::ostream& stream) const override;
|
||||
std::ostream &print(std::ostream &stream) const override;
|
||||
|
||||
std::string const &value_ref() const { return value; }
|
||||
|
||||
std::string value;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -2,15 +2,22 @@
|
||||
|
||||
#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>>
|
||||
class RecordAccessor
|
||||
{
|
||||
friend DbAccessor;
|
||||
|
||||
public:
|
||||
RecordAccessor(DbTransaction &db) : db(db){};
|
||||
RecordAccessor(vlist_t *vlist, DbTransaction &db) : vlist(vlist), db(db)
|
||||
{
|
||||
assert(vlist != nullptr);
|
||||
}
|
||||
|
||||
RecordAccessor(T *t, vlist_t *vlist, DbTransaction &db)
|
||||
: record(t), vlist(vlist), db(db)
|
||||
@ -19,15 +26,19 @@ public:
|
||||
assert(vlist != nullptr);
|
||||
}
|
||||
|
||||
RecordAccessor(vlist_t *vlist, DbTransaction &db)
|
||||
: record(vlist->find(db.trans)), vlist(vlist), db(db)
|
||||
{
|
||||
assert(record != nullptr);
|
||||
assert(vlist != nullptr);
|
||||
}
|
||||
RecordAccessor(RecordAccessor const &other) = default;
|
||||
RecordAccessor(RecordAccessor &&other) = default;
|
||||
|
||||
bool empty() const { return record == nullptr; }
|
||||
|
||||
// Fills accessor and returns true if there is valid data for current
|
||||
// transaction false otherwise.
|
||||
bool fill() const
|
||||
{
|
||||
const_cast<RecordAccessor *>(this)->record = vlist->find(db.trans);
|
||||
return record != nullptr;
|
||||
}
|
||||
|
||||
const Id &id() const
|
||||
{
|
||||
assert(!empty());
|
||||
@ -48,28 +59,62 @@ 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; }
|
||||
|
||||
explicit operator bool() const { return record != nullptr; }
|
||||
|
||||
// protected:
|
||||
T *const record{nullptr};
|
||||
vlist_t *const vlist{nullptr};
|
||||
T const *operator->() const { return record; }
|
||||
T *operator->() { return record; }
|
||||
|
||||
// Assumes same transaction
|
||||
friend bool operator==(const RecordAccessor &a, const RecordAccessor &b)
|
||||
{
|
||||
return a.vlist == b.vlist;
|
||||
}
|
||||
|
||||
// Assumes same transaction
|
||||
friend bool operator!=(const RecordAccessor &a, const RecordAccessor &b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
protected:
|
||||
IndexRecord<T, std::nullptr_t> create_index_record()
|
||||
{
|
||||
return create_index_record(std::nullptr_t());
|
||||
}
|
||||
|
||||
template <class K>
|
||||
IndexRecord<T, K> create_index_record(K &&key)
|
||||
{
|
||||
return IndexRecord<T, K>(std::move(key), record, vlist);
|
||||
}
|
||||
|
||||
T *record{nullptr};
|
||||
vlist_t *const vlist;
|
||||
DbTransaction &db;
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "mvcc/record.hpp"
|
||||
#include "storage/model/vertex_model.hpp"
|
||||
#include "storage/model/properties/traversers/jsonwriter.hpp"
|
||||
#include "storage/model/vertex_model.hpp"
|
||||
|
||||
class Vertex : public mvcc::Record<Vertex>
|
||||
{
|
||||
@ -10,19 +10,19 @@ public:
|
||||
class Accessor;
|
||||
|
||||
Vertex() = default;
|
||||
Vertex(const VertexModel& data) : data(data) {}
|
||||
Vertex(VertexModel&& data) : data(std::move(data)) {}
|
||||
Vertex(const VertexModel &data) : data(data) {}
|
||||
Vertex(VertexModel &&data) : data(std::move(data)) {}
|
||||
|
||||
Vertex(const Vertex&) = delete;
|
||||
Vertex(Vertex&&) = delete;
|
||||
Vertex(const Vertex &) = delete;
|
||||
Vertex(Vertex &&) = delete;
|
||||
|
||||
Vertex& operator=(const Vertex&) = delete;
|
||||
Vertex& operator=(Vertex&&) = delete;
|
||||
Vertex &operator=(const Vertex &) = delete;
|
||||
Vertex &operator=(Vertex &&) = delete;
|
||||
|
||||
VertexModel data;
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& stream, const Vertex& record)
|
||||
inline std::ostream &operator<<(std::ostream &stream, const Vertex &record)
|
||||
{
|
||||
StringBuffer buffer;
|
||||
JsonWriter<StringBuffer> writer(buffer);
|
||||
@ -33,6 +33,5 @@ inline std::ostream& operator<<(std::ostream& stream, const Vertex& record)
|
||||
|
||||
return stream << "Vertex"
|
||||
<< "(cre = " << record.tx.cre()
|
||||
<< ", exp = " << record.tx.exp()
|
||||
<< "): " << buffer.str();
|
||||
<< ", exp = " << record.tx.exp() << "): " << buffer.str();
|
||||
}
|
||||
|
@ -10,15 +10,32 @@ 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;
|
||||
|
||||
const std::set<label_ref_t> &labels() const;
|
||||
|
||||
auto out() const;
|
||||
|
||||
auto in() const;
|
||||
|
||||
// True if there exists edge other->this
|
||||
bool in_contains(Vertex::Accessor const &other) const;
|
||||
};
|
||||
|
@ -1,32 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "database/db_transaction.hpp"
|
||||
#include "storage/common.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();
|
||||
|
||||
const Vertex::Accessor find(DbTransaction &t, const Id &id);
|
||||
|
||||
const Vertex::Accessor first(DbTransaction &t);
|
||||
Option<const Vertex::Accessor> find(DbTransaction &t, const Id &id);
|
||||
|
||||
// Creates new Vertex and returns filled Vertex::Accessor.
|
||||
Vertex::Accessor insert(DbTransaction &t);
|
||||
|
||||
void update_label_index(const Label &label,
|
||||
VertexIndexRecord &&index_record);
|
||||
|
||||
VertexIndexRecordCollection &find_label_index(const Label &label);
|
||||
PropertyFamily &property_family_find_or_create(const std::string &name);
|
||||
|
||||
private:
|
||||
vertices_t vertices;
|
||||
Index<label_ref_t, VertexIndexRecordCollection> label_index;
|
||||
// TODO: Because families wont be removed this could be done with more
|
||||
// efficent
|
||||
// data structure.
|
||||
prop_familys_t prop_familys;
|
||||
AtomicCounter<uint64_t> counter;
|
||||
};
|
||||
|
51
include/utils/border.hpp
Normal file
51
include/utils/border.hpp
Normal file
@ -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>();
|
||||
}
|
35
include/utils/iterator/accessor.hpp
Normal file
35
include/utils/iterator/accessor.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#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<RangeIterator<T, I>>()) {}
|
||||
OneTimeAccessor(I &&it) : it(RangeIterator<T, I>(std::move(it))) {}
|
||||
|
||||
RangeIterator<T, I> begin() { return it.take(); }
|
||||
|
||||
RangeIterator<T, I> end() { return RangeIterator<T, I>(); }
|
||||
|
||||
private:
|
||||
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));
|
||||
}
|
||||
}
|
27
include/utils/iterator/for_all.hpp
Normal file
27
include/utils/iterator/for_all.hpp
Normal file
@ -0,0 +1,27 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/option.hpp"
|
||||
|
||||
namespace iter
|
||||
{
|
||||
template <class I, class C>
|
||||
void for_all(I &&iter, C &&consumer)
|
||||
{
|
||||
auto e = iter.next();
|
||||
while (e.is_present()) {
|
||||
consumer(e.take());
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
9
include/utils/iterator/iterator.hpp
Normal file
9
include/utils/iterator/iterator.hpp
Normal file
@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/iterator/accessor.hpp"
|
||||
#include "utils/iterator/for_all.hpp"
|
||||
#include "utils/iterator/iterator_accessor.hpp"
|
||||
#include "utils/iterator/iterator_base.hpp"
|
||||
#include "utils/iterator/lambda_iterator.hpp"
|
||||
#include "utils/iterator/map.hpp"
|
||||
#include "utils/iterator/range_iterator.hpp"
|
59
include/utils/iterator/iterator_accessor.hpp
Normal file
59
include/utils/iterator/iterator_accessor.hpp
Normal file
@ -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);
|
||||
}
|
||||
}
|
12
include/utils/iterator/iterator_base.hpp
Normal file
12
include/utils/iterator/iterator_base.hpp
Normal file
@ -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;
|
||||
};
|
32
include/utils/iterator/lambda_iterator.hpp
Normal file
32
include/utils/iterator/lambda_iterator.hpp
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/iterator/iterator_base.hpp"
|
||||
|
||||
namespace iter
|
||||
{
|
||||
// Wraps lambda into interator with next().
|
||||
// T - type of return value
|
||||
// F - type of wraped lambda
|
||||
template <class T, class F>
|
||||
class LambdaIterator : public IteratorBase<T>
|
||||
{
|
||||
public:
|
||||
LambdaIterator(F &&f) : func(std::move(f)) {}
|
||||
|
||||
Option<T> next() final { return func(); }
|
||||
|
||||
private:
|
||||
F func;
|
||||
};
|
||||
|
||||
// Wraps lambda 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 LambdaIterator<decltype(f().take()), F>(std::move(f));
|
||||
}
|
||||
}
|
48
include/utils/iterator/map.hpp
Normal file
48
include/utils/iterator/map.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/iterator/iterator_base.hpp"
|
||||
#include "utils/option.hpp"
|
||||
|
||||
namespace iter
|
||||
{
|
||||
|
||||
// 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;
|
||||
|
||||
// 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<T>(op(item.take()));
|
||||
} else {
|
||||
return Option<T>();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
I iter;
|
||||
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));
|
||||
}
|
||||
}
|
74
include/utils/iterator/range_iterator.hpp
Normal file
74
include/utils/iterator/range_iterator.hpp
Normal file
@ -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));
|
||||
}
|
||||
}
|
113
include/utils/option.hpp
Normal file
113
include/utils/option.hpp
Normal file
@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <ext/aligned_buffer.h>
|
||||
#include <utility>
|
||||
|
||||
template <class T>
|
||||
class Option
|
||||
{
|
||||
public:
|
||||
Option() {}
|
||||
//
|
||||
// Option(T item)
|
||||
// {
|
||||
// new (data._M_addr()) T(std::forward(item));
|
||||
// initialized = true;
|
||||
// }
|
||||
|
||||
Option(T const &item)
|
||||
{
|
||||
new (data._M_addr()) T(item);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
Option(T &&item)
|
||||
{
|
||||
new (data._M_addr()) T(std::move(item));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
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) {
|
||||
data = std::move(other.data);
|
||||
other.initialized = false;
|
||||
initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
~Option()
|
||||
{
|
||||
if (initialized) get().~T();
|
||||
}
|
||||
|
||||
Option &operator=(Option &other) = default;
|
||||
Option &operator=(Option &&other)
|
||||
{
|
||||
if (initialized) {
|
||||
get().~T();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
if (other.initialized) {
|
||||
data = std::move(other.data);
|
||||
other.initialized = false;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_present() const { return initialized; }
|
||||
|
||||
T &get() noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
const T &get() const noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
T take()
|
||||
{
|
||||
assert(initialized);
|
||||
initialized = false;
|
||||
return std::move(*data._M_ptr());
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
auto make_option()
|
||||
{
|
||||
return Option<T>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto make_option(T &&data)
|
||||
{
|
||||
return Option<T>(std::move(data));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
auto make_option_const(const T &&data)
|
||||
{
|
||||
return Option<const T>(std::move(data));
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
#include <ext/aligned_buffer.h>
|
||||
#include <utility>
|
||||
|
||||
template <class T>
|
||||
class Placeholder
|
||||
@ -9,40 +9,41 @@ class Placeholder
|
||||
public:
|
||||
Placeholder() = default;
|
||||
|
||||
Placeholder(Placeholder&) = delete;
|
||||
Placeholder(Placeholder&&) = delete;
|
||||
Placeholder(Placeholder &) = delete;
|
||||
Placeholder(Placeholder &&) = delete;
|
||||
|
||||
~Placeholder()
|
||||
{
|
||||
if(initialized)
|
||||
get().~T();
|
||||
if (initialized) get().~T();
|
||||
};
|
||||
|
||||
T& get() noexcept
|
||||
bool is_initialized() { return initialized; }
|
||||
|
||||
T &get() noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
const T& get() const noexcept
|
||||
const T &get() const noexcept
|
||||
{
|
||||
assert(initialized);
|
||||
return *data._M_ptr();
|
||||
}
|
||||
|
||||
void set(const T& item)
|
||||
void set(const T &item)
|
||||
{
|
||||
new (data._M_addr()) T(item);
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void set(T&& item)
|
||||
void set(T &&item)
|
||||
{
|
||||
new (data._M_addr()) T(std::move(item));
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
private:
|
||||
__gnu_cxx::__aligned_buffer<T> data;
|
||||
__gnu_cxx::__aligned_buffer<T> data;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
10
include/utils/void.hpp
Normal file
10
include/utils/void.hpp
Normal file
@ -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; }
|
||||
};
|
352
poc/astar.cpp
352
poc/astar.cpp
@ -11,32 +11,59 @@
|
||||
#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;
|
||||
void load_graph_dummy(Db &db);
|
||||
void load_csv(Db &db, char *file_path, char *edge_file_path);
|
||||
int load_csv(Db &db, char *file_path, char *edge_file_path);
|
||||
|
||||
class Node
|
||||
{
|
||||
public:
|
||||
Node *parent = {nullptr};
|
||||
type_key_t<Double> tkey;
|
||||
double cost;
|
||||
int depth = {0};
|
||||
Vertex *vertex;
|
||||
VertexRecord *record;
|
||||
VertexAccessor vacc;
|
||||
|
||||
Node(Vertex *va, VertexRecord *record, double cost)
|
||||
: cost(cost), vertex(va), record(record)
|
||||
Node(VertexAccessor vacc, double cost, type_key_t<Double> tkey)
|
||||
: cost(cost), vacc(vacc), tkey(tkey)
|
||||
{
|
||||
}
|
||||
Node(Vertex *va, VertexRecord *record, double cost, Node *parent)
|
||||
: cost(cost), vertex(va), parent(parent), depth(parent->depth + 1),
|
||||
record(record)
|
||||
Node(VertexAccessor vacc, double cost, Node *parent,
|
||||
type_key_t<Double> tkey)
|
||||
: cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1),
|
||||
tkey(tkey)
|
||||
{
|
||||
}
|
||||
|
||||
VertexRecord *&get_key() { return record; }
|
||||
double sum_vertex_score()
|
||||
{
|
||||
auto now = this;
|
||||
double sum = 0;
|
||||
do {
|
||||
sum += *(now->vacc.at(tkey).get());
|
||||
now = now->parent;
|
||||
} while (now != nullptr);
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
class Score
|
||||
{
|
||||
public:
|
||||
Score() : value(std::numeric_limits<double>::max()) {}
|
||||
Score(double v) : value(v) {}
|
||||
double value;
|
||||
};
|
||||
|
||||
// class Iterator : public Crtp<Iterator>
|
||||
@ -79,118 +106,144 @@ public:
|
||||
// Node *head;
|
||||
// };
|
||||
|
||||
void found_result(Node *bef)
|
||||
void found_result(Node *res)
|
||||
{
|
||||
std::cout << "{score: " << bef->cost << endl;
|
||||
double sum = res->sum_vertex_score();
|
||||
|
||||
std::cout << "{score: " << sum << endl;
|
||||
auto bef = res;
|
||||
while (bef != nullptr) {
|
||||
std::cout << " " << *(bef->vertex) << endl;
|
||||
std::cout << " " << *(bef->vacc.operator->()) << endl;
|
||||
bef = bef->parent;
|
||||
}
|
||||
}
|
||||
|
||||
double calc_heuristic_cost_dummy(Edge *edge, Vertex *vertex)
|
||||
double calc_heuristic_cost_dummy(type_key_t<Double> tkey, Edge::Accessor &edge,
|
||||
Vertex::Accessor &vertex)
|
||||
{
|
||||
return 1 - vertex->data.props.at("score").as<Double>().value;
|
||||
assert(!vertex.empty());
|
||||
return 1 - *vertex.at(tkey).get();
|
||||
}
|
||||
|
||||
typedef bool (*EdgeFilter)(DbAccessor &t, EdgeRecord *, Node *before);
|
||||
typedef bool (*VertexFilter)(DbAccessor &t, Vertex *, Node *before);
|
||||
typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before);
|
||||
typedef bool (*VertexFilter)(DbAccessor &t, Vertex::Accessor &, Node *before);
|
||||
|
||||
bool edge_filter_dummy(DbAccessor &t, EdgeRecord *e, Node *before)
|
||||
bool edge_filter_dummy(DbAccessor &t, Edge::Accessor &e, Node *before)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vertex_filter_dummy(DbAccessor &t, Vertex *v, Node *before)
|
||||
bool vertex_filter_dummy(DbAccessor &t, Vertex::Accessor &va, Node *before)
|
||||
{
|
||||
return true;
|
||||
return va.fill();
|
||||
}
|
||||
|
||||
bool vertex_filter_contained_dummy(DbAccessor &t, Vertex *v, Node *before)
|
||||
bool vertex_filter_contained_dummy(DbAccessor &t, Vertex::Accessor &v,
|
||||
Node *before)
|
||||
{
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
before = before->parent;
|
||||
if (before == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for (auto edge : before->vertex->data.out) {
|
||||
Vertex *e_v = edge->to()->find(*t);
|
||||
if (e_v == v) {
|
||||
found = true;
|
||||
break;
|
||||
if (v.fill()) {
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
before = before->parent;
|
||||
if (before == nullptr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while (found);
|
||||
auto it = before->vacc.out();
|
||||
for (auto e = it.next(); e.is_present(); e = it.next()) {
|
||||
VertexAccessor va = e.get().to();
|
||||
if (va == v) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (found);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool vertex_filter_contained(DbAccessor &t, Vertex *v, Node *before)
|
||||
bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before)
|
||||
{
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
before = before->parent;
|
||||
if (before == nullptr) {
|
||||
return true;
|
||||
}
|
||||
} while (v->data.in.contains(before->record));
|
||||
if (v.fill()) {
|
||||
bool found;
|
||||
do {
|
||||
found = false;
|
||||
before = before->parent;
|
||||
if (before == nullptr) {
|
||||
return true;
|
||||
}
|
||||
} while (v.in_contains(before->vacc));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Vertex filter ima max_depth funkcija te edge filter ima max_depth funkcija.
|
||||
// Jedan za svaku dubinu.
|
||||
// Filtri vracaju true ako element zadovoljava uvjete.
|
||||
void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
|
||||
auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
|
||||
VertexFilter v_filter[],
|
||||
double (*calc_heuristic_cost)(Edge *edge, Vertex *vertex),
|
||||
double (*calc_heuristic_cost)(type_key_t<Double> tkey,
|
||||
Edge::Accessor &edge,
|
||||
Vertex::Accessor &vertex),
|
||||
int limit)
|
||||
{
|
||||
DbAccessor t(db);
|
||||
RhHashMap<VertexRecord *, Node> visited;
|
||||
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;
|
||||
auto cmp = [](Node *left, Node *right) { return left->cost > right->cost; };
|
||||
std::priority_queue<Node *, std::vector<Node *>, decltype(cmp)> queue(cmp);
|
||||
|
||||
auto start_vr = t.vertex_find(sys_id_start).vlist;
|
||||
Node *start = new Node(start_vr->find(*t), start_vr, 0);
|
||||
auto start_vr = t.vertex_find(sys_id_start);
|
||||
assert(start_vr);
|
||||
start_vr.get().fill();
|
||||
Node *start = new Node(start_vr.take(), 0, tkey);
|
||||
queue.push(start);
|
||||
int count = 0;
|
||||
do {
|
||||
auto now = queue.top();
|
||||
queue.pop();
|
||||
|
||||
// if(!visited.insert(now)){
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if (max_depth <= now->depth) {
|
||||
found_result(now);
|
||||
best.push_back(now);
|
||||
count++;
|
||||
if (count >= limit) {
|
||||
return;
|
||||
return best;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto edge : now->vertex->data.out) {
|
||||
// { // FOUND FILTER
|
||||
// Score &bef = best_found[now->depth][now->vacc.id()];
|
||||
// if (bef.value <= now->cost) {
|
||||
// continue;
|
||||
// }
|
||||
// bef.value = now->cost;
|
||||
// }
|
||||
|
||||
iter::for_all(now->vacc.out(), [&](auto edge) {
|
||||
if (e_filter[now->depth](t, edge, now)) {
|
||||
Vertex *v = edge->to()->find(*t);
|
||||
if (v_filter[now->depth](t, v, now)) {
|
||||
Node *n = new Node(
|
||||
v, edge->to(),
|
||||
now->cost + calc_heuristic_cost(edge->find(*t), v),
|
||||
now);
|
||||
VertexAccessor va = edge.to();
|
||||
if (v_filter[now->depth](t, va, now)) {
|
||||
auto cost = calc_heuristic_cost(tkey, edge, va);
|
||||
Node *n = new Node(va, now->cost + cost, now, tkey);
|
||||
queue.push(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} while (!queue.empty());
|
||||
std::cout << "Found: " << count << " resoults\n";
|
||||
// std::cout << "Found: " << count << " resoults\n";
|
||||
// TODO: GUBI SE MEMORIJA JER SE NODOVI NEBRISU
|
||||
|
||||
t.commit();
|
||||
return best;
|
||||
}
|
||||
|
||||
// class Data
|
||||
@ -206,23 +259,66 @@ void a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
|
||||
// const int &get_key() { return key; }
|
||||
// };
|
||||
|
||||
int main()
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
std::cout << "Not enough input values\n";
|
||||
return 0;
|
||||
} else if (argc > 4) {
|
||||
std::cout << "To much input values\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
Db db;
|
||||
load_csv(db, "graph_nodes_export.csv", "graph_edges_export.csv");
|
||||
//
|
||||
// load_graph_dummy(db);
|
||||
//
|
||||
auto vertex_no = load_csv(db, argv[argc - 2], argv[argc - 1]);
|
||||
|
||||
EdgeFilter e_filters[] = {&edge_filter_dummy, &edge_filter_dummy,
|
||||
&edge_filter_dummy, &edge_filter_dummy};
|
||||
VertexFilter f_filters[] = {
|
||||
&vertex_filter_contained, &vertex_filter_contained,
|
||||
&vertex_filter_contained, &vertex_filter_contained};
|
||||
auto begin = clock();
|
||||
a_star(db, 0, 3, e_filters, f_filters, &calc_heuristic_cost_dummy, 10);
|
||||
clock_t end = clock();
|
||||
double elapsed_ms = (double(end - begin) / CLOCKS_PER_SEC) * 1000;
|
||||
std::cout << "A-star: " << elapsed_ms << " [ms]\n";
|
||||
|
||||
// CONF
|
||||
std::srand(time(0));
|
||||
auto best_n = 10;
|
||||
auto bench_n = 1000;
|
||||
auto best_print_n = 10;
|
||||
bool pick_best_found = argc > 3 ? true : false;
|
||||
|
||||
double sum = 0;
|
||||
std::vector<Node *> best;
|
||||
for (int i = 0; i < bench_n; i++) {
|
||||
auto start_vertex_index = std::rand() % vertex_no;
|
||||
|
||||
auto begin = clock();
|
||||
auto found = a_star(db, start_vertex_index, 3, e_filters, f_filters,
|
||||
&calc_heuristic_cost_dummy, best_n);
|
||||
clock_t end = clock();
|
||||
|
||||
double elapsed_ms = (double(end - begin) / CLOCKS_PER_SEC) * 1000;
|
||||
sum += elapsed_ms;
|
||||
|
||||
if ((best.size() < best_print_n && found.size() > best.size()) ||
|
||||
(pick_best_found && found.size() > 0 &&
|
||||
found.front()->sum_vertex_score() >
|
||||
best.front()->sum_vertex_score())) {
|
||||
best = found;
|
||||
}
|
||||
|
||||
// Just to be safe
|
||||
if (i + 1 == bench_n && best.size() == 0) {
|
||||
bench_n++;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "\nSearch for best " << best_n
|
||||
<< " results has runing time of:\n avg: " << sum / bench_n
|
||||
<< " [ms]\n";
|
||||
std::cout << "\nExample of best result:\n";
|
||||
for (int i = 0; i < best_print_n && best.size() > 0; i++) {
|
||||
found_result(best.front());
|
||||
best.erase(best.begin());
|
||||
}
|
||||
|
||||
// RhHashMultiMap benchmark
|
||||
// const int n_pow2 = 20;
|
||||
@ -261,7 +357,7 @@ vector<string> split(const string &s, char delim)
|
||||
return elems;
|
||||
}
|
||||
|
||||
void load_csv(Db &db, char *file_path, char *edge_file_path)
|
||||
int load_csv(Db &db, char *file_path, char *edge_file_path)
|
||||
{
|
||||
std::fstream file(file_path);
|
||||
std::fstream e_file(edge_file_path);
|
||||
@ -269,6 +365,18 @@ void 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
|
||||
@ -279,21 +387,25 @@ void 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));
|
||||
// from Kruno's head :) (could be ALMOST anything else)
|
||||
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);
|
||||
}
|
||||
return vertex_accessor.id();
|
||||
|
||||
return vertex_accessor;
|
||||
};
|
||||
|
||||
// Skip header
|
||||
std::getline(file, line);
|
||||
|
||||
vector<Vertex::Accessor> va;
|
||||
@ -307,11 +419,12 @@ void load_csv(Db &db, char *file_path, char *edge_file_path)
|
||||
auto splited = split(line, ',');
|
||||
vector<string> labels(splited.begin() + 1,
|
||||
splited.begin() + splited.size() - 2);
|
||||
auto id = v(stoi(splited[0]), labels, stoi(splited[splited.size() - 2]),
|
||||
stoi(splited[splited.size() - 1]));
|
||||
auto vacs =
|
||||
v(stoi(splited[0]), labels, stoi(splited[splited.size() - 2]),
|
||||
stoi(splited[splited.size() - 1]));
|
||||
|
||||
assert(va.size() == (uint64_t)id);
|
||||
va.push_back(t.vertex_find(id));
|
||||
assert(va.size() == (uint64_t)vacs.id());
|
||||
va.push_back(vacs);
|
||||
}
|
||||
|
||||
// EDGE IMPORT
|
||||
@ -320,7 +433,7 @@ void load_csv(Db &db, char *file_path, char *edge_file_path)
|
||||
|
||||
auto v2 = va[to - start_vertex_id];
|
||||
|
||||
auto edge_accessor = t.edge_insert(v1.vlist, v2.vlist);
|
||||
auto edge_accessor = t.edge_insert(v1, v2);
|
||||
|
||||
auto &edge_type = t.type_find_or_create(type);
|
||||
edge_accessor.edge_type(edge_type);
|
||||
@ -338,44 +451,47 @@ void load_csv(Db &db, char *file_path, char *edge_file_path)
|
||||
<< endl;
|
||||
|
||||
t.commit();
|
||||
return v_count;
|
||||
}
|
||||
|
||||
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.vlist, v2.vlist);
|
||||
|
||||
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);
|
||||
|
||||
// TODO: update code
|
||||
// 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();
|
||||
}
|
||||
|
@ -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) {}
|
||||
|
@ -1,77 +1,86 @@
|
||||
#include "database/db_accessor.hpp"
|
||||
#include "database/db.hpp"
|
||||
#include "utils/iterator/iterator.hpp"
|
||||
|
||||
DbAccessor::DbAccessor(Db &db) : db(DbTransaction(db, db.tx_engine.begin())) {}
|
||||
DbAccessor::DbAccessor(Db &db)
|
||||
: db_transaction(DbTransaction(db, db.tx_engine.begin()))
|
||||
{
|
||||
}
|
||||
|
||||
// VERTEX METHODS
|
||||
Vertices::vertices_t::Accessor DbAccessor::vertex_access()
|
||||
auto DbAccessor::vertex_access()
|
||||
{
|
||||
return this->db.db.graph.vertices.access();
|
||||
return iter::make_map(
|
||||
iter::make_iter(this->db_transaction.db.graph.vertices.access()),
|
||||
[&](auto e) -> auto {
|
||||
return Vertex::Accessor(&(e->second), db_transaction);
|
||||
});
|
||||
}
|
||||
|
||||
const Vertex::Accessor DbAccessor::vertex_find(const Id &id)
|
||||
Option<const Vertex::Accessor> DbAccessor::vertex_find(const Id &id)
|
||||
{
|
||||
return this->db.db.graph.vertices.find(db, id);
|
||||
}
|
||||
|
||||
const Vertex::Accessor DbAccessor::vertex_first()
|
||||
{
|
||||
return this->db.db.graph.vertices.first(db);
|
||||
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
|
||||
|
||||
Edge::Accessor DbAccessor::edge_find(const Id &id)
|
||||
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(VertexRecord *from, VertexRecord *to)
|
||||
Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from,
|
||||
Vertex::Accessor const &to)
|
||||
{
|
||||
auto edge_accessor = db.db.graph.edges.insert(db, from, to);
|
||||
from->update(db.trans)->data.out.add(edge_accessor.vlist);
|
||||
to->update(db.trans)->data.in.add(edge_accessor.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;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
// TRANSACTION METHODS
|
||||
void DbAccessor::commit() { db.trans.commit(); }
|
||||
void DbAccessor::abort() { db.trans.abort(); }
|
||||
PropertyFamily &DbAccessor::vertex_property_family_get(const std::string &name)
|
||||
{
|
||||
return db_transaction.db.graph.vertices.property_family_find_or_create(
|
||||
name);
|
||||
}
|
||||
|
||||
// EASE OF USE METHODS
|
||||
tx::Transaction &DbAccessor::operator*() { return db.trans; }
|
||||
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_transaction.trans.commit(); }
|
||||
void DbAccessor::abort() { db_transaction.trans.abort(); }
|
||||
|
@ -1,8 +0,0 @@
|
||||
#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));
|
||||
}
|
@ -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);
|
||||
|
@ -20,10 +20,3 @@ Vertex::Accessor Edge::Accessor::to() const
|
||||
{
|
||||
return Vertex::Accessor(this->vlist->to(), this->db);
|
||||
}
|
||||
|
||||
VertexRecord *Edge::Accessor::from_record() const
|
||||
{
|
||||
return this->vlist->from();
|
||||
}
|
||||
|
||||
VertexRecord *Edge::Accessor::to_record() const { return this->vlist->to(); }
|
||||
|
@ -1,18 +1,16 @@
|
||||
#include "storage/edges.hpp"
|
||||
#include "storage/model/properties/property_family.hpp"
|
||||
#include "utils/iterator/iterator.hpp"
|
||||
|
||||
Edge::Accessor Edges::find(DbTransaction &t, const Id &id)
|
||||
Option<const Edge::Accessor> Edges::find(DbTransaction &t, const Id &id)
|
||||
{
|
||||
auto edges_accessor = edges.access();
|
||||
auto edges_iterator = edges_accessor.find(id);
|
||||
|
||||
if (edges_iterator == edges_accessor.end()) return Edge::Accessor(t);
|
||||
if (edges_iterator == edges_accessor.end())
|
||||
return make_option<const Edge::Accessor>();
|
||||
|
||||
// find edge
|
||||
auto edge = edges_iterator->second.find(t.trans);
|
||||
|
||||
if (edge == nullptr) return Edge::Accessor(t);
|
||||
|
||||
return Edge::Accessor(edge, &edges_iterator->second, t);
|
||||
return make_option_const(Edge::Accessor(&edges_iterator->second, t));
|
||||
}
|
||||
|
||||
Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from,
|
||||
@ -34,3 +32,18 @@ Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from,
|
||||
|
||||
return Edge::Accessor(edge, &inserted_edge_record->second, t);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
61
src/storage/indexes/impl/nonunique_unordered_index.cpp
Normal file
61
src/storage/indexes/impl/nonunique_unordered_index.cpp
Normal file
@ -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>;
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include "storage/model/properties/bool.hpp"
|
||||
|
||||
const bool TRUE = true;
|
||||
const bool FALSE = false;
|
||||
|
||||
Bool::Bool(bool value) : Property(value ? Flags::True : Flags::False) {}
|
||||
|
||||
bool Bool::value() const
|
||||
@ -15,32 +18,25 @@ bool Bool::value() const
|
||||
return (underlying_cast(flags) - underlying_cast(Flags::True)) == 0;
|
||||
}
|
||||
|
||||
Bool::operator bool() const
|
||||
{
|
||||
return value();
|
||||
}
|
||||
bool const &Bool::value_ref() const { return value() ? TRUE : FALSE; }
|
||||
|
||||
bool Bool::operator==(const Property& other) const
|
||||
Bool::operator bool() const { return value(); }
|
||||
|
||||
bool Bool::operator==(const Property &other) const
|
||||
{
|
||||
return other.is<Bool>() && operator==(other.as<Bool>());
|
||||
}
|
||||
|
||||
bool Bool::operator==(const Bool& other) const
|
||||
{
|
||||
return other.flags == flags;
|
||||
}
|
||||
bool Bool::operator==(const Bool &other) const { return other.flags == flags; }
|
||||
|
||||
bool Bool::operator==(bool v) const
|
||||
{
|
||||
return value() == v;
|
||||
}
|
||||
bool Bool::operator==(bool v) const { return value() == v; }
|
||||
|
||||
std::ostream& Bool::print(std::ostream& stream) const
|
||||
std::ostream &Bool::print(std::ostream &stream) const
|
||||
{
|
||||
return operator<<(stream, *this);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, const Bool& prop)
|
||||
std::ostream &operator<<(std::ostream &stream, const Bool &prop)
|
||||
{
|
||||
return stream << prop.value();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
39
src/storage/model/properties/property_family.cpp
Normal file
39
src/storage/model/properties/property_family.cpp
Normal file
@ -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);
|
||||
}
|
8
src/storage/record_accessor.cpp
Normal file
8
src/storage/record_accessor.cpp
Normal file
@ -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);
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
#include "database/db.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/vertices.hpp"
|
||||
#include "utils/iterator/iterator.hpp"
|
||||
|
||||
size_t Vertex::Accessor::out_degree() const
|
||||
{
|
||||
@ -14,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_index_record());
|
||||
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
|
||||
@ -33,3 +40,26 @@ const std::set<label_ref_t> &Vertex::Accessor::labels() const
|
||||
{
|
||||
return this->record->data.labels();
|
||||
}
|
||||
|
||||
// Returns unfilled accessors
|
||||
auto Vertex::Accessor::out() const
|
||||
{
|
||||
DbTransaction &t = this->db;
|
||||
return iter::make_map(
|
||||
iter::make_iter_ref(record->data.out),
|
||||
[&](auto e) -> auto { return Edge::Accessor(*e, t); });
|
||||
}
|
||||
|
||||
// Returns unfilled accessors
|
||||
auto Vertex::Accessor::in() const
|
||||
{
|
||||
DbTransaction &t = this->db;
|
||||
return iter::make_one_time_accessor(
|
||||
iter::make_map(iter::make_iter_ref(record->data.in),
|
||||
[&](auto e) -> auto { return Edge::Accessor(e, t); }));
|
||||
}
|
||||
|
||||
bool Vertex::Accessor::in_contains(Vertex::Accessor const &other) const
|
||||
{
|
||||
return record->data.in.contains(other.vlist);
|
||||
}
|
||||
|
@ -1,37 +1,17 @@
|
||||
#include "storage/vertices.hpp"
|
||||
#include "utils/iterator/iterator.hpp"
|
||||
|
||||
Vertices::vertices_t::Accessor Vertices::access() { return vertices.access(); }
|
||||
|
||||
const Vertex::Accessor Vertices::find(DbTransaction &t, const Id &id)
|
||||
Option<const Vertex::Accessor> Vertices::find(DbTransaction &t, const Id &id)
|
||||
{
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto vertices_iterator = vertices_accessor.find(id);
|
||||
|
||||
if (vertices_iterator == vertices_accessor.end())
|
||||
return Vertex::Accessor(t);
|
||||
return make_option<const Vertex::Accessor>();
|
||||
|
||||
// find vertex
|
||||
auto vertex = vertices_iterator->second.find(t.trans);
|
||||
|
||||
if (vertex == nullptr) return Vertex::Accessor(t);
|
||||
|
||||
return Vertex::Accessor(vertex, &vertices_iterator->second, t);
|
||||
}
|
||||
|
||||
// TODO
|
||||
const Vertex::Accessor Vertices::first(DbTransaction &t)
|
||||
{
|
||||
auto vertices_accessor = vertices.access();
|
||||
auto vertices_iterator = vertices_accessor.begin();
|
||||
|
||||
if (vertices_iterator == vertices_accessor.end())
|
||||
return Vertex::Accessor(t);
|
||||
|
||||
auto vertex = vertices_iterator->second.find(t.trans);
|
||||
|
||||
if (vertex == nullptr) return Vertex::Accessor(t);
|
||||
|
||||
return Vertex::Accessor(vertex, &vertices_iterator->second, t);
|
||||
return make_option_const(Vertex::Accessor(&vertices_iterator->second, t));
|
||||
}
|
||||
|
||||
Vertex::Accessor Vertices::insert(DbTransaction &t)
|
||||
@ -54,13 +34,15 @@ 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)
|
||||
PropertyFamily &
|
||||
Vertices::property_family_find_or_create(const std::string &name)
|
||||
{
|
||||
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
|
||||
}
|
||||
|
||||
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "data_structures/static_array.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
using std::cout;
|
||||
@ -231,3 +233,9 @@ void memory_check(size_t no_threads, std::function<void()> f)
|
||||
std::cout << "leaked: " << leaked << "\n";
|
||||
permanent_assert(leaked <= 0, "Memory leak check");
|
||||
}
|
||||
|
||||
//Initializes loging faccilityes
|
||||
void init_log(){
|
||||
logging::init_async();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
#include "common.h"
|
||||
#include "data_structures/linked_list.hpp"
|
||||
|
||||
using std::cout;
|
||||
@ -10,51 +11,52 @@ using std::endl;
|
||||
template <typename list_type>
|
||||
void test_concurrent_list_access(list_type &list, std::size_t size)
|
||||
{
|
||||
// test concurrent access
|
||||
for (int i = 0; i < 1000000; ++i) {
|
||||
// test concurrent access
|
||||
for (int i = 0; i < 1000000; ++i) {
|
||||
|
||||
std::thread t1([&list] {
|
||||
list.push_front(1);
|
||||
list.pop_front();
|
||||
});
|
||||
std::thread t1([&list] {
|
||||
list.push_front(1);
|
||||
list.pop_front();
|
||||
});
|
||||
|
||||
std::thread t2([&list] {
|
||||
list.push_front(2);
|
||||
list.pop_front();
|
||||
});
|
||||
std::thread t2([&list] {
|
||||
list.push_front(2);
|
||||
list.pop_front();
|
||||
});
|
||||
|
||||
t1.join();
|
||||
t2.join();
|
||||
t1.join();
|
||||
t2.join();
|
||||
|
||||
assert(list.size() == size);
|
||||
}
|
||||
assert(list.size() == size);
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
LinkedList<int> list;
|
||||
init_log();
|
||||
LinkedList<int> list;
|
||||
|
||||
// push & pop operations
|
||||
list.push_front(10);
|
||||
list.push_front(20);
|
||||
auto a = list.front();
|
||||
assert(a == 20);
|
||||
list.pop_front();
|
||||
a = list.front();
|
||||
assert(a == 10);
|
||||
list.pop_front();
|
||||
assert(list.size() == 0);
|
||||
// push & pop operations
|
||||
list.push_front(10);
|
||||
list.push_front(20);
|
||||
auto a = list.front();
|
||||
assert(a == 20);
|
||||
list.pop_front();
|
||||
a = list.front();
|
||||
assert(a == 10);
|
||||
list.pop_front();
|
||||
assert(list.size() == 0);
|
||||
|
||||
// concurrent test
|
||||
LinkedList<int> concurrent_list;
|
||||
concurrent_list.push_front(1);
|
||||
concurrent_list.push_front(1);
|
||||
std::list<int> no_concurrent_list;
|
||||
no_concurrent_list.push_front(1);
|
||||
no_concurrent_list.push_front(1);
|
||||
// concurrent test
|
||||
LinkedList<int> concurrent_list;
|
||||
concurrent_list.push_front(1);
|
||||
concurrent_list.push_front(1);
|
||||
std::list<int> no_concurrent_list;
|
||||
no_concurrent_list.push_front(1);
|
||||
no_concurrent_list.push_front(1);
|
||||
|
||||
test_concurrent_list_access(concurrent_list, 2);
|
||||
// test_concurrent_list_access(no_concurrent_list, 2);
|
||||
test_concurrent_list_access(concurrent_list, 2);
|
||||
// test_concurrent_list_access(no_concurrent_list, 2);
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
|
||||
// Test checks for missing data and changed/overwriten data.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -10,6 +10,7 @@ constexpr size_t key_range = elems_per_thread * THREADS_NO * 2;
|
||||
// Test checks for missing data and changed/overwriten data.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -5,6 +5,7 @@ constexpr size_t elems_per_thread = 1e5;
|
||||
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [&] {
|
||||
ds::static_array<std::thread, THREADS_NO> threads;
|
||||
map_t skiplist;
|
||||
|
@ -7,18 +7,20 @@ constexpr size_t elements = 2e6;
|
||||
// Test for simple memory leaks
|
||||
int main()
|
||||
{
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
auto futures = run<size_t>(THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
for (size_t i = 0; i < elements; i++) {
|
||||
acc.insert(i, index);
|
||||
}
|
||||
return index;
|
||||
auto futures =
|
||||
run<size_t>(THREADS_NO, skiplist, [](auto acc, auto index) {
|
||||
for (size_t i = 0; i < elements; i++) {
|
||||
acc.insert(i, index);
|
||||
}
|
||||
return index;
|
||||
});
|
||||
collect(futures);
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
check_size<map_t>(accessor, elements);
|
||||
});
|
||||
collect(futures);
|
||||
|
||||
auto accessor = skiplist.access();
|
||||
check_size<map_t>(accessor, elements);
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ constexpr size_t elems_per_thread = 16e5;
|
||||
// Known memory leak at 1,600,000 elements.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [&] {
|
||||
ds::static_array<std::thread, THREADS_NO> threads;
|
||||
map_t skiplist;
|
||||
|
@ -13,6 +13,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// succeed.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
multimap_t skiplist;
|
||||
|
||||
|
@ -14,6 +14,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// succeed.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
multimap_t skiplist;
|
||||
|
||||
|
@ -15,6 +15,7 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// succeed.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
multimap_t skiplist;
|
||||
|
||||
|
@ -13,6 +13,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// succeed.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
multimap_t skiplist;
|
||||
std::atomic<long long> size(0);
|
||||
|
@ -11,6 +11,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// succeed.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
multiset_t skiplist;
|
||||
|
||||
|
@ -12,6 +12,7 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -10,6 +10,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -12,6 +12,7 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -10,6 +10,7 @@ constexpr size_t no_insert_for_one_delete = 2;
|
||||
// Calls of remove method are interleaved with insert calls.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
set_t skiplist;
|
||||
|
||||
|
@ -14,6 +14,7 @@ constexpr size_t no_insert_for_one_delete = 1;
|
||||
// no_find_per_change and no_insert_for_one_delete.
|
||||
int main()
|
||||
{
|
||||
init_log();
|
||||
memory_check(THREADS_NO, [] {
|
||||
map_t skiplist;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1,21 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <deque>
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "communication/bolt/v1/transport/chunked_encoder.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
|
||||
using byte = unsigned char;
|
||||
|
||||
void print_hex(byte x)
|
||||
{
|
||||
printf("%02X ", static_cast<byte>(x));
|
||||
}
|
||||
void print_hex(byte x) { printf("%02X ", static_cast<byte>(x)); }
|
||||
|
||||
class DummyStream
|
||||
{
|
||||
public:
|
||||
void write(const byte* values, size_t n)
|
||||
void write(const byte *values, size_t n)
|
||||
{
|
||||
num_calls++;
|
||||
data.insert(data.end(), values, values + n);
|
||||
@ -28,36 +27,33 @@ public:
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t pop_size()
|
||||
{
|
||||
return ((size_t)pop() << 8) | pop();
|
||||
}
|
||||
size_t pop_size() { return ((size_t)pop() << 8) | pop(); }
|
||||
|
||||
void print()
|
||||
{
|
||||
for(size_t i = 0; i < data.size(); ++i)
|
||||
for (size_t i = 0; i < data.size(); ++i)
|
||||
print_hex(data[i]);
|
||||
}
|
||||
|
||||
std::deque<byte> data;
|
||||
size_t num_calls {0};
|
||||
size_t num_calls{0};
|
||||
};
|
||||
|
||||
using Encoder = bolt::ChunkedEncoder<DummyStream>;
|
||||
|
||||
void write_ff(Encoder& encoder, size_t n)
|
||||
void write_ff(Encoder &encoder, size_t n)
|
||||
{
|
||||
std::vector<byte> v;
|
||||
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
v.push_back('\xFF');
|
||||
|
||||
encoder.write(v.data(), v.size());
|
||||
}
|
||||
|
||||
void check_ff(DummyStream& stream, size_t n)
|
||||
void check_ff(DummyStream &stream, size_t n)
|
||||
{
|
||||
for(size_t i = 0; i < n; ++i)
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
assert(stream.pop() == byte('\xFF'));
|
||||
|
||||
(void)stream;
|
||||
@ -65,6 +61,8 @@ void check_ff(DummyStream& stream, size_t n)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
logging::init_async();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
DummyStream stream;
|
||||
bolt::ChunkedEncoder<DummyStream> encoder(stream);
|
||||
|
||||
@ -80,7 +78,7 @@ int main(void)
|
||||
write_ff(encoder, 67000);
|
||||
encoder.flush();
|
||||
|
||||
for(int i = 0; i < 10000; ++i)
|
||||
for (int i = 0; i < 10000; ++i)
|
||||
write_ff(encoder, 1500);
|
||||
encoder.flush();
|
||||
|
||||
@ -100,8 +98,7 @@ int main(void)
|
||||
|
||||
size_t k = 10000 * 1500;
|
||||
|
||||
while(k > 0)
|
||||
{
|
||||
while (k > 0) {
|
||||
auto size = k > encoder.chunk_size ? encoder.chunk_size : k;
|
||||
assert(stream.pop_size() == size);
|
||||
check_ff(stream, size);
|
||||
|
75
tests/unit/concurrent_list.cpp
Normal file
75
tests/unit/concurrent_list.cpp
Normal file
@ -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());
|
||||
}
|
@ -3,6 +3,8 @@
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
using std::cout;
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "data_structures/concurrent/concurrent_set.hpp"
|
||||
#include "logging/default.hpp"
|
||||
#include "logging/streams/stdout.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
using std::cout;
|
||||
@ -18,6 +20,8 @@ void print_skiplist(const ConcurrentSet<int>::Accessor &skiplist)
|
||||
|
||||
int main(void)
|
||||
{
|
||||
logging::init_async();
|
||||
logging::log->pipe(std::make_unique<Stdout>());
|
||||
ConcurrentSet<int> set;
|
||||
|
||||
auto accessor = set.access();
|
||||
|
Loading…
Reference in New Issue
Block a user