Properties refactoring: STABLE STATE. Memgraph compiles. Properties are out. Tests are in progress.
This commit is contained in:
parent
70a8b93b0b
commit
55f1912910
@ -316,6 +316,7 @@ set(memgraph_src_files
|
||||
${src_dir}/storage/typed_value.cpp
|
||||
${src_dir}/storage/locking/record_lock.cpp
|
||||
# ${src_dir}/storage/garbage/garbage.cpp
|
||||
${src_dir}/storage/record_accessor.cpp
|
||||
${src_dir}/storage/vertex_accessor.cpp
|
||||
${src_dir}/storage/edge_accessor.cpp
|
||||
# ${src_dir}/storage/record_accessor.cpp
|
||||
|
@ -7,11 +7,12 @@
|
||||
|
||||
#include "utils/exceptions/basic_exception.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* Thrown when something (Edge or a Vertex) can not
|
||||
* be created. Typically due to database overload.
|
||||
*/
|
||||
class CreationException : public BasicException {
|
||||
public:
|
||||
using BasicException::BasicException;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -21,6 +21,9 @@ class EdgeAccessor;
|
||||
|
||||
/**
|
||||
* Main class which represents Database concept in code.
|
||||
* This class is essentially a data structure. It exposes
|
||||
* all the data publicly, and should therefore not be directly
|
||||
* exposed to client functions. The GraphDbAccessor is used for that.
|
||||
*/
|
||||
class GraphDb {
|
||||
|
||||
|
@ -9,6 +9,14 @@
|
||||
#include "transactions/transaction.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* An accessor for the database object: exposes functions
|
||||
* for operating on the database. All the functions in
|
||||
* this class should be self-sufficient: for example the
|
||||
* function for creating
|
||||
* a new Vertex should take care of all the book-keeping around
|
||||
* the creation.
|
||||
*/
|
||||
class GraphDbAccessor {
|
||||
|
||||
public:
|
||||
@ -50,6 +58,13 @@ public:
|
||||
*/
|
||||
void detach_remove_vertex(VertexAccessor &vertex_accessor);
|
||||
|
||||
/**
|
||||
* Returns accessors to all the vertices in the graph.
|
||||
* TODO: switch to the Iterator library and map function.
|
||||
* @return
|
||||
*/
|
||||
std::vector<VertexAccessor> vertices();
|
||||
|
||||
/**
|
||||
* Creates a new Edge and returns an accessor to it.
|
||||
*
|
||||
@ -67,6 +82,13 @@ public:
|
||||
*/
|
||||
void remove_edge(EdgeAccessor& edge_accessor);
|
||||
|
||||
/**
|
||||
* Returns accessors to all the edges in the graph.
|
||||
* TODO: switch to the Iterator library and map function.
|
||||
* @return
|
||||
*/
|
||||
std::vector<EdgeAccessor> edges();
|
||||
|
||||
/**
|
||||
* Obtains the Label for the label's name.
|
||||
* @return See above.
|
||||
|
@ -11,6 +11,7 @@ namespace mvcc {
|
||||
|
||||
template<class T>
|
||||
class VersionList {
|
||||
// TODO what is this Accessor? Dead code?
|
||||
friend class Accessor;
|
||||
|
||||
public:
|
||||
|
@ -6,18 +6,42 @@
|
||||
#include "utils/reference_wrapper.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
|
||||
// forward declaring the VertexAccessor because it's returned
|
||||
// by some functions
|
||||
class VertexAccessor;
|
||||
|
||||
class EdgeAccessor : public RecordAccessor<Edge, EdgeAccessor> {
|
||||
/**
|
||||
* Provides ways for the client programmer (i.e. code generated
|
||||
* by the compiler) to interact with an Edge.
|
||||
*
|
||||
* This class indirectly inherits MVCC data structures and
|
||||
* takes care of MVCC versioning.
|
||||
*/
|
||||
class EdgeAccessor : public RecordAccessor<Edge> {
|
||||
public:
|
||||
using RecordAccessor::RecordAccessor;
|
||||
|
||||
/**
|
||||
* Sets a new edge type.
|
||||
* @param edge_type The new type.
|
||||
*/
|
||||
void set_edge_type(GraphDb::EdgeType edge_type);
|
||||
|
||||
/**
|
||||
* Returns the edge type.
|
||||
* @return
|
||||
*/
|
||||
GraphDb::EdgeType edge_type() const;
|
||||
|
||||
/**
|
||||
* Returns an accessor to the originating Vertex of this edge.
|
||||
* @return
|
||||
*/
|
||||
VertexAccessor from() const;
|
||||
|
||||
/**
|
||||
* Returns an accessor to the destination Vertex of this edge.
|
||||
*/
|
||||
VertexAccessor to() const;
|
||||
|
||||
// void remove();
|
||||
|
@ -6,7 +6,18 @@
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
#include "utils/pass_key.hpp"
|
||||
|
||||
template<typename TRecord, typename TDerived>
|
||||
#include "storage/typed_value_store.hpp"
|
||||
|
||||
/**
|
||||
* An accessor to a database record (an Edge or a Vertex).
|
||||
*
|
||||
* Exposes view and update functions to the client programmer.
|
||||
* Assumes responsibility of doing all the relevant book-keeping
|
||||
* (such as index updates etc).
|
||||
*
|
||||
* @tparam TRecord Type of record (MVCC Version) of the accessor.
|
||||
*/
|
||||
template<typename TRecord>
|
||||
class RecordAccessor {
|
||||
|
||||
public:
|
||||
@ -21,47 +32,64 @@ public:
|
||||
*/
|
||||
friend GraphDbAccessor;
|
||||
|
||||
RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(vlist_.find(db_accessor.transaction_)), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
/**
|
||||
* @param vlist MVCC record that this accessor wraps.
|
||||
* @param db_accessor The DB accessor that "owns" this record accessor.
|
||||
*/
|
||||
RecordAccessor(mvcc::VersionList<TRecord>& vlist, GraphDbAccessor& db_accessor);
|
||||
|
||||
/**
|
||||
* @param vlist MVCC record that this accessor wraps.
|
||||
* @param record MVCC version (that is viewable from this db_accessor.transaction)
|
||||
* of the given record. Slightly more optimal then the constructor that does not
|
||||
* accept an already found record.
|
||||
* @param db_accessor The DB accessor that "owns" this record accessor.
|
||||
*/
|
||||
RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
TRecord& record,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(&record), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
GraphDbAccessor& db_accessor);
|
||||
|
||||
/**
|
||||
* Gets the property for the given key.
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
const TypedValue &PropsAt(GraphDb::Property key) const;
|
||||
|
||||
/**
|
||||
* Sets a value on the record for the given property.
|
||||
*
|
||||
* @tparam TValue Type of the value being set.
|
||||
* @param key Property key.
|
||||
* @param value The value to set.
|
||||
*/
|
||||
template<typename TValue>
|
||||
void PropsSet(GraphDb::Property key, TValue value) {
|
||||
update().props_.set(key, value);
|
||||
update().properties_.set(key, value);
|
||||
}
|
||||
|
||||
size_t PropsErase(GraphDb::Property key) {
|
||||
return update().props_.erase(key);
|
||||
}
|
||||
/**
|
||||
* Erases the property for the given key.
|
||||
*
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
size_t PropsErase(GraphDb::Property key);
|
||||
|
||||
const TypedValueStore<GraphDb::Property> &Properties() const {
|
||||
return view().properties_;
|
||||
}
|
||||
const TypedValueStore<GraphDb::Property> &Properties() const;
|
||||
|
||||
void PropertiesAccept(std::function<void(const GraphDb::Property key, const TypedValue &prop)> handler,
|
||||
std::function<void()> finish = {}) const {
|
||||
view().props_.Accept(handler, finish);
|
||||
}
|
||||
std::function<void()> finish = {}) const;
|
||||
|
||||
// Assumes same transaction
|
||||
friend bool operator==(const RecordAccessor &a, const RecordAccessor &b) {
|
||||
// TODO consider the legitimacy of this comparison
|
||||
return a.vlist_ == b.vlist_;
|
||||
return &a.vlist_ == &b.vlist_;
|
||||
}
|
||||
|
||||
// Assumes same transaction
|
||||
friend bool operator!=(const RecordAccessor &a, const RecordAccessor &b) {
|
||||
// TODO consider the legitimacy of this comparison
|
||||
return !(a == b);
|
||||
return a != b;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,18 +97,14 @@ public:
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
GraphDbAccessor& db_accessor() {
|
||||
return db_accessor_;
|
||||
}
|
||||
GraphDbAccessor& db_accessor();
|
||||
|
||||
/**
|
||||
* Returns a const GraphDB accessor of this record accessor.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const GraphDbAccessor& db_accessor() const {
|
||||
return db_accessor_;
|
||||
}
|
||||
* Returns a GraphDB accessor of this record accessor.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const GraphDbAccessor& db_accessor() const;
|
||||
|
||||
protected:
|
||||
|
||||
@ -89,27 +113,14 @@ protected:
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
TRecord& update() {
|
||||
// TODO consider renaming this to something more indicative
|
||||
// of the underlying MVCC functionality (like "new_version" or so)
|
||||
if (!record_->is_visible_write(db_accessor_.transaction_))
|
||||
record_ = vlist_.update(record_, db_accessor_.transaction_);
|
||||
|
||||
return *record_;
|
||||
}
|
||||
TRecord& update();
|
||||
|
||||
/**
|
||||
* Returns a version of the record that is only for viewing.
|
||||
*
|
||||
* @return See above.
|
||||
*/
|
||||
const TRecord& view() const {
|
||||
return *record_;
|
||||
}
|
||||
|
||||
// The record (edge or vertex) this accessor provides access to.
|
||||
// Immutable, set in the constructor and never changed.
|
||||
mvcc::VersionList<TRecord>& vlist_;
|
||||
const TRecord& view() const;
|
||||
|
||||
// The database accessor for which this record accessor is created
|
||||
// Provides means of getting to the transaction and database functions.
|
||||
@ -117,6 +128,10 @@ protected:
|
||||
GraphDbAccessor& db_accessor_;
|
||||
|
||||
private:
|
||||
// The record (edge or vertex) this accessor provides access to.
|
||||
// Immutable, set in the constructor and never changed.
|
||||
mvcc::VersionList<TRecord>& vlist_;
|
||||
|
||||
/* The version of the record currently used in this transaction. Defaults to the
|
||||
* latest viewable version (set in the constructor). After the first update done
|
||||
* through this accessor a new, editable version, is created for this transaction,
|
||||
|
@ -128,7 +128,6 @@ public:
|
||||
|
||||
if (finish)
|
||||
finish();
|
||||
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -8,30 +8,70 @@
|
||||
#include "utils/iterator/iterator.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
|
||||
// forward declaring the EdgeAccessor because it's returned
|
||||
// by some functions
|
||||
class EdgeAccessor;
|
||||
|
||||
class VertexAccessor : public RecordAccessor<Vertex, VertexAccessor> {
|
||||
/**
|
||||
* Provides ways for the client programmer (i.e. code generated
|
||||
* by the compiler) to interact with a Vertex.
|
||||
*
|
||||
* This class indirectly inherits MVCC data structures and
|
||||
* takes care of MVCC versioning.
|
||||
*/
|
||||
class VertexAccessor : public RecordAccessor<Vertex> {
|
||||
public:
|
||||
using RecordAccessor::RecordAccessor;
|
||||
|
||||
/**
|
||||
* Returns the number of outgoing edges.
|
||||
* @return
|
||||
*/
|
||||
size_t out_degree() const;
|
||||
|
||||
/**
|
||||
* Returns the number of incoming edges.
|
||||
* @return
|
||||
*/
|
||||
size_t in_degree() const;
|
||||
|
||||
/**
|
||||
* Adds a label to the Vertex. If the Vertex already
|
||||
* has that label the call has no effect.
|
||||
* @param label A label.
|
||||
* @return If or not a new Label was set on this Vertex.
|
||||
*/
|
||||
bool add_label(GraphDb::Label label);
|
||||
|
||||
/**
|
||||
* Removes a label from the Vertex.
|
||||
* @param label The label to remove.
|
||||
* @return The number of removed labels (can be 0 or 1).
|
||||
*/
|
||||
size_t remove_label(GraphDb::Label label);
|
||||
|
||||
/**
|
||||
* Indicates if the Vertex has the given label.
|
||||
* @param label A label.
|
||||
* @return
|
||||
*/
|
||||
bool has_label(GraphDb::Label label) const;
|
||||
|
||||
/**
|
||||
* Returns all the Labels of the Vertex.
|
||||
* @return
|
||||
*/
|
||||
const std::set<GraphDb::Label>& labels() const;
|
||||
|
||||
/**
|
||||
* Returns EdgeAccessors for all incoming edges.
|
||||
* @return
|
||||
*/
|
||||
std::vector<EdgeAccessor> in();
|
||||
|
||||
/**
|
||||
* Returns EdgeAccessors for all outgoing edges.
|
||||
* @return
|
||||
*/
|
||||
std::vector<EdgeAccessor> out();
|
||||
|
||||
// returns if remove was possible due to connections
|
||||
// bool remove();
|
||||
//
|
||||
// void detach_remove();
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include <database/creation_exception.hpp>
|
||||
#include "database/creation_exception.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
#include "storage/vertex.hpp"
|
||||
@ -20,7 +20,7 @@ VertexAccessor GraphDbAccessor::insert_vertex() {
|
||||
Vertex *vertex = vertex_vlist->insert(transaction_);
|
||||
|
||||
// insert the newly created record into the main storage
|
||||
// TODO make the number of tries configurable configurable
|
||||
// TODO make the number of tries configurable
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
bool success = db_.vertices_.access().insert(vertex_vlist).second;
|
||||
if (success)
|
||||
@ -37,9 +37,6 @@ bool GraphDbAccessor::remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
return false;
|
||||
|
||||
vertex_accessor.vlist_.remove(&vertex_accessor.update(), transaction_);
|
||||
|
||||
// TODO remove the vertex from the main storage once it gets garbage collected
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -55,8 +52,22 @@ void GraphDbAccessor::detach_remove_vertex(VertexAccessor &vertex_accessor) {
|
||||
|
||||
// mvcc removal of the vertex
|
||||
vertex_accessor.vlist_.remove(&vertex_accessor.update(), transaction_);
|
||||
}
|
||||
|
||||
// TODO remove the vertex from the main storage once it gets garbage collected
|
||||
std::vector<VertexAccessor> GraphDbAccessor::vertices() {
|
||||
auto sl_accessor = db_.vertices_.access();
|
||||
|
||||
std::vector<VertexAccessor> accessors;
|
||||
accessors.reserve(sl_accessor.size());
|
||||
|
||||
for (auto vlist : sl_accessor){
|
||||
auto record = vlist->find(transaction_);
|
||||
if (record == nullptr)
|
||||
continue;
|
||||
accessors.emplace_back(*vlist, *record, *this);
|
||||
}
|
||||
|
||||
return accessors;
|
||||
}
|
||||
|
||||
EdgeAccessor GraphDbAccessor::insert_edge(
|
||||
@ -84,23 +95,39 @@ EdgeAccessor GraphDbAccessor::insert_edge(
|
||||
throw CreationException("Unable to create an Edge after 5 attempts");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given edge pointer from a vector of pointers.
|
||||
* Does NOT maintain edge pointer ordering (for efficiency).
|
||||
*/
|
||||
void swap_out_edge(std::vector<mvcc::VersionList<Edge>*> &edges, mvcc::VersionList<Edge> *edge) {
|
||||
auto found = std::find(edges.begin(), edges.end(), edge);
|
||||
assert(found != edges.end());
|
||||
std::swap(*found, edges.back());
|
||||
edges.pop_back();
|
||||
}
|
||||
|
||||
void GraphDbAccessor::remove_edge(EdgeAccessor& edge_accessor) {
|
||||
// remove this edge's reference from the "from" vertex
|
||||
auto& vertex_from = edge_accessor.from().update();
|
||||
std::remove(vertex_from.out_.begin(),
|
||||
vertex_from.out_.end(),
|
||||
&edge_accessor.vlist_);
|
||||
|
||||
// remove this edge's reference from the "to" vertex
|
||||
auto& vertex_to = edge_accessor.to().update();
|
||||
std::remove(vertex_to.in_.begin(),
|
||||
vertex_to.in_.end(),
|
||||
&edge_accessor.vlist_);
|
||||
|
||||
// remove this record from the database via MVCC
|
||||
swap_out_edge(edge_accessor.from().update().out_, &edge_accessor.vlist_);
|
||||
swap_out_edge(edge_accessor.to().update().in_, &edge_accessor.vlist_);
|
||||
edge_accessor.vlist_.remove(&edge_accessor.update(), transaction_);
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> GraphDbAccessor::edges() {
|
||||
auto sl_accessor = db_.edges_.access();
|
||||
|
||||
std::vector<EdgeAccessor> accessors;
|
||||
accessors.reserve(sl_accessor.size());
|
||||
|
||||
for (auto vlist : sl_accessor){
|
||||
auto record = vlist->find(transaction_);
|
||||
if (record == nullptr)
|
||||
continue;
|
||||
accessors.emplace_back(*vlist, *record, *this);
|
||||
}
|
||||
|
||||
return accessors;
|
||||
}
|
||||
|
||||
GraphDb::Label GraphDbAccessor::label(const std::string& label_name) {
|
||||
return &(*db_.labels_.access().insert(label_name).first);
|
||||
}
|
||||
|
@ -2,11 +2,11 @@
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
void EdgeAccessor::set_edge_type(GraphDb::EdgeType edge_type) {
|
||||
this->update().edge_type_ = edge_type;
|
||||
update().edge_type_ = edge_type;
|
||||
}
|
||||
|
||||
GraphDb::EdgeType EdgeAccessor::edge_type() const {
|
||||
return this->view().edge_type_;
|
||||
return view().edge_type_;
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::from() const {
|
||||
@ -14,23 +14,5 @@ VertexAccessor EdgeAccessor::from() const {
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::to() const {
|
||||
return VertexAccessor(view().to_, db_accessor_);
|
||||
return VertexAccessor(view().to_, db_accessor_);
|
||||
}
|
||||
|
||||
//void EdgeAccessor::remove() {
|
||||
// // remove this edge's reference from the "from" vertex
|
||||
// auto& vertex_from = from().update();
|
||||
// std::remove(vertex_from.out_.begin(),
|
||||
// vertex_from.out_.end(),
|
||||
// vlist_);
|
||||
//
|
||||
// // remove this edge's reference from the "to" vertex
|
||||
// auto& vertex_to = to().update();
|
||||
// std::remove(vertex_to.in_.begin(),
|
||||
// vertex_to.in_.end(),
|
||||
// vlist_);
|
||||
//
|
||||
// // remove this record from the database via MVCC
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
//}
|
||||
|
||||
|
69
src/storage/record_accessor.cpp
Normal file
69
src/storage/record_accessor.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "storage/record_accessor.hpp"
|
||||
#include "storage/edge.hpp"
|
||||
#include "storage/vertex.hpp"
|
||||
|
||||
template<typename TRecord>
|
||||
RecordAccessor<TRecord>::RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(vlist_.find(db_accessor.transaction_)), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
RecordAccessor<TRecord>::RecordAccessor(mvcc::VersionList<TRecord>& vlist,
|
||||
TRecord& record,
|
||||
GraphDbAccessor& db_accessor)
|
||||
: vlist_(vlist), record_(&record), db_accessor_(db_accessor) {
|
||||
assert(record_ != nullptr);
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
const TypedValue &RecordAccessor<TRecord>::PropsAt(GraphDb::Property key) const {
|
||||
return view().properties_.at(key);
|
||||
}
|
||||
|
||||
|
||||
template<typename TRecord>
|
||||
size_t RecordAccessor<TRecord>::PropsErase(GraphDb::Property key) {
|
||||
return update().properties_.erase(key);
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
const TypedValueStore<GraphDb::Property> &RecordAccessor<TRecord>::Properties() const {
|
||||
return view().properties_;
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
void RecordAccessor<TRecord>::PropertiesAccept(
|
||||
std::function<void(const GraphDb::Property key, const TypedValue &prop)> handler,
|
||||
std::function<void()> finish) const {
|
||||
view().properties_.Accept(handler, finish);
|
||||
}
|
||||
|
||||
|
||||
template<typename TRecord>
|
||||
GraphDbAccessor &RecordAccessor<TRecord>::db_accessor() {
|
||||
return db_accessor_;
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
const GraphDbAccessor &RecordAccessor<TRecord>::db_accessor() const {
|
||||
return db_accessor_;
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
TRecord &RecordAccessor<TRecord>::update() {
|
||||
if (!record_->is_visible_write(db_accessor_.transaction_))
|
||||
record_ = vlist_.update(db_accessor_.transaction_);
|
||||
|
||||
return *record_;
|
||||
}
|
||||
|
||||
template<typename TRecord>
|
||||
const TRecord &RecordAccessor<TRecord>::view() const {
|
||||
return *record_;
|
||||
}
|
||||
|
||||
template class RecordAccessor<Vertex>;
|
||||
template class RecordAccessor<Edge>;
|
||||
|
@ -3,19 +3,19 @@
|
||||
#include "storage/util.hpp"
|
||||
|
||||
size_t VertexAccessor::out_degree() const {
|
||||
return this->view().out_.size();
|
||||
return view().out_.size();
|
||||
}
|
||||
|
||||
size_t VertexAccessor::in_degree() const {
|
||||
return this->view().in_.size();
|
||||
return view().in_.size();
|
||||
}
|
||||
|
||||
bool VertexAccessor::add_label(GraphDb::Label label) {
|
||||
return this->update().labels_.emplace(label).second;
|
||||
return update().labels_.emplace(label).second;
|
||||
}
|
||||
|
||||
size_t VertexAccessor::remove_label(GraphDb::Label label) {
|
||||
return this->update().labels_.erase(label);
|
||||
return update().labels_.erase(label);
|
||||
}
|
||||
|
||||
bool VertexAccessor::has_label(GraphDb::Label label) const {
|
||||
@ -28,38 +28,9 @@ const std::set<GraphDb::Label>& VertexAccessor::labels() const {
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> VertexAccessor::in() {
|
||||
const Vertex& record = view();
|
||||
std::vector<EdgeAccessor> in;
|
||||
in.reserve(record.in_.size());
|
||||
for (auto edge_vlist_ptr : record.in_)
|
||||
in.emplace_back(EdgeAccessor(*edge_vlist_ptr, db_accessor_));
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
std::vector<EdgeAccessor> VertexAccessor::out() {
|
||||
return make_accessors<EdgeAccessor>(view().in_, db_accessor_);
|
||||
}
|
||||
|
||||
|
||||
//bool VertexAccessor::remove() {
|
||||
// // TODO consider if this works well with MVCC
|
||||
// if (out_degree() > 0 || in_degree() > 0)
|
||||
// return false;
|
||||
//
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
// return true;
|
||||
//}
|
||||
//
|
||||
//void VertexAccessor::detach_remove() {
|
||||
// // removing edges via accessors is both safe
|
||||
// // and it should remove all the pointers in the relevant
|
||||
// // vertices (including this one)
|
||||
// for (auto edge_vlist : view().out_)
|
||||
// EdgeAccessor(*edge_vlist, db_accessor_).remove();
|
||||
//
|
||||
// for (auto edge_vlist : view().in_)
|
||||
// EdgeAccessor(*edge_vlist, db_accessor_).remove();
|
||||
//
|
||||
// vlist_.remove(&update(), db_accessor_.transaction_);
|
||||
//}
|
||||
std::vector<EdgeAccessor> VertexAccessor::out() {
|
||||
return make_accessors<EdgeAccessor>(view().out_, db_accessor_);
|
||||
}
|
||||
|
266
tests/unit/graph_db_accessor.cpp
Normal file
266
tests/unit/graph_db_accessor.cpp
Normal file
@ -0,0 +1,266 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "dbms/dbms.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
|
||||
|
||||
size_t CountVertices(GraphDbAccessor &db_accessor) {
|
||||
size_t r_val = 0;
|
||||
for (auto va : db_accessor.vertices())
|
||||
r_val++;
|
||||
|
||||
return r_val;
|
||||
}
|
||||
|
||||
size_t CountEdges(GraphDbAccessor &db_accessor) {
|
||||
size_t r_val = 0;
|
||||
for (auto va : db_accessor.edges())
|
||||
r_val++;
|
||||
|
||||
return r_val;
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, DbmsCreateDefault) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor accessor = dbms.active();
|
||||
EXPECT_EQ(accessor.name(), "default");
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, InsertVertex) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor accessor = dbms.active();
|
||||
|
||||
EXPECT_EQ(CountVertices(accessor), 0);
|
||||
|
||||
accessor.insert_vertex();
|
||||
EXPECT_EQ(CountVertices(accessor), 1);
|
||||
|
||||
accessor.insert_vertex();
|
||||
EXPECT_EQ(CountVertices(accessor), 2);
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, RemoveVertexSameTransaction) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor accessor = dbms.active();
|
||||
|
||||
EXPECT_EQ(CountVertices(accessor), 0);
|
||||
|
||||
auto va1 = accessor.insert_vertex();
|
||||
EXPECT_EQ(CountVertices(accessor), 1);
|
||||
|
||||
EXPECT_TRUE(accessor.remove_vertex(va1));
|
||||
EXPECT_EQ(CountVertices(accessor), 0);
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, RemoveVertexDifferentTransaction) {
|
||||
Dbms dbms;
|
||||
|
||||
// first transaction creates a vertex
|
||||
GraphDbAccessor accessor1 = dbms.active();
|
||||
accessor1.insert_vertex();
|
||||
accessor1.transaction_.commit();
|
||||
|
||||
// second transaction checks that it sees it, and deletes it
|
||||
GraphDbAccessor accessor2 = dbms.active();
|
||||
EXPECT_EQ(CountVertices(accessor2), 1);
|
||||
for (auto vertex_accessor : accessor2.vertices())
|
||||
accessor2.remove_vertex(vertex_accessor);
|
||||
accessor2.transaction_.commit();
|
||||
|
||||
// third transaction checks that it does not see the vertex
|
||||
GraphDbAccessor accessor3 = dbms.active();
|
||||
EXPECT_EQ(CountVertices(accessor3), 0);
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, InsertEdge) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba = dbms.active();
|
||||
|
||||
auto va1 = dba.insert_vertex();
|
||||
auto va2 = dba.insert_vertex();
|
||||
EXPECT_EQ(va1.in().size(), 0);
|
||||
EXPECT_EQ(va1.out().size(), 0);
|
||||
EXPECT_EQ(va2.in().size(), 0);
|
||||
EXPECT_EQ(va2.out().size(), 0);
|
||||
|
||||
// setup (v1) - [:likes] -> (v2)
|
||||
dba.insert_edge(va1, va2, dba.edge_type("likes"));
|
||||
EXPECT_EQ(CountEdges(dba), 1);
|
||||
EXPECT_EQ(va1.out()[0].to(), va2);
|
||||
EXPECT_EQ(va2.in()[0].from(), va1);
|
||||
EXPECT_EQ(va1.in().size(), 0);
|
||||
EXPECT_EQ(va1.out().size(), 1);
|
||||
EXPECT_EQ(va2.in().size(), 1);
|
||||
EXPECT_EQ(va2.out().size(), 0);
|
||||
|
||||
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
|
||||
auto va3 = dba.insert_vertex();
|
||||
dba.insert_edge(va3, va2, dba.edge_type("hates"));
|
||||
EXPECT_EQ(CountEdges(dba), 2);
|
||||
EXPECT_EQ(va3.out()[0].to(), va2);
|
||||
EXPECT_EQ(va1.in().size(), 0);
|
||||
EXPECT_EQ(va1.out().size(), 1);
|
||||
EXPECT_EQ(va2.in().size(), 2);
|
||||
EXPECT_EQ(va2.out().size(), 0);
|
||||
EXPECT_EQ(va3.in().size(), 0);
|
||||
EXPECT_EQ(va3.out().size(), 1);
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, RemoveEdge) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba1 = dbms.active();
|
||||
|
||||
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
|
||||
auto va1 = dba1.insert_vertex();
|
||||
auto va2 = dba1.insert_vertex();
|
||||
auto va3 = dba1.insert_vertex();
|
||||
dba1.insert_edge(va1, va2, dba1.edge_type("likes"));
|
||||
dba1.insert_edge(va3, va2, dba1.edge_type("hates"));
|
||||
EXPECT_EQ(CountEdges(dba1), 2);
|
||||
|
||||
// remove all [:hates] edges
|
||||
dba1.transaction_.commit();
|
||||
GraphDbAccessor dba2 = dbms.active();
|
||||
EXPECT_EQ(CountEdges(dba2), 2);
|
||||
for (auto edge : dba2.edges())
|
||||
if (edge.edge_type() == dba2.edge_type("hates"))
|
||||
dba2.remove_edge(edge);
|
||||
|
||||
// current state: (v1) - [:likes] -> (v2), (v3)
|
||||
dba2.transaction_.commit();
|
||||
GraphDbAccessor dba3 = dbms.active();
|
||||
EXPECT_EQ(CountEdges(dba3), 1);
|
||||
EXPECT_EQ(CountVertices(dba3), 3);
|
||||
for (auto edge : dba3.edges()) {
|
||||
EXPECT_EQ(edge.edge_type(), dba3.edge_type("likes"));
|
||||
auto v1 = edge.from();
|
||||
auto v2 = edge.to();
|
||||
|
||||
// ensure correct connectivity for all the vertices
|
||||
for (auto vertex : dba3.vertices()) {
|
||||
if (vertex == v1) {
|
||||
EXPECT_EQ(vertex.in().size(), 0);
|
||||
EXPECT_EQ(vertex.out().size(), 1);
|
||||
} else if (vertex == v2) {
|
||||
EXPECT_EQ(vertex.in().size(), 1);
|
||||
EXPECT_EQ(vertex.out().size(), 0);
|
||||
} else {
|
||||
EXPECT_EQ(vertex.in().size(), 0);
|
||||
EXPECT_EQ(vertex.out().size(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, DetachRemoveVertex) {
|
||||
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba1 = dbms.active();
|
||||
|
||||
// setup (v1) - [:likes] -> (v2) <- [:hates] - (v3)
|
||||
auto va1 = dba1.insert_vertex();
|
||||
auto va2 = dba1.insert_vertex();
|
||||
auto va3 = dba1.insert_vertex();
|
||||
dba1.insert_edge(va1, va2, dba1.edge_type("likes"));
|
||||
dba1.insert_edge(va1, va3, dba1.edge_type("likes"));
|
||||
|
||||
// ensure that plain remove does NOT work
|
||||
EXPECT_EQ(CountVertices(dba1), 3);
|
||||
EXPECT_EQ(CountEdges(dba1), 2);
|
||||
EXPECT_FALSE(dba1.remove_vertex(va1));
|
||||
EXPECT_FALSE(dba1.remove_vertex(va2));
|
||||
EXPECT_FALSE(dba1.remove_vertex(va3));
|
||||
EXPECT_EQ(CountVertices(dba1), 3);
|
||||
EXPECT_EQ(CountEdges(dba1), 2);
|
||||
|
||||
// make a new transaction because at the moment deletions
|
||||
// in the same transaction are not visible
|
||||
// DETACH REMOVE V3
|
||||
// new situation: (v1) - [:likes] -> (v2)
|
||||
dba1.detach_remove_vertex(va3);
|
||||
dba1.transaction_.commit();
|
||||
GraphDbAccessor dba2 = dbms.active();
|
||||
|
||||
EXPECT_EQ(CountVertices(dba2), 2);
|
||||
EXPECT_EQ(CountEdges(dba2), 1);
|
||||
for (auto va : dba2.vertices())
|
||||
EXPECT_FALSE(dba2.remove_vertex(va));
|
||||
|
||||
dba2.transaction_.commit();
|
||||
GraphDbAccessor dba3 = dbms.active();
|
||||
EXPECT_EQ(CountVertices(dba3), 2);
|
||||
EXPECT_EQ(CountEdges(dba3), 1);
|
||||
|
||||
for (auto va : dba3.vertices()) {
|
||||
EXPECT_FALSE(dba3.remove_vertex(va));
|
||||
dba3.detach_remove_vertex(va);
|
||||
break;
|
||||
}
|
||||
|
||||
dba3.transaction_.commit();
|
||||
GraphDbAccessor dba4 = dbms.active();
|
||||
EXPECT_EQ(CountVertices(dba4), 1);
|
||||
EXPECT_EQ(CountEdges(dba4), 0);
|
||||
|
||||
// remove the last vertex, it has no connections
|
||||
// so that should work
|
||||
for (auto va : dba4.vertices())
|
||||
EXPECT_TRUE(dba4.remove_vertex(va));
|
||||
|
||||
dba4.transaction_.commit();
|
||||
GraphDbAccessor dba5 = dbms.active();
|
||||
EXPECT_EQ(CountVertices(dba5), 0);
|
||||
EXPECT_EQ(CountEdges(dba5), 0);
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, Labels) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba1 = dbms.active();
|
||||
|
||||
GraphDb::Label label_friend = dba1.label("friend");
|
||||
EXPECT_EQ(label_friend, dba1.label("friend"));
|
||||
EXPECT_NE(label_friend, dba1.label("friend2"));
|
||||
EXPECT_EQ(dba1.label_name(label_friend), "friend");
|
||||
|
||||
// test that getting labels through a different accessor works
|
||||
EXPECT_EQ(label_friend, dbms.active().label("friend"));
|
||||
EXPECT_NE(label_friend, dbms.active().label("friend2"));
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, EdgeTypes) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba1 = dbms.active();
|
||||
|
||||
GraphDb::EdgeType edge_type = dba1.edge_type("likes");
|
||||
EXPECT_EQ(edge_type, dba1.edge_type("likes"));
|
||||
EXPECT_NE(edge_type, dba1.edge_type("hates"));
|
||||
EXPECT_EQ(dba1.edge_type_name(edge_type), "likes");
|
||||
|
||||
// test that getting labels through a different accessor works
|
||||
EXPECT_EQ(edge_type, dbms.active().edge_type("likes"));
|
||||
EXPECT_NE(edge_type, dbms.active().edge_type("hates"));
|
||||
}
|
||||
|
||||
TEST(GraphDbAccessorTest, Properties) {
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba1 = dbms.active();
|
||||
|
||||
GraphDb::EdgeType prop = dba1.property("name");
|
||||
EXPECT_EQ(prop, dba1.property("name"));
|
||||
EXPECT_NE(prop, dba1.property("surname"));
|
||||
EXPECT_EQ(dba1.property_name(prop), "name");
|
||||
|
||||
// test that getting labels through a different accessor works
|
||||
EXPECT_EQ(prop, dbms.active().property("name"));
|
||||
EXPECT_NE(prop, dbms.active().property("surname"));
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
24
tests/unit/record_edge_vertex_accessor.cpp
Normal file
24
tests/unit/record_edge_vertex_accessor.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "dbms/dbms.hpp"
|
||||
#include "database/graph_db.hpp"
|
||||
#include "database/graph_db_accessor.hpp"
|
||||
|
||||
#include "storage/typed_value.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
|
||||
|
||||
TEST(RecordAccessor, PropertySet) {
|
||||
|
||||
Dbms dbms;
|
||||
GraphDbAccessor dba = dbms.active();
|
||||
|
||||
auto vertex = dba.insert_vertex();
|
||||
auto property = dba.property("PropName");
|
||||
|
||||
vertex.PropsSet(property, 42);
|
||||
EXPECT_EQ(vertex.PropsAt(property).Value<int>(), 42);
|
||||
EXPECT_TRUE((vertex.PropsAt(dba.property("Other")) == TypedValue::Null).Value<bool>());
|
||||
}
|
Loading…
Reference in New Issue
Block a user