Alpha version of label indexes.

Squashed messages from 9 commits:

9.
Properties now uses PropertyFamily and contained classes.
Fetching,seting,clearing properties can be done with PropertyFamilyKey or PropertyTypeKey.
Hierarchy of newly added clases is:
Vertices -n-> PropertyFamily {name: String} <-1-n-> PropertyType {type: Property::Flags}
Edges -n-> PropertyFamily {name: String} <-1-n-> PropertyType {type: Property::Flags}

PropertyFamilyKey -> PropertyType
PropertyTypeKey -> PropertyType

PropertyType t0,t1;
let t0!=t1 be true
let t0.family==t1.family be true

then next is true
PropertyTypeKey{&t0}!=PropertyTypeKey{&t1}
PropertyFamilyKey{&t0}==PropertyFamilyKey{&t1}
PropertyFamilyKey{&t0}==PropertyTypeKey{&t1}
PropertyTypeKey{&t0}==PropertyFamilyKey{&t1}

8.
Intermedate commit.
Noticed that integration queries throw SEGFAULT.

7.
Defined interface for indexes.
Fixed three memory leaks.
Fixed integration_queries test which now passes.

6.
Commit which return Xorshift128plus to valid shape.

5.
Tmp commit.

4.
Label Index is compiling.

3.
tmp

2.
Vertex::Accessor now updates Label index.

1.
Applied changes for code review.
This commit is contained in:
Kruno Tomola Fabro 2016-08-18 15:34:36 +01:00
parent 530be96b36
commit 5a42e15c4a
60 changed files with 1791 additions and 574 deletions

View File

@ -213,10 +213,12 @@ FILE(COPY ${include_dir}/storage/model/properties/string.hpp DESTINATION ${build
FILE(COPY ${include_dir}/storage/model/properties/floating.hpp DESTINATION ${build_include_dir}/storage/model/properties)
FILE(COPY ${include_dir}/storage/model/properties/number.hpp DESTINATION ${build_include_dir}/storage/model/properties)
FILE(COPY ${include_dir}/storage/model/properties/integral.hpp DESTINATION ${build_include_dir}/storage/model/properties)
FILE(COPY ${include_dir}/storage/model/properties/property_family.hpp DESTINATION ${build_include_dir}/storage/model/properties)
FILE(COPY ${include_dir}/storage/model/properties/utils/math_operations.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils)
FILE(COPY ${include_dir}/storage/model/properties/utils/unary_negation.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils)
FILE(COPY ${include_dir}/storage/model/properties/utils/modulo.hpp DESTINATION ${build_include_dir}/storage/model/properties/utils)
FILE(COPY ${include_dir}/storage/model/edge_model.hpp DESTINATION ${build_include_dir}/storage/model)
FILE(COPY ${include_dir}/storage/model/property_model.hpp DESTINATION ${build_include_dir}/storage/model)
FILE(COPY ${include_dir}/storage/model/vertex_model.hpp DESTINATION ${build_include_dir}/storage/model)
@ -401,6 +403,8 @@ set(memgraph_src_files
${src_dir}/storage/model/properties/bool.cpp
${src_dir}/storage/model/properties/string.cpp
${src_dir}/storage/model/properties/properties.cpp
${src_dir}/storage/model/properties/property_family.cpp
${src_dir}/storage/indexes/impl/nonunique_unordered_index.cpp
${src_dir}/storage/locking/record_lock.cpp
${src_dir}/storage/vertex_accessor.cpp
${src_dir}/transactions/transaction.cpp
@ -416,6 +420,8 @@ set(memgraph_src_files
${src_dir}/database/db_accessor.cpp
${src_dir}/database/db_transaction.cpp
${src_dir}/storage/edge_accessor.cpp
${src_dir}/storage/record_accessor.cpp
)
# STATIC library used by memgraph executables

View File

@ -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;
};

View File

@ -0,0 +1,284 @@
#pragma once
#include <atomic>
#include <cassert>
#include "utils/crtp.hpp"
template <class T>
class List
{
template <class V>
static V load(std::atomic<V> &atomic)
{
return atomic.load(std::memory_order_acquire);
}
template <class V>
static void store(std::atomic<V> &atomic, V desired)
{ // Maybe could be relaxed
atomic.store(desired, std::memory_order_release);
}
template <class V>
static bool cas(std::atomic<V> &atomic, V expected, V desired)
{ // Could be relaxed must be atleast Release.
return atomic.compare_exchange_strong(expected, desired,
std::memory_order_seq_cst);
}
template <class V>
static V *swap(std::atomic<V *> &atomic, V *desired)
{ // Could be relaxed
return atomic.exchange(desired, std::memory_order_seq_cst);
}
private:
class Node
{
public:
Node(const T &data) : data(data) {}
Node(T &&data) : data(std::move(data)) {}
T data;
std::atomic<Node *> next{nullptr};
std::atomic<Node *> next_rem{nullptr};
std::atomic<bool> removed{false};
};
template <class It>
class IteratorBase : public Crtp<It>
{
friend class List;
protected:
IteratorBase() : list(nullptr), curr(nullptr) {}
IteratorBase(List *list) : list(list)
{
assert(list != nullptr);
list->count++;
reset();
}
public:
IteratorBase(const IteratorBase &) = delete;
IteratorBase(IteratorBase &&other)
: list(other.list), curr(other.curr), prev(other.prev)
{
other.list = nullptr;
other.curr = nullptr;
}
~IteratorBase()
{
if (list == nullptr) {
return;
}
auto head_rem = load(list->removed);
// Fetch could be relaxed
// There exist possibility that no one will delete garbage at this
// time.
if (list->count.fetch_sub(1) == 1 && head_rem != nullptr &&
cas<Node *>(
list->removed, head_rem,
nullptr)) { // I am the last one and there is garbage to be
// removed.
auto now = head_rem;
do {
auto next = load(now->next_rem);
delete now;
now = next;
} while (now != nullptr);
}
}
T &operator*() const
{
assert(valid());
return curr->data;
}
T *operator->() const
{
assert(valid());
return &(curr->data);
}
bool valid() const { return curr != nullptr; }
// Iterating is wait free.
It &operator++()
{
assert(valid());
do {
prev = curr;
curr = load(curr->next);
} while (valid() && is_removed());
return this->derived();
}
It &operator++(int) { return operator++(); }
bool is_removed()
{
assert(valid());
return load(curr->removed);
}
// Returns IteratorBase to begining
void reset()
{
prev = nullptr;
curr = load(list->head);
while (valid() && is_removed()) {
operator++();
}
}
// Adds to the begining of list
// It is lock free but it isn't wait free.
void push(T &&data)
{
auto node = new Node(data);
Node *next = nullptr;
do {
next = load(list->head);
store(node->next, next);
} while (!cas(list->head, next, node));
}
// True only if this call removed the element. Only reason for fail is
// if
// the element is already removed.
// Remove has deadlock if another thread dies between marking node for
// removal
// and the disconnection.
// This can be improved with combinig the removed flag with prev.next or
// curr.next
bool remove()
{
assert(valid());
if (cas(curr->removed, false, true)) {
if (!disconnect()) {
find_and_disconnect();
}
store(curr->next_rem, swap(list->removed, curr));
return true;
}
return false;
}
friend bool operator==(const It &a, const It &b)
{
return a.curr == b.curr;
}
friend bool operator!=(const It &a, const It &b) { return !(a == b); }
private:
void find_and_disconnect()
{
auto it = It(list);
auto next = load(curr->next);
while (it.valid()) {
if (it.curr == curr) {
if (it.disconnect()) {
return;
}
it.reset();
} else if (it.curr == next) { // Comparison with next is
// optimization for early return.
return;
} else {
it++;
}
}
}
bool disconnect()
{
auto next = load(curr->next);
if (prev != nullptr) {
store(prev->next, next);
if (load(prev->removed)) {
return false;
}
} else if (!cas(list->head, curr, next)) {
return false;
}
return true;
}
List *list;
Node *prev{nullptr};
Node *curr;
};
public:
class ConstIterator : public IteratorBase<ConstIterator>
{
friend class List;
public:
using IteratorBase<ConstIterator>::IteratorBase;
const T &operator*() const
{
return IteratorBase<ConstIterator>::operator*();
}
const T *operator->() const
{
return IteratorBase<ConstIterator>::operator->();
}
operator const T &() const
{
return IteratorBase<ConstIterator>::operator T &();
}
};
class Iterator : public IteratorBase<Iterator>
{
friend class List;
public:
using IteratorBase<Iterator>::IteratorBase;
};
public:
List() = default;
List(List &) = delete;
List(List &&) = delete;
~List()
{
auto now = head.load();
while (now != nullptr) {
auto next = now->next.load();
delete now;
now = next;
}
}
void operator=(List &) = delete;
Iterator begin() { return Iterator(this); }
// ConstIterator begin() { return ConstIterator(this); }
ConstIterator cbegin() { return ConstIterator(this); }
Iterator end() { return Iterator(); }
// ConstIterator end() { return ConstIterator(); }
ConstIterator cend() { return ConstIterator(); }
private:
std::atomic<std::size_t> count{0};
std::atomic<Node *> head{nullptr};
std::atomic<Node *> removed{nullptr};
};

View File

@ -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); }

View File

@ -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.
//

View File

@ -1,15 +1,20 @@
#pragma once
#include "database/db.hpp"
#include "database/db_accessor.hpp"
#include "storage/record_accessor.hpp"
#include "storage/vertex.hpp"
#include "database/db_transaction.hpp"
// #include "storage/record_accessor.hpp"
// #include "storage/vertex.hpp"
#include "storage/vertex_accessor.hpp"
#include "storage/vertices.hpp"
#include "transactions/transaction.hpp"
#include "utils/iterator/iterator.hpp"
// #include "storage/vertices.hpp"
// #include "transactions/transaction.hpp"
// #include "utils/iterator/iterator.hpp"
#include "utils/border.hpp"
#include "utils/option.hpp"
namespace tx
{
class Transaction;
}
/*
* DbAccessor
* -Guarantees that access to Vertex and Edge is possible only through
@ -65,21 +70,31 @@ public:
bool label_contains(const std::string &name);
VertexIndexRecordCollection &label_find_index(const Label &label);
//********************TYPE METHODS
const EdgeType &type_find_or_create(const std::string &name);
bool type_contains(const std::string &name);
//********************PROPERTY METHODS
// Vertices::prop_familys_t::Accessor vertex_property_family_access();
//
// auto edge_property_family_access();
PropertyFamily &vertex_property_family_get(const std::string &name);
PropertyFamily &edge_property_family_get(const std::string &name);
//********************TRANSACTION METHODS
void commit();
void abort();
private:
DbTransaction db;
template <class T, class K>
friend class NonUniqueUnorderedIndex;
DbTransaction db_transaction;
};
//**********************CONVENIENT FUNCTIONS

View File

@ -1,7 +1,5 @@
#pragma once
#include "storage/indexes/index_record.hpp"
#include "storage/label/label.hpp"
#include "transactions/transaction.hpp"
class Db;
@ -9,6 +7,8 @@ class DbAccessor;
// Inner structures local to transaction can hold ref to this structure and use
// its methods.
// Also serves as a barrier for calling methods defined public but meant for
// internal use. That kind of method should request DbTransaction&.
class DbTransaction
{
friend DbAccessor;
@ -16,8 +16,10 @@ class DbTransaction
public:
DbTransaction(Db &db, tx::Transaction &trans) : db(db), trans(trans) {}
void update_label_index(const Label &label,
VertexIndexRecord &&index_record);
// Global transactional algorithms,operations and general methods meant for
// internal use should be here or should be routed through this object.
// This should provide cleaner hierarchy of operations on database.
// For example cleaner.
tx::Transaction &trans;

View File

@ -5,8 +5,11 @@
#include "database/db_accessor.hpp"
#include "query_engine/query_stripper.hpp"
#include "query_engine/util.hpp"
#include "storage/indexes/impl/nonunique_unordered_index.cpp"
#include "storage/model/properties/property.hpp"
#include "storage/model/properties/property_family.hpp"
#include "utils/command_line/arguments.hpp"
#include "utils/iterator/iterator.hpp"
auto load_queries(Db &db)
{
@ -15,8 +18,12 @@ auto load_queries(Db &db)
// CREATE (n {prop: 0}) RETURN n)
auto create_node = [&db](const properties_t &args) {
DbAccessor t(db);
auto prop_key = t.vertex_property_family_get("prop")
.get(args[0]->flags)
.family_key();
auto vertex_accessor = t.vertex_insert();
vertex_accessor.property("prop", args[0]);
vertex_accessor.set(prop_key, args[0]);
t.commit();
return true;
};
@ -24,8 +31,12 @@ auto load_queries(Db &db)
auto create_labeled_and_named_node = [&db](const properties_t &args) {
DbAccessor t(db);
auto prop_key = t.vertex_property_family_get("name")
.get(args[0]->flags)
.family_key();
auto vertex_accessor = t.vertex_insert();
vertex_accessor.property("name", args[0]);
vertex_accessor.set(prop_key, args[0]);
auto &label = t.label_find_or_create("LABEL");
vertex_accessor.add_label(label);
cout_properties(vertex_accessor.properties());
@ -35,11 +46,23 @@ auto load_queries(Db &db)
auto create_account = [&db](const properties_t &args) {
DbAccessor t(db);
auto prop_id =
t.vertex_property_family_get("id").get(args[0]->flags).family_key();
auto prop_name = t.vertex_property_family_get("name")
.get(args[1]->flags)
.family_key();
auto prop_country = t.vertex_property_family_get("country")
.get(args[2]->flags)
.family_key();
auto prop_created = t.vertex_property_family_get("created_at")
.get(args[3]->flags)
.family_key();
auto vertex_accessor = t.vertex_insert();
vertex_accessor.property("id", args[0]);
vertex_accessor.property("name", args[1]);
vertex_accessor.property("country", args[2]);
vertex_accessor.property("created_at", args[3]);
vertex_accessor.set(prop_id, args[0]);
vertex_accessor.set(prop_name, args[1]);
vertex_accessor.set(prop_country, args[2]);
vertex_accessor.set(prop_created, args[3]);
auto &label = t.label_find_or_create("ACCOUNT");
vertex_accessor.add_label(label);
cout_properties(vertex_accessor.properties());
@ -49,8 +72,7 @@ auto load_queries(Db &db)
auto find_node_by_internal_id = [&db](const properties_t &args) {
DbAccessor t(db);
auto id = static_cast<Int32 &>(*args[0]);
auto maybe_va = t.vertex_find(Id(id.value));
auto maybe_va = t.vertex_find(Id(args[0]->as<Int32>().value));
if (!option_fill(maybe_va)) {
cout << "vertex doesn't exist" << endl;
t.commit();
@ -117,12 +139,15 @@ auto load_queries(Db &db)
auto update_node = [&db](const properties_t &args) {
DbAccessor t(db);
auto prop_name = t.vertex_property_family_get("name")
.get(args[1]->flags)
.family_key();
auto maybe_v = t.vertex_find(args[0]->as<Int32>().value);
if (!option_fill(maybe_v)) return t.commit(), false;
auto v = maybe_v.get();
v.property("name", args[1]);
v.set(prop_name, args[1]);
cout_properties(v.properties());
t.commit();
@ -134,13 +159,19 @@ auto load_queries(Db &db)
// weight: 70}]-(n2) RETURN r
auto create_edge_v2 = [&db](const properties_t &args) {
DbAccessor t(db);
auto prop_age =
t.edge_property_family_get("age").get(args[2]->flags).family_key();
auto prop_weight = t.edge_property_family_get("weight")
.get(args[3]->flags)
.family_key();
auto n1 = t.vertex_find(args[0]->as<Int64>().value);
if (!option_fill(n1)) return t.commit(), false;
auto n2 = t.vertex_find(args[1]->as<Int64>().value);
if (!option_fill(n2)) return t.commit(), false;
auto r = t.edge_insert(n2.get(), n1.get());
r.property("age", args[2]);
r.property("weight", args[3]);
r.set(prop_age, args[2]);
r.set(prop_weight, args[3]);
auto &IS = t.type_find_or_create("IS");
r.edge_type(IS);
@ -173,13 +204,12 @@ auto load_queries(Db &db)
DbAccessor t(db);
auto &label = t.label_find_or_create("LABEL");
auto prop_key =
t.vertex_property_family_get("name").get(Type::String).family_key();
auto &index_record_collection = t.label_find_index(label);
auto accessor = index_record_collection.access();
cout << "VERTICES" << endl;
for (auto &v : accessor) {
cout << v.record->data.props.at("name").as<String>().value << endl;
}
iter::for_all(label.index->for_range_exact(t),
[&](auto a) { cout << a.at(prop_key) << endl; });
// TODO
// db.graph.vertices.fileter("LABEL").all(t, handler);

View File

@ -40,5 +40,4 @@ std::string code_line(const std::string &format_str, const Args &... args)
{
return "\t" + format(format_str, args...) + "\n";
}
}

View File

@ -1,13 +1,17 @@
#pragma once
#include <string>
#include "data_structures/concurrent/concurrent_map.hpp"
#include "mvcc/version_list.hpp"
#include "storage/common.hpp"
#include "storage/edge_accessor.hpp"
#include "storage/model/properties/property_family.hpp"
#include "utils/option.hpp"
class Edges
{
using prop_familys_t = ConcurrentMap<std::string, PropertyFamily *>;
public:
Option<const Edge::Accessor> find(DbTransaction &t, const Id &id);
@ -15,7 +19,15 @@ public:
Edge::Accessor insert(DbTransaction &t, VertexRecord *from,
VertexRecord *to);
// auto property_family_access();
PropertyFamily &property_family_find_or_create(const std::string &name);
private:
ConcurrentMap<uint64_t, EdgeRecord> edges;
// TODO: Because familys wont be removed this could be done with more
// efficent
// data structure.
prop_familys_t prop_familys;
AtomicCounter<uint64_t> counter;
};

View 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;
};

View File

@ -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;
// };

View 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;
};

View File

@ -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>;

View File

@ -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>;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -6,4 +6,3 @@
#include "storage/model/properties/int32.hpp"
#include "storage/model/properties/int64.hpp"
#include "storage/model/properties/string.hpp"

View 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
};

View File

@ -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;
}
}

View File

@ -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;
};

View File

@ -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;
};

View 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;
};

View File

@ -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;

View File

@ -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);
}

View File

@ -2,8 +2,10 @@
#include "database/db_transaction.hpp"
#include "mvcc/version_list.hpp"
#include "storage/indexes/index_record.hpp"
#include "storage/model/properties/properties.hpp"
#include "storage/model/properties/property.hpp"
#include "storage/model/properties/property_family.hpp"
#include "transactions/transaction.hpp"
template <class T, class Derived, class vlist_t = mvcc::VersionList<T>>
@ -57,30 +59,32 @@ public:
return vlist->remove(record, db.trans);
}
const Property &property(const std::string &key) const
{
return record->data.props.at(key);
}
const Property &at(prop_key_t &key) const { return properties().at(key); }
template <class V>
auto at(type_key_t<V> &key) const;
template <class V, class... Args>
void property(const std::string &key, Args &&... args)
void set(type_key_t<V> &key, Args &&... args)
{
record->data.props.template set<V>(key, std::forward<Args>(args)...);
properties().template set<V>(key, std::forward<Args>(args)...);
}
void property(const std::string &key, Property::sptr value)
void set(prop_key_t &key, Property::sptr value)
{
record->data.props.set(key, std::move(value));
properties().set(key, std::move(value));
}
void clear(prop_key_t &key) { properties().clear(key); }
template <class Handler>
void accept(Handler &handler) const
{
properties().template accept<Handler>(handler);
}
Properties &properties() const { return record->data.props; }
template <class V>
auto at(const std::string &key) const
{
return properties().at(key).template as<V>().value_ref();
}
explicit operator bool() const { return record != nullptr; }
T const *operator->() const { return record; }
@ -99,6 +103,12 @@ public:
}
protected:
template <class K>
IndexRecord<T, K> create_ir(K &&key)
{
return IndexRecord<T, K>(std::move(key), record, vlist);
}
T *record{nullptr};
vlist_t *const vlist;
DbTransaction &db;

View File

@ -10,13 +10,23 @@ class Vertex::Accessor : public RecordAccessor<Vertex, Vertex::Accessor>
public:
using RecordAccessor::RecordAccessor;
static Vertex::Accessor create(Vertex *t, mvcc::VersionList<Vertex> *vlist,
DbTransaction &db)
{
return Vertex::Accessor(t, vlist, db);
}
size_t out_degree() const;
size_t in_degree() const;
size_t degree() const;
void add_label(const Label &label);
// False if it's label with it already.
bool add_label(const Label &label);
// False if it doesn't have label.
bool remove_label(const Label &label);
bool has_label(const Label &label) const;

View File

@ -1,17 +1,24 @@
#pragma once
#include <memory>
#include <string>
#include "data_structures/concurrent/concurrent_map.hpp"
#include "database/db_transaction.hpp"
// #include "database/db_transaction.hpp"
#include "storage/common.hpp"
#include "storage/indexes/index.hpp"
#include "storage/indexes/index_record_collection.hpp"
// #include "storage/indexes/index.hpp"
// #include "storage/indexes/index_record_collection.hpp"
#include "storage/model/properties/property_family.hpp"
#include "storage/vertex_accessor.hpp"
#include "utils/option.hpp"
class DbTransaction;
class Vertices
{
public:
using vertices_t = ConcurrentMap<uint64_t, VertexRecord>;
using prop_familys_t =
ConcurrentMap<std::string, std::unique_ptr<PropertyFamily>>;
vertices_t::Accessor access();
@ -20,13 +27,15 @@ public:
// Creates new Vertex and returns filled Vertex::Accessor.
Vertex::Accessor insert(DbTransaction &t);
void update_label_index(const Label &label,
VertexIndexRecord &&index_record);
PropertyFamily &property_family_find_or_create(const std::string &name);
VertexIndexRecordCollection &find_label_index(const Label &label);
// prop_familys_t::Accessor property_family_access();
private:
vertices_t vertices;
Index<label_ref_t, VertexIndexRecordCollection> label_index;
// TODO: Because familys wont be removed this could be done with more
// efficent
// data structure.
prop_familys_t prop_familys;
AtomicCounter<uint64_t> counter;
};

51
include/utils/border.hpp Normal file
View 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>();
}

View File

@ -1,28 +1,35 @@
#pragma once
#include "utils/iterator/wrap.hpp"
#include "utils/iterator/range_iterator.hpp"
#include "utils/option.hpp"
namespace iter
{
// Class which turns ranged iterator with next() into accessor.
// T - type of return value
// I - iterator type
template <class T, class I>
class OneTimeAccessor
{
public:
OneTimeAccessor() : it(Option<Wrap<T, I>>()) {}
OneTimeAccessor(I &&it) : it(Wrap<T, I>(std::move(it))) {}
OneTimeAccessor() : it(Option<RangeIterator<T, I>>()) {}
OneTimeAccessor(I &&it) : it(RangeIterator<T, I>(std::move(it))) {}
Wrap<T, I> begin() { return it.take(); }
RangeIterator<T, I> begin() { return it.take(); }
Wrap<T, I> end() { return Wrap<T, I>(); }
RangeIterator<T, I> end() { return RangeIterator<T, I>(); }
private:
Option<Wrap<T, I>> it;
Option<RangeIterator<T, I>> it;
};
template <class I>
auto make_one_time_accessor(I &&iter)
{
// Because function isn't receving or in any way using type T from
// OneTimeAccessor compiler can't deduce it thats way there is decltype in
// construction of OneTimeAccessor. Resoulting type of iter.next().take() is
// T.
return OneTimeAccessor<decltype(iter.next().take()), I>(std::move(iter));
}
}

View File

@ -14,4 +14,14 @@ void for_all(I &&iter, C &&consumer)
e = iter.next();
}
}
template <class I, class C>
void for_all(std::unique_ptr<I> &&iter, C &&consumer)
{
auto e = iter->next();
while (e.is_present()) {
consumer(e.take());
e = iter->next();
}
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "utils/iterator/iterator_base.hpp"
namespace iter
{
// Wraps function into interator with next().
// T - type of return value
// F - type of wraped function
template <class T, class F>
class FunctionIterator : public IteratorBase<T>
{
public:
FunctionIterator(F &&f) : func(std::move(f)) {}
Option<T> next() final { return func(); }
private:
F func;
};
// Wraps function which returns options as an iterator.
template <class F>
auto make_iterator(F &&f)
{
// Because function isn't receving or in any way using type T from
// FunctionIterator compiler can't deduce it thats way there is decltype in
// construction of FunctionIterator. Resoulting type of iter.next().take()
// is T.
return FunctionIterator<decltype(f().take()), F>(std::move(f));
}
}

View File

@ -1,48 +0,0 @@
#pragma once
#include "utils/option.hpp"
namespace iter
{
template <class T, class I, class A>
class Iter
{
public:
Iter() = delete;
Iter(A &&acc) : begin(std::move(acc.begin())), acc(std::forward<A>(acc)) {}
// Iter(const Iter &other) = delete;
// Iter(Iter &&other) :
// begin(std::move(other.begin)),end(std::move(other.end)) {};
auto next()
{
if (begin != acc.end()) {
auto ret = Option<T>(&(*(begin.operator->())));
begin++;
return ret;
} else {
return Option<T>();
}
}
private:
I begin;
A acc;
};
// TODO: Join to make functions into one
template <class A>
auto make_iter(A &&acc)
{
return Iter<decltype(&(*(acc.begin().operator->()))), decltype(acc.begin()),
A>(std::move(acc));
}
template <class A>
auto make_iter_ref(A &acc)
{
return Iter<decltype(&(*(acc.begin().operator->()))), decltype(acc.begin()),
A &>(acc);
}
}

View File

@ -2,6 +2,8 @@
#include "utils/iterator/accessor.hpp"
#include "utils/iterator/for_all.hpp"
#include "utils/iterator/iter.hpp"
#include "utils/iterator/func_iterator.hpp"
#include "utils/iterator/iterator_accessor.hpp"
#include "utils/iterator/iterator_base.hpp"
#include "utils/iterator/map.hpp"
#include "utils/iterator/wrap.hpp"
#include "utils/iterator/range_iterator.hpp"

View 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);
}
}

View 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;
};

View File

@ -1,38 +1,47 @@
#pragma once
#include "utils/iterator/iterator_base.hpp"
#include "utils/option.hpp"
namespace iter
{
template <class U, class I, class MapOperator>
class Map
// Class which maps values returned by I iterator into value of type T with OP
// function.
// T - type of return value
// I - iterator type
// OP - type of mapper function
template <class T, class I, class OP>
class Map : public IteratorBase<T>
{
public:
Map() = delete;
template <class IT, class OP>
Map(IT &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op))
{
}
auto next()
// Map operation is designed to be used in chained calls which operate on a
// iterator. Map will in that usecase receive other iterator by value and
// std::move is a optimization for it.
Map(I &&iter, OP &&op) : iter(std::move(iter)), op(std::move(op)) {}
Option<T> next() final
{
auto item = iter.next();
if (item.is_present()) {
return Option<U>(op(item.take()));
return Option<T>(op(item.take()));
} else {
return Option<U>();
return Option<T>();
}
}
private:
I iter;
MapOperator op;
OP op;
};
template <class I, class OP>
auto make_map(I &&iter, OP &&op)
{
// Compiler cant deduce type T. decltype is here to help with it.
return Map<decltype(op(iter.next().take())), I, OP>(std::move(iter),
std::move(op));
}

View 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));
}
}

View File

@ -1,60 +0,0 @@
#pragma once
#include "utils/option.hpp"
namespace iter
{
template <class T, class I>
class Wrap
{
public:
Wrap() : iter(Option<I>()), value(Option<T>()){};
Wrap(I &&iter) : value(iter.next()), iter(Option<I>(std::move(iter))) {}
T &operator*()
{
assert(value.is_present());
return value.get();
}
T *operator->()
{
assert(value.is_present());
return &value.get();
}
operator T &()
{
assert(value.is_present());
return value.get();
}
Wrap &operator++()
{
assert(iter.is_present());
value = iter.get().next();
return (*this);
}
Wrap &operator++(int) { return operator++(); }
friend bool operator==(const Wrap &a, const Wrap &b)
{
return a.value.is_present() == b.value.is_present();
}
friend bool operator!=(const Wrap &a, const Wrap &b) { return !(a == b); }
private:
Option<I> iter;
Option<T> value;
};
template <class I>
auto make_wrap(I &&iter)
{
return Wrap<decltype(iter.next().take()), I>(std::move(iter));
}
}

View File

@ -1,5 +1,6 @@
#pragma once
#include <cassert>
#include <ext/aligned_buffer.h>
#include <utility>
@ -8,6 +9,12 @@ class Option
{
public:
Option() {}
//
// Option(T item)
// {
// new (data._M_addr()) T(std::forward(item));
// initialized = true;
// }
Option(T const &item)
{
@ -22,6 +29,9 @@ public:
}
Option(Option &other) = default;
// Containers from std which have strong exception guarantees wont use move
// constructors and operators wihtout noexcept. "Optimized C++,2016 , Kurt
// Guntheroth, page: 142, title: Moving instances into std::vector"
Option(Option &&other) noexcept
{
if (other.initialized) {
@ -36,7 +46,8 @@ public:
if (initialized) get().~T();
}
Option<T> &operator=(Option<T> &&other)
Option &operator=(Option &other) = default;
Option &operator=(Option &&other)
{
if (initialized) {
get().~T();
@ -60,7 +71,11 @@ public:
return *data._M_ptr();
}
const T &get() const noexcept { assert(initialized); }
const T &get() const noexcept
{
assert(initialized);
return *data._M_ptr();
}
T take()
{
@ -72,6 +87,9 @@ public:
explicit operator bool() const { return initialized; }
private:
// Aligned buffer is here to ensure aligment for data of type T. It isn't
// applicable to just put T field because the field has to be able to be
// uninitialized to fulfill the semantics of Option class.
__gnu_cxx::__aligned_buffer<T> data;
bool initialized = false;
};

10
include/utils/void.hpp Normal file
View 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; }
};

View File

@ -11,8 +11,15 @@
#include "data_structures/map/rh_hashmap.hpp"
#include "database/db.hpp"
#include "database/db_accessor.hpp"
#include "storage/edges.cpp"
#include "storage/edges.hpp"
#include "storage/indexes/impl/nonunique_unordered_index.cpp"
#include "storage/model/properties/properties.cpp"
#include "storage/record_accessor.cpp"
#include "storage/vertex_accessor.cpp"
#include "storage/vertex_accessor.hpp"
#include "storage/vertices.cpp"
#include "storage/vertices.hpp"
using namespace std;
typedef Vertex::Accessor VertexAccessor;
@ -23,13 +30,19 @@ class Node
{
public:
Node *parent = {nullptr};
type_key_t<Double> tkey;
double cost;
int depth = {0};
VertexAccessor vacc;
Node(VertexAccessor vacc, double cost) : cost(cost), vacc(vacc) {}
Node(VertexAccessor vacc, double cost, Node *parent)
: cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1)
Node(VertexAccessor vacc, double cost, type_key_t<Double> tkey)
: cost(cost), vacc(vacc), tkey(tkey)
{
}
Node(VertexAccessor vacc, double cost, Node *parent,
type_key_t<Double> tkey)
: cost(cost), vacc(vacc), parent(parent), depth(parent->depth + 1),
tkey(tkey)
{
}
@ -38,7 +51,7 @@ public:
auto now = this;
double sum = 0;
do {
sum += now->vacc.at<Double>("score");
sum += *(now->vacc.at(tkey).get());
now = now->parent;
} while (now != nullptr);
return sum;
@ -105,10 +118,11 @@ void found_result(Node *res)
}
}
double calc_heuristic_cost_dummy(Edge::Accessor &edge, Vertex::Accessor &vertex)
double calc_heuristic_cost_dummy(type_key_t<Double> tkey, Edge::Accessor &edge,
Vertex::Accessor &vertex)
{
assert(!vertex.empty());
return 1 - vertex.at<Double>("score");
return 1 - *vertex.at(tkey).get();
}
typedef bool (*EdgeFilter)(DbAccessor &t, Edge::Accessor &, Node *before);
@ -168,11 +182,16 @@ bool vertex_filter_contained(DbAccessor &t, Vertex::Accessor &v, Node *before)
// Filtri vracaju true ako element zadovoljava uvjete.
auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
VertexFilter v_filter[],
double (*calc_heuristic_cost)(Edge::Accessor &edge,
double (*calc_heuristic_cost)(type_key_t<Double> tkey,
Edge::Accessor &edge,
Vertex::Accessor &vertex),
int limit)
{
DbAccessor t(db);
type_key_t<Double> tkey = t.vertex_property_family_get("score")
.get(Type::Double)
.type_key<Double>();
auto best_found = new std::map<Id, Score>[max_depth];
std::vector<Node *> best;
@ -182,7 +201,7 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
auto start_vr = t.vertex_find(sys_id_start);
assert(start_vr);
start_vr.get().fill();
Node *start = new Node(start_vr.take(), 0);
Node *start = new Node(start_vr.take(), 0, tkey);
queue.push(start);
int count = 0;
do {
@ -213,8 +232,8 @@ auto a_star(Db &db, int64_t sys_id_start, uint max_depth, EdgeFilter e_filter[],
if (e_filter[now->depth](t, edge, now)) {
VertexAccessor va = edge.to();
if (v_filter[now->depth](t, va, now)) {
auto cost = calc_heuristic_cost(edge, va);
Node *n = new Node(va, now->cost + cost, now);
auto cost = calc_heuristic_cost(tkey, edge, va);
Node *n = new Node(va, now->cost + cost, now, tkey);
queue.push(n);
}
}
@ -346,6 +365,18 @@ int load_csv(Db &db, char *file_path, char *edge_file_path)
std::string line;
DbAccessor t(db);
auto key_id =
t.vertex_property_family_get("id").get(Type::Int32).family_key();
auto key_garment_id = t.vertex_property_family_get("garment_id")
.get(Type::Int32)
.family_key();
auto key_garment_category_id =
t.vertex_property_family_get("garment_category_id")
.get(Type::Int32)
.family_key();
auto key_score =
t.vertex_property_family_get("score").get(Type::Double).family_key();
int max_score = 1000000;
// VERTEX import
@ -356,14 +387,15 @@ int load_csv(Db &db, char *file_path, char *edge_file_path)
}
auto vertex_accessor = t.vertex_insert();
vertex_accessor.property("id", std::make_shared<Int32>(id));
vertex_accessor.property("garment_id", std::make_shared<Int32>(gar_id));
vertex_accessor.property("garment_category_id",
std::make_shared<Int32>(cat_id));
vertex_accessor.set(key_id, std::make_shared<Int32>(id));
vertex_accessor.set(key_garment_id, std::make_shared<Int32>(gar_id));
vertex_accessor.set(key_garment_category_id,
std::make_shared<Int32>(cat_id));
std::srand(id ^ 0x7482616);
vertex_accessor.property(
"score", std::make_shared<Double>((std::rand() % max_score) /
(max_score + 0.0)));
vertex_accessor.set(key_score,
std::make_shared<Double>((std::rand() % max_score) /
(max_score + 0.0)));
for (auto l_name : labels) {
auto &label = t.label_find_or_create(l_name);
vertex_accessor.add_label(label);
@ -372,6 +404,7 @@ int load_csv(Db &db, char *file_path, char *edge_file_path)
return vertex_accessor;
};
// Skip header
std::getline(file, line);
vector<Vertex::Accessor> va;
@ -423,39 +456,39 @@ int load_csv(Db &db, char *file_path, char *edge_file_path)
void load_graph_dummy(Db &db)
{
DbAccessor t(db);
auto v = [&](auto id, auto score) {
auto vertex_accessor = t.vertex_insert();
vertex_accessor.property("id", std::make_shared<Int32>(id));
vertex_accessor.property("score", std::make_shared<Double>(score));
return vertex_accessor.id();
};
Id va[] = {
v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8),
};
auto e = [&](auto from, auto type, auto to) {
auto v1 = t.vertex_find(va[from]);
auto v2 = t.vertex_find(va[to]);
auto edge_accessor = t.edge_insert(v1.get(), v2.get());
auto &edge_type = t.type_find_or_create(type);
edge_accessor.edge_type(edge_type);
};
e(0, "ok", 3);
e(0, "ok", 2);
e(0, "ok", 4);
e(1, "ok", 3);
e(2, "ok", 1);
e(2, "ok", 4);
e(3, "ok", 4);
e(3, "ok", 5);
e(4, "ok", 0);
e(4, "ok", 1);
e(5, "ok", 2);
// auto v = [&](auto id, auto score) {
// auto vertex_accessor = t.vertex_insert();
// vertex_accessor.property("id", std::make_shared<Int32>(id));
// vertex_accessor.property("score", std::make_shared<Double>(score));
// return vertex_accessor.id();
// };
//
// Id va[] = {
// v(0, 0.5), v(1, 1), v(2, 0.3), v(3, 0.15), v(4, 0.8), v(5, 0.8),
// };
//
// auto e = [&](auto from, auto type, auto to) {
// auto v1 = t.vertex_find(va[from]);
//
// auto v2 = t.vertex_find(va[to]);
//
// auto edge_accessor = t.edge_insert(v1.get(), v2.get());
//
// auto &edge_type = t.type_find_or_create(type);
// edge_accessor.edge_type(edge_type);
// };
//
// e(0, "ok", 3);
// e(0, "ok", 2);
// e(0, "ok", 4);
// e(1, "ok", 3);
// e(2, "ok", 1);
// e(2, "ok", 4);
// e(3, "ok", 4);
// e(3, "ok", 5);
// e(4, "ok", 0);
// e(4, "ok", 1);
// e(5, "ok", 2);
t.commit();
}

View File

@ -187,22 +187,26 @@ public:
RhBase() {}
RhBase(const RhBase &other)
{
capacity = other.capacity;
count = other.count;
if (capacity > 0) {
size_t bytes = sizeof(Combined) * capacity;
array = (Combined *)malloc(bytes);
memcpy(array, other.array, bytes);
RhBase(const RhBase &other) { copy_from(other); }
} else {
array = nullptr;
}
}
RhBase(RhBase &&other) { take_from(std::move(other)); }
~RhBase() { this->clear(); }
RhBase &operator=(const RhBase &other)
{
clear();
copy_from(other);
return *this;
}
RhBase &operator=(RhBase &&other)
{
clear();
take_from(std::move(other));
return *this;
}
Iterator begin() { return Iterator(this); }
ConstIterator begin() const { return ConstIterator(this); }
@ -216,6 +220,30 @@ public:
ConstIterator cend() const { return ConstIterator(); }
protected:
void copy_from(const RhBase &other)
{
capacity = other.capacity;
count = other.count;
if (capacity > 0) {
size_t bytes = sizeof(Combined) * capacity;
array = (Combined *)malloc(bytes);
memcpy(array, other.array, bytes);
} else {
array = nullptr;
}
}
void take_from(RhBase &&other)
{
capacity = other.capacity;
count = other.count;
array = other.array;
other.array = nullptr;
other.count = 0;
other.capacity = 0;
}
void init_array(size_t size)
{
size_t bytes = sizeof(Combined) * size;

View File

@ -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) {}

View File

@ -1,36 +1,46 @@
#include "database/db_accessor.hpp"
DbAccessor::DbAccessor(Db &db) : db(DbTransaction(db, db.tx_engine.begin())) {}
#include "database/db.hpp"
#include "utils/iterator/iterator.hpp"
DbAccessor::DbAccessor(Db &db)
: db_transaction(DbTransaction(db, db.tx_engine.begin()))
{
}
// VERTEX METHODS
auto DbAccessor::vertex_access()
{
return iter::make_map(
iter::make_iter(this->db.db.graph.vertices.access()),
[&](auto e) -> auto { return Vertex::Accessor(&(e->second), db); });
iter::make_iter(this->db_transaction.db.graph.vertices.access()),
[&](auto e) -> auto {
return Vertex::Accessor(&(e->second), db_transaction);
});
}
Option<const Vertex::Accessor> DbAccessor::vertex_find(const Id &id)
{
return this->db.db.graph.vertices.find(db, id);
return this->db_transaction.db.graph.vertices.find(db_transaction, id);
}
Vertex::Accessor DbAccessor::vertex_insert()
{
return this->db.db.graph.vertices.insert(db);
return this->db_transaction.db.graph.vertices.insert(db_transaction);
}
// EDGE METHODS
Option<const Edge::Accessor> DbAccessor::edge_find(const Id &id)
{
return db.db.graph.edges.find(db, id);
return db_transaction.db.graph.edges.find(db_transaction, id);
}
Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from,
Vertex::Accessor const &to)
{
auto edge_accessor = db.db.graph.edges.insert(db, from.vlist, to.vlist);
auto edge_accessor = db_transaction.db.graph.edges.insert(
db_transaction, from.vlist, to.vlist);
from.update()->data.out.add(edge_accessor.vlist);
to.update()->data.in.add(edge_accessor.vlist);
return edge_accessor;
@ -39,37 +49,57 @@ Edge::Accessor DbAccessor::edge_insert(Vertex::Accessor const &from,
// LABEL METHODS
const Label &DbAccessor::label_find_or_create(const std::string &name)
{
return db.db.graph.label_store.find_or_create(
return db_transaction.db.graph.label_store.find_or_create(
std::forward<const std::string &>(name));
}
bool DbAccessor::label_contains(const std::string &name)
{
return db.db.graph.label_store.contains(
return db_transaction.db.graph.label_store.contains(
std::forward<const std::string &>(name));
}
VertexIndexRecordCollection &DbAccessor::label_find_index(const Label &label)
{
return db.db.graph.vertices.find_label_index(label);
}
// TYPE METHODS
const EdgeType &DbAccessor::type_find_or_create(const std::string &name)
{
return db.db.graph.edge_type_store.find_or_create(
return db_transaction.db.graph.edge_type_store.find_or_create(
std::forward<const std::string &>(name));
}
bool DbAccessor::type_contains(const std::string &name)
{
return db.db.graph.edge_type_store.contains(
return db_transaction.db.graph.edge_type_store.contains(
std::forward<const std::string &>(name));
}
//********************PROPERTY METHODS
// Vertices::prop_familys_t::Accessor
// DbAccessor::vertex_property_family_access()
// {
// return db.db.graph.vertices.property_family_access();
// }
//
// auto DbAccessor::edge_property_family_access()
// {
// return db.db.graph.edges.property_family_access();
// }
auto edge_property_family_access();
PropertyFamily &DbAccessor::vertex_property_family_get(const std::string &name)
{
return db_transaction.db.graph.vertices.property_family_find_or_create(
name);
}
PropertyFamily &DbAccessor::edge_property_family_get(const std::string &name)
{
return db_transaction.db.graph.edges.property_family_find_or_create(name);
}
// TRANSACTION METHODS
void DbAccessor::commit() { db.trans.commit(); }
void DbAccessor::abort() { db.trans.abort(); }
void DbAccessor::commit() { db_transaction.trans.commit(); }
void DbAccessor::abort() { db_transaction.trans.abort(); }
// // EASE OF USE METHODS
// tx::Transaction &DbAccessor::operator*() { return db.trans; }

View File

@ -1,8 +1,2 @@
#include "database/db.hpp"
#include "database/db_transaction.hpp"
void DbTransaction::update_label_index(const Label &label,
VertexIndexRecord &&index_record)
{
db.graph.vertices.update_label_index(label, std::move(index_record));
}
// #include "database/db.hpp"
// #include "database/db_transaction.hpp"

View File

@ -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);

View File

@ -1,4 +1,6 @@
#include "storage/edges.hpp"
#include "storage/model/properties/property_family.hpp"
#include "utils/iterator/iterator.hpp"
Option<const Edge::Accessor> Edges::find(DbTransaction &t, const Id &id)
{
@ -30,3 +32,25 @@ Edge::Accessor Edges::insert(DbTransaction &t, VertexRecord *from,
return Edge::Accessor(edge, &inserted_edge_record->second, t);
}
// auto Edges::property_family_access()
// { // Returnig access directly would allow extern code to remove elements.
// Which
// // would be BAD, VERY BAD.
// return iter::make_iter(prop_familys.access());
// }
PropertyFamily &Edges::property_family_find_or_create(const std::string &name)
{
auto acc = prop_familys.access();
auto it = acc.find(name);
if (it == acc.end()) {
PropertyFamily *family = new PropertyFamily(name);
auto res = acc.insert(name, family);
if (!res.second) {
delete family;
}
it = res.first;
}
return *(it->second);
}

View 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>;

View File

@ -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; }

View File

@ -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;
}

View File

@ -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);
}

View 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);
}

View 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);
}

View File

@ -15,14 +15,20 @@ size_t Vertex::Accessor::in_degree() const
size_t Vertex::Accessor::degree() const { return in_degree() + out_degree(); }
void Vertex::Accessor::add_label(const Label &label)
bool Vertex::Accessor::add_label(const Label &label)
{
// update vertex
this->record->data.labels.add(label);
if (this->record->data.labels.add(label)) {
label.index->insert(create_ir(std::nullptr_t()));
return true;
}
return false;
}
// update index
this->db.update_label_index(label,
VertexIndexRecord(this->record, this->vlist));
bool Vertex::Accessor::remove_label(const Label &label)
{
// update vertex
return this->record->data.labels.remove(label);
}
bool Vertex::Accessor::has_label(const Label &label) const

View File

@ -1,4 +1,5 @@
#include "storage/vertices.hpp"
#include "utils/iterator/iterator.hpp"
Vertices::vertices_t::Accessor Vertices::access() { return vertices.access(); }
@ -33,13 +34,21 @@ Vertex::Accessor Vertices::insert(DbTransaction &t)
return Vertex::Accessor(vertex, &inserted_vertex_record->second, t);
}
void Vertices::update_label_index(const Label &label,
VertexIndexRecord &&index_record)
{
label_index.update(label, std::forward<VertexIndexRecord>(index_record));
}
//
// Vertices::prop_familys_t::Accessor Vertices::property_family_access()
// {
// return prop_familys.access();
// }
VertexIndexRecordCollection &Vertices::find_label_index(const Label &label)
PropertyFamily &
Vertices::property_family_find_or_create(const std::string &name)
{
return label_index.find(label);
auto acc = prop_familys.access();
auto it = acc.find(name);
if (it == acc.end()) {
auto family = std::unique_ptr<PropertyFamily>(new PropertyFamily(name));
auto res = acc.insert(name, std::move(family));
it = res.first;
}
return *(it->second);
}

View File

@ -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);

View File

@ -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;

View 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());
}