diff --git a/src/database/graph_db_accessor.cpp b/src/database/graph_db_accessor.cpp index 9767e460e..8e6176a2f 100644 --- a/src/database/graph_db_accessor.cpp +++ b/src/database/graph_db_accessor.cpp @@ -51,8 +51,12 @@ void GraphDbAccessor::update_label_index(const GraphDbTypes::Label &label, this->db_.labels_index_.Update(label, vertex_accessor.vlist_, vertex); } -size_t GraphDbAccessor::vertices_count(const GraphDbTypes::Label &label) { - return this->db_.labels_index_.Count(label); +size_t GraphDbAccessor::vertices_count() const { + return db_.vertices_.access().size(); +} + +size_t GraphDbAccessor::vertices_count(const GraphDbTypes::Label &label) const { + return db_.labels_index_.Count(label); } bool GraphDbAccessor::remove_vertex(VertexAccessor &vertex_accessor) { @@ -111,8 +115,13 @@ void GraphDbAccessor::update_edge_type_index( this->db_.edge_types_index_.Update(edge_type, edge_accessor.vlist_, edge); } -size_t GraphDbAccessor::edges_count(const GraphDbTypes::EdgeType &edge_type) { - return this->db_.edge_types_index_.Count(edge_type); +size_t GraphDbAccessor::edges_count() const { + return db_.edges_.access().size(); +} + +size_t GraphDbAccessor::edges_count( + const GraphDbTypes::EdgeType &edge_type) const { + return db_.edge_types_index_.Count(edge_type); } /** diff --git a/src/database/graph_db_accessor.hpp b/src/database/graph_db_accessor.hpp index 858d496c9..33c66ba20 100644 --- a/src/database/graph_db_accessor.hpp +++ b/src/database/graph_db_accessor.hpp @@ -78,8 +78,13 @@ class GraphDbAccessor { /** * Returns iterable over accessors to all the vertices in the graph * visible to the current transaction. + * + * @param current_state If true then the graph state for the + * current transaction+command is returned (insertions, updates and + * deletions performed in the current transaction+command are not + * ignored). */ - auto vertices() { + auto vertices(bool current_state = false) { // wrap version lists into accessors, which will look for visible versions auto accessors = iter::imap([this](auto vlist) { return VertexAccessor(*vlist, *this); }, @@ -87,8 +92,12 @@ class GraphDbAccessor { // filter out the accessors not visible to the current transaction return iter::filter( - [this](const VertexAccessor &accessor) { - return accessor.old_ != nullptr; + [this, current_state](const VertexAccessor &accessor) { + return (accessor.old_ && + !(current_state && + accessor.old_->is_deleted_by(*transaction_))) || + (current_state && accessor.new_ && + !accessor.new_->is_deleted_by(*transaction_)); }, std::move(accessors)); } @@ -97,12 +106,18 @@ class GraphDbAccessor { * Return VertexAccessors which contain the current label for the current * transaction visibilty. * @param label - label for which to return VertexAccessors + * @param current_state If true then the graph state for the + * current transaction+command is returned (insertions, updates and + * deletions performed in the current transaction+command are not + * ignored). * @return iterable collection */ - auto vertices(const GraphDbTypes::Label &label) { + auto vertices(const GraphDbTypes::Label &label, bool current_state = false) { return iter::imap( - [this](auto vlist) { return VertexAccessor(*vlist, *this); }, - db_.labels_index_.GetVlists(label, *transaction_)); + [this, current_state](auto vlist) { + return VertexAccessor(*vlist, *this); + }, + db_.labels_index_.GetVlists(label, *transaction_, current_state)); } /** @@ -126,8 +141,13 @@ class GraphDbAccessor { /** * Returns iterable over accessors to all the edges in the graph * visible to the current transaction. + * + * @param current_state If true then the graph state for the + * current transaction+command is returned (insertions, updates and + * deletions performed in the current transaction+command are not + * ignored). */ - auto edges() { + auto edges(bool current_state = false) { // wrap version lists into accessors, which will look for visible versions auto accessors = iter::imap([this](auto vlist) { return EdgeAccessor(*vlist, *this); }, @@ -135,22 +155,32 @@ class GraphDbAccessor { // filter out the accessors not visible to the current transaction return iter::filter( - [this](const EdgeAccessor &accessor) { - return accessor.old_ != nullptr; + [this, current_state](const EdgeAccessor &accessor) { + return (accessor.old_ && + !(current_state && + accessor.old_->is_deleted_by(*transaction_))) || + (current_state && accessor.new_ && + !accessor.new_->is_deleted_by(*transaction_)); }, std::move(accessors)); } /** * Return EdgeAccessors which contain the edge_type for the current - * transaction visibilty. + * transaction visibility. * @param edge_type - edge_type for which to return EdgeAccessors + * @param current_state If true then the graph state for the + * current transaction+command is returned (insertions, updates and + * deletions performed in the current transaction+command are not + * ignored). * @return iterable collection */ - auto edges(const GraphDbTypes::EdgeType &edge_type) { - return iter::imap( - [this](auto vlist) { return EdgeAccessor(*vlist, *this); }, - db_.edge_types_index_.GetVlists(edge_type, *transaction_)); + auto edges(const GraphDbTypes::EdgeType &edge_type, + bool current_state = false) { + return iter::imap([this, current_state]( + auto vlist) { return EdgeAccessor(*vlist, *this); }, + db_.edge_types_index_.GetVlists(edge_type, *transaction_, + current_state)); } /** @@ -173,13 +203,25 @@ class GraphDbAccessor { const EdgeAccessor &edge_accessor, const Edge *edge); + /** + * Return approximate number of all vertices in the database. + * Note that this is always an over-estimate and never an under-estimate. + */ + size_t vertices_count() const; + + /* + * Return approximate number of all edges in the database. + * Note that this is always an over-estimate and never an under-estimate. + */ + size_t edges_count() const; + /** * Return approximate number of vertices under indexes with the given label. * Note that this is always an over-estimate and never an under-estimate. * @param label - label to check for * @return number of vertices with the given label */ - size_t vertices_count(const GraphDbTypes::Label &label); + size_t vertices_count(const GraphDbTypes::Label &label) const; /** * Return approximate number of edges under indexes with the given edge_type. @@ -187,7 +229,7 @@ class GraphDbAccessor { * @param edge_type - edge_type to check for * @return number of edges with the given edge_type */ - size_t edges_count(const GraphDbTypes::EdgeType &edge_type); + size_t edges_count(const GraphDbTypes::EdgeType &edge_type) const; /** * Obtains the Label for the label's name. @@ -258,7 +300,7 @@ class GraphDbAccessor { */ template bool Reconstruct(RecordAccessor &accessor) { - accessor.vlist_->find_set_new_old(*transaction_, accessor.old_, + accessor.vlist_->find_set_old_new(*transaction_, accessor.old_, accessor.new_); accessor.current_ = accessor.old_ ? accessor.old_ : accessor.new_; return accessor.old_ != nullptr || accessor.new_ != nullptr; diff --git a/src/database/indexes/key_index.hpp b/src/database/indexes/key_index.hpp index 630f297c6..d9bc3a25c 100644 --- a/src/database/indexes/key_index.hpp +++ b/src/database/indexes/key_index.hpp @@ -44,19 +44,40 @@ class KeyIndex { * still have that label visible in this transaction. * @param key - key to query. * @param t - current transaction, which determines visibility. + * @param current_state If true then the graph state for the + * current transaction+command is returned (insertions, updates and + * deletions performed in the current transaction+command are not + * ignored). * @return iterable collection of vlists records with the requested * TKey. */ - auto GetVlists(const TKey &key, tx::Transaction &t) { + auto GetVlists(const TKey &key, tx::Transaction &t, + bool current_state = false) { auto index = GetKeyStorage(key); mvcc::VersionList *prev = nullptr; auto filtered = iter::filter( - [this, &key, &t, prev](auto entry) mutable { + [this, &key, &t, prev, current_state](auto entry) mutable { + // we check for previous match first as an optimization + // it's legal because if the first v-list pair does not + // pass, neither will any other identical one if (entry.vlist_ == prev) return false; - auto version = entry.vlist_->find(t); prev = entry.vlist_; - if (version == nullptr) return false; - return Exists(key, version); + // TODO when refactoring MVCC reconsider the return-value-arg idiom here + TRecord *old_record, *new_record; + entry.vlist_->find_set_old_new(t, old_record, new_record); + // filtering out records not visible to the current + // transaction+command + // taking into account the current_state flag + bool visible = + (old_record && + !(current_state && old_record->is_deleted_by(t))) || + (current_state && new_record && !new_record->is_deleted_by(t)); + if (!visible) return false; + // if we current_state and we have the new record, then that's the + // reference value, and that needs to be compared with the index + // predicate + return (current_state && new_record) ? Exists(key, new_record) + : Exists(key, old_record); }, index->access()); return iter::imap([this](auto entry) { return entry.vlist_; }, diff --git a/src/mvcc/version_list.hpp b/src/mvcc/version_list.hpp index 877be31b8..bc6c7a351 100644 --- a/src/mvcc/version_list.hpp +++ b/src/mvcc/version_list.hpp @@ -178,7 +178,7 @@ class VersionList { * * @param t The transaction */ - void find_set_new_old(const tx::Transaction &t, T *&old_ref, T *&new_ref) { + void find_set_old_new(const tx::Transaction &t, T *&old_ref, T *&new_ref) { // assume that the sought old record is further down the list // from new record, so that if we found old we can stop looking new_ref = nullptr; diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index 53a3fbf85..9eda6b76c 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -188,12 +188,8 @@ std::unique_ptr ScanAll::MakeCursor(GraphDbAccessor &db) { ScanAll::ScanAllCursor::ScanAllCursor(const ScanAll &self, GraphDbAccessor &db) : self_(self), input_cursor_(self.input_->MakeCursor(db)), - // TODO change to db.vertices(self.graph_view_ == GraphView::NEW) - // once this GraphDbAccessor API is available - vertices_(db.vertices()), - vertices_it_(vertices_.end()) { - if (self.graph_view_ == GraphView::NEW) throw utils::NotYetImplemented(); -} + vertices_(db.vertices(self.graph_view_ == GraphView::NEW)), + vertices_it_(vertices_.end()) {} bool ScanAll::ScanAllCursor::Pull(Frame &frame, const SymbolTable &symbol_table) { diff --git a/tests/unit/labels_index.cpp b/tests/unit/database_key_index.cpp similarity index 60% rename from tests/unit/labels_index.cpp rename to tests/unit/database_key_index.cpp index 401a9b5c7..92800944b 100644 --- a/tests/unit/labels_index.cpp +++ b/tests/unit/database_key_index.cpp @@ -2,66 +2,46 @@ #include "gtest/gtest.h" #include "data_structures/ptr_int.hpp" -#include "database/graph_db_accessor.hpp" #include "database/graph_db_datatypes.hpp" +#include "database/graph_db_accessor.hpp" #include "dbms/dbms.hpp" #include "storage/vertex.hpp" using testing::UnorderedElementsAreArray; -// Test counter of indexed vertices with the given label. -TEST(LabelsIndex, Count) { - Dbms dbms; - auto accessor = dbms.active(); - const int ITERS = 50; - size_t cnt = 0; - for (int i = 0; i < ITERS; ++i) { - auto vertex = accessor->insert_vertex(); - if (rand() & 1) { - vertex.add_label(accessor->label("test")); - ++cnt; - } else { - vertex.add_label(accessor->label("test2")); - } - // Greater or equal since we said that we always estimate at least the - // real number. - EXPECT_GE(accessor->vertices_count(accessor->label("test")), cnt); - } -} - // Test index does it insert everything uniquely TEST(LabelsIndex, UniqueInsert) { KeyIndex index; Dbms dbms; - auto access = dbms.active(); + auto dba = dbms.active(); tx::Engine engine; auto t1 = engine.begin(); mvcc::VersionList vlist(*t1); t1->commit(); auto t2 = engine.begin(); - vlist.find(*t2)->labels_.push_back(access->label("1")); - index.Update(access->label("1"), &vlist, vlist.find(*t2)); + vlist.find(*t2)->labels_.push_back(dba->label("1")); + index.Update(dba->label("1"), &vlist, vlist.find(*t2)); // Try multiple inserts - index.Update(access->label("1"), &vlist, vlist.find(*t2)); + index.Update(dba->label("1"), &vlist, vlist.find(*t2)); - vlist.find(*t2)->labels_.push_back(access->label("2")); - index.Update(access->label("2"), &vlist, vlist.find(*t2)); + vlist.find(*t2)->labels_.push_back(dba->label("2")); + index.Update(dba->label("2"), &vlist, vlist.find(*t2)); - vlist.find(*t2)->labels_.push_back(access->label("3")); - index.Update(access->label("3"), &vlist, vlist.find(*t2)); + vlist.find(*t2)->labels_.push_back(dba->label("3")); + index.Update(dba->label("3"), &vlist, vlist.find(*t2)); t2->commit(); - EXPECT_EQ(index.Count(access->label("1")), 1); - EXPECT_EQ(index.Count(access->label("2")), 1); - EXPECT_EQ(index.Count(access->label("3")), 1); + EXPECT_EQ(index.Count(dba->label("1")), 1); + EXPECT_EQ(index.Count(dba->label("2")), 1); + EXPECT_EQ(index.Count(dba->label("3")), 1); } // Check if index filters duplicates. TEST(LabelsIndex, UniqueFilter) { KeyIndex index; Dbms dbms; - auto access = dbms.active(); + auto dba = dbms.active(); tx::Engine engine; auto t1 = engine.begin(); @@ -70,10 +50,10 @@ TEST(LabelsIndex, UniqueFilter) { t1->engine.advance( t1->id); // advance command so we can see our inserted version auto r1v1 = vlist1.find(*t1); - auto r1v2 = vlist1.find(*t1); + auto r1v2 = vlist2.find(*t1); EXPECT_NE(vlist1.find(*t1), nullptr); - auto label1 = access->label("1"); + auto label1 = dba->label("1"); vlist1.find(*t1)->labels_.push_back(label1); vlist2.find(*t1)->labels_.push_back(label1); index.Update(label1, &vlist1, r1v1); @@ -140,10 +120,10 @@ TEST(LabelsIndex, Refresh) { // Transaction hasn't ended and so the vertex is not visible. TEST(LabelsIndexDb, AddGetZeroLabels) { Dbms dbms; - auto accessor = dbms.active(); - auto vertex = accessor->insert_vertex(); - vertex.add_label(accessor->label("test")); - auto collection = accessor->vertices(accessor->label("test")); + auto dba = dbms.active(); + auto vertex = dba->insert_vertex(); + vertex.add_label(dba->label("test")); + auto collection = dba->vertices(dba->label("test")); std::vector collection_vector(collection.begin(), collection.end()); EXPECT_EQ(collection_vector.size(), (size_t)0); @@ -154,61 +134,61 @@ TEST(LabelsIndexDb, AddGetZeroLabels) { TEST(LabelsIndexDb, AddGetRemoveLabel) { Dbms dbms; { - auto accessor = dbms.active(); + auto dba = dbms.active(); - auto vertex1 = accessor->insert_vertex(); - vertex1.add_label(accessor->label("test")); + auto vertex1 = dba->insert_vertex(); + vertex1.add_label(dba->label("test")); - auto vertex2 = accessor->insert_vertex(); - vertex2.add_label(accessor->label("test2")); + auto vertex2 = dba->insert_vertex(); + vertex2.add_label(dba->label("test2")); - auto vertex3 = accessor->insert_vertex(); - vertex3.add_label(accessor->label("test")); + auto vertex3 = dba->insert_vertex(); + vertex3.add_label(dba->label("test")); - accessor->commit(); + dba->commit(); } // Finish transaction. { - auto accessor = dbms.active(); + auto dba = dbms.active(); - auto filtered = accessor->vertices(accessor->label("test")); + auto filtered = dba->vertices(dba->label("test")); std::vector collection(filtered.begin(), filtered.end()); - auto vertices = accessor->vertices(); + auto vertices = dba->vertices(); std::vector expected_collection; for (auto vertex : vertices) { - if (vertex.has_label(accessor->label("test"))) { + if (vertex.has_label(dba->label("test"))) { expected_collection.push_back(vertex); } else { - EXPECT_TRUE(vertex.has_label(accessor->label("test2"))); + EXPECT_TRUE(vertex.has_label(dba->label("test2"))); } } EXPECT_EQ(expected_collection.size(), collection.size()); - EXPECT_TRUE(collection[0].has_label(accessor->label("test"))); - EXPECT_TRUE(collection[1].has_label(accessor->label("test"))); - EXPECT_FALSE(collection[0].has_label(accessor->label("test2"))); - EXPECT_FALSE(collection[1].has_label(accessor->label("test2"))); - accessor->remove_vertex(collection[0]); // Remove from database and test if - // index won't return it. + EXPECT_TRUE(collection[0].has_label(dba->label("test"))); + EXPECT_TRUE(collection[1].has_label(dba->label("test"))); + EXPECT_FALSE(collection[0].has_label(dba->label("test2"))); + EXPECT_FALSE(collection[1].has_label(dba->label("test2"))); + dba->remove_vertex(collection[0]); // Remove from database and test if + // index won't return it. // Remove label from the vertex and add new label. - collection[1].remove_label(accessor->label("test")); - collection[1].add_label(accessor->label("test2")); - accessor->commit(); + collection[1].remove_label(dba->label("test")); + collection[1].add_label(dba->label("test2")); + dba->commit(); } { - auto accessor = dbms.active(); + auto dba = dbms.active(); - auto filtered = accessor->vertices(accessor->label("test")); + auto filtered = dba->vertices(dba->label("test")); std::vector collection(filtered.begin(), filtered.end()); - auto vertices = accessor->vertices(); + auto vertices = dba->vertices(); std::vector expected_collection; for (auto vertex : vertices) { - if (vertex.has_label(accessor->label("test"))) { + if (vertex.has_label(dba->label("test"))) { expected_collection.push_back(vertex); } else { - EXPECT_TRUE(vertex.has_label(accessor->label("test2"))); + EXPECT_TRUE(vertex.has_label(dba->label("test2"))); } } @@ -219,6 +199,8 @@ TEST(LabelsIndexDb, AddGetRemoveLabel) { } } +// TODO gleich - discuss with Flor the API changes and the tests + int main(int argc, char **argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/tests/unit/edge_types_index.cpp b/tests/unit/edge_types_index.cpp deleted file mode 100644 index a9bbefaee..000000000 --- a/tests/unit/edge_types_index.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "gmock/gmock.h" -#include "gtest/gtest.h" - -#include "data_structures/ptr_int.hpp" -#include "database/graph_db_accessor.hpp" -#include "dbms/dbms.hpp" - -using testing::UnorderedElementsAreArray; - -// Test counter of indexed edges with the given edge_type. -TEST(EdgeTypesIndex, Count) { - Dbms dbms; - auto accessor = dbms.active(); - const int ITERS = 50; - size_t cnt = 0; - for (int i = 0; i < ITERS; ++i) { - auto vertex1 = accessor->insert_vertex(); - auto vertex2 = accessor->insert_vertex(); - if (rand() & 1) { - accessor->insert_edge(vertex1, vertex2, accessor->edge_type("test")); - ++cnt; - } else { - accessor->insert_edge(vertex1, vertex2, accessor->edge_type("test2")); - } - // Greater or equal since we said that we always estimate at least the - // real number. - EXPECT_GE(accessor->edges_count(accessor->edge_type("test")), cnt); - } -} - -// Transaction hasn't ended and so the edge is not visible. -TEST(EdgeTypesIndex, AddGetZeroEdgeTypes) { - Dbms dbms; - auto accessor = dbms.active(); - auto vertex1 = accessor->insert_vertex(); - auto vertex2 = accessor->insert_vertex(); - accessor->insert_edge(vertex1, vertex2, accessor->edge_type("test")); - auto collection = accessor->edges(accessor->edge_type("test")); - std::vector collection_vector(collection.begin(), - collection.end()); - EXPECT_EQ(collection_vector.size(), (size_t)0); -} - -// Test edge type index by adding and removing one edge, checking edge_type of -// another, while the third one with an irrelevant edge_type exists. -TEST(LabelsIndex, AddGetRemoveEdgeTypes) { - Dbms dbms; - { - auto accessor = dbms.active(); - - auto vertex11 = accessor->insert_vertex(); - auto vertex12 = accessor->insert_vertex(); - accessor->insert_edge(vertex11, vertex12, accessor->edge_type("test")); - - auto vertex21 = accessor->insert_vertex(); - auto vertex22 = accessor->insert_vertex(); - accessor->insert_edge(vertex21, vertex22, accessor->edge_type("test2")); - - auto vertex31 = accessor->insert_vertex(); - auto vertex32 = accessor->insert_vertex(); - accessor->insert_edge(vertex31, vertex32, accessor->edge_type("test")); - - accessor->commit(); - } // Finish transaction. - { - auto accessor = dbms.active(); - - auto filtered = accessor->edges(accessor->edge_type("test")); - std::vector collection(filtered.begin(), filtered.end()); - auto edges = accessor->edges(); - - std::vector expected_collection; - for (auto edge : edges) { - if (edge.edge_type() == accessor->edge_type("test")) { - expected_collection.push_back(edge); - } else { - EXPECT_TRUE(edge.edge_type() == accessor->edge_type("test2")); - } - } - - EXPECT_EQ(expected_collection.size(), collection.size()); - EXPECT_TRUE(collection[0].edge_type() == accessor->edge_type("test")); - EXPECT_TRUE(collection[1].edge_type() == accessor->edge_type("test")); - EXPECT_FALSE(collection[0].edge_type() == accessor->edge_type("test2")); - EXPECT_FALSE(collection[1].edge_type() == accessor->edge_type("test2")); - accessor->remove_edge(collection[0]); // Remove from database and test if - // index won't return it. - accessor->remove_edge(collection[1]); // Remove from database and test if - // index won't return it. - - accessor->commit(); - } - { - auto accessor = dbms.active(); - - auto filtered = accessor->edges(accessor->edge_type("test")); - std::vector collection(filtered.begin(), filtered.end()); - auto edges = accessor->edges(); - - std::vector expected_collection; - for (auto edge : edges) { - if (edge.edge_type() == accessor->edge_type("test")) { - expected_collection.push_back(edge); - } else { - EXPECT_TRUE(edge.edge_type() == accessor->edge_type("test2")); - } - } - - // It should be empty since everything with an old edge_type is either - // deleted or doesn't have that edge_type anymore. - EXPECT_EQ(expected_collection.size(), 0); - EXPECT_EQ(collection.size(), 0); - } -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/tests/unit/graph_db_accessor.cpp b/tests/unit/graph_db_accessor.cpp index 6685fa628..4f54ff117 100644 --- a/tests/unit/graph_db_accessor.cpp +++ b/tests/unit/graph_db_accessor.cpp @@ -7,18 +7,10 @@ #include "storage/edge_accessor.hpp" #include "storage/vertex_accessor.hpp" -size_t CountVertices(GraphDbAccessor &db_accessor) { - size_t r_val = 0; - for ([[gnu::unused]] auto va : db_accessor.vertices()) r_val++; - return r_val; -} - -size_t CountEdges(GraphDbAccessor &db_accessor) { - size_t r_val = 0; - for ([[gnu::unused]] auto va : db_accessor.edges()) r_val++; - - return r_val; +template +auto Count(TIterable iterable) { + return std::distance(iterable.begin(), iterable.end()); } TEST(GraphDbAccessorTest, DbmsCreateDefault) { @@ -31,31 +23,37 @@ TEST(GraphDbAccessorTest, InsertVertex) { Dbms dbms; auto accessor = dbms.active(); - EXPECT_EQ(CountVertices(*accessor), 0); + EXPECT_EQ(Count(accessor->vertices()), 0); accessor->insert_vertex(); + EXPECT_EQ(Count(accessor->vertices()), 0); + EXPECT_EQ(Count(accessor->vertices(true)), 1); accessor->advance_command(); - EXPECT_EQ(CountVertices(*accessor), 1); + EXPECT_EQ(Count(accessor->vertices()), 1); accessor->insert_vertex(); + EXPECT_EQ(Count(accessor->vertices()), 1); + EXPECT_EQ(Count(accessor->vertices(true)), 2); accessor->advance_command(); - EXPECT_EQ(CountVertices(*accessor), 2); + EXPECT_EQ(Count(accessor->vertices()), 2); } TEST(GraphDbAccessorTest, RemoveVertexSameTransaction) { Dbms dbms; auto accessor = dbms.active(); - EXPECT_EQ(CountVertices(*accessor), 0); + EXPECT_EQ(Count(accessor->vertices()), 0); auto va1 = accessor->insert_vertex(); accessor->advance_command(); - EXPECT_EQ(CountVertices(*accessor), 1); + EXPECT_EQ(Count(accessor->vertices()), 1); EXPECT_TRUE(accessor->remove_vertex(va1)); - EXPECT_EQ(CountVertices(*accessor), 1); + EXPECT_EQ(Count(accessor->vertices()), 1); + EXPECT_EQ(Count(accessor->vertices(true)), 0); accessor->advance_command(); - EXPECT_EQ(CountVertices(*accessor), 0); + EXPECT_EQ(Count(accessor->vertices()), 0); + EXPECT_EQ(Count(accessor->vertices(true)), 0); } TEST(GraphDbAccessorTest, RemoveVertexDifferentTransaction) { @@ -68,14 +66,16 @@ TEST(GraphDbAccessorTest, RemoveVertexDifferentTransaction) { // second transaction checks that it sees it, and deletes it auto accessor2 = dbms.active(); - EXPECT_EQ(CountVertices(*accessor2), 1); + EXPECT_EQ(Count(accessor2->vertices()), 1); + EXPECT_EQ(Count(accessor2->vertices(true)), 1); for (auto vertex_accessor : accessor2->vertices()) accessor2->remove_vertex(vertex_accessor); accessor2->commit(); // third transaction checks that it does not see the vertex auto accessor3 = dbms.active(); - EXPECT_EQ(CountVertices(*accessor3), 0); + EXPECT_EQ(Count(accessor3->vertices()), 0); + EXPECT_EQ(Count(accessor3->vertices(true)), 0); } TEST(GraphDbAccessorTest, InsertEdge) { @@ -92,8 +92,11 @@ TEST(GraphDbAccessorTest, InsertEdge) { // setup (v1) - [:likes] -> (v2) dba->insert_edge(va1, va2, dba->edge_type("likes")); + EXPECT_EQ(Count(dba->edges()), 0); + EXPECT_EQ(Count(dba->edges(true)), 1); dba->advance_command(); - EXPECT_EQ(CountEdges(*dba), 1); + EXPECT_EQ(Count(dba->edges()), 1); + EXPECT_EQ(Count(dba->edges(true)), 1); EXPECT_EQ(va1.out().begin()->to(), va2); EXPECT_EQ(va2.in().begin()->from(), va1); EXPECT_EQ(va1.in_degree(), 0); @@ -104,8 +107,10 @@ TEST(GraphDbAccessorTest, InsertEdge) { // setup (v1) - [:likes] -> (v2) <- [:hates] - (v3) auto va3 = dba->insert_vertex(); dba->insert_edge(va3, va2, dba->edge_type("hates")); + EXPECT_EQ(Count(dba->edges(false)), 1); + EXPECT_EQ(Count(dba->edges(true)), 2); dba->advance_command(); - EXPECT_EQ(CountEdges(*dba), 2); + EXPECT_EQ(Count(dba->edges()), 2); EXPECT_EQ(va3.out().begin()->to(), va2); EXPECT_EQ(va1.in_degree(), 0); EXPECT_EQ(va1.out_degree(), 1); @@ -126,17 +131,21 @@ TEST(GraphDbAccessorTest, RemoveEdge) { dba->insert_edge(va1, va2, dba->edge_type("likes")); dba->insert_edge(va3, va2, dba->edge_type("hates")); dba->advance_command(); - EXPECT_EQ(CountEdges(*dba), 2); + EXPECT_EQ(Count(dba->edges()), 2); + EXPECT_EQ(Count(dba->edges(true)), 2); // remove all [:hates] edges - EXPECT_EQ(CountEdges(*dba), 2); for (auto edge : dba->edges()) if (edge.edge_type() == dba->edge_type("hates")) dba->remove_edge(edge); + EXPECT_EQ(Count(dba->edges()), 2); + EXPECT_EQ(Count(dba->edges(true)), 1); // current state: (v1) - [:likes] -> (v2), (v3) dba->advance_command(); - EXPECT_EQ(CountEdges(*dba), 1); - EXPECT_EQ(CountVertices(*dba), 3); + EXPECT_EQ(Count(dba->edges()), 1); + EXPECT_EQ(Count(dba->edges(true)), 1); + EXPECT_EQ(Count(dba->vertices()), 3); + EXPECT_EQ(Count(dba->vertices(true)), 3); for (auto edge : dba->edges()) { EXPECT_EQ(edge.edge_type(), dba->edge_type("likes")); auto v1 = edge.from(); @@ -175,50 +184,58 @@ TEST(GraphDbAccessorTest, DetachRemoveVertex) { for (auto &vertex : vertices) vertex.Reconstruct(); // ensure that plain remove does NOT work - EXPECT_EQ(CountVertices(*dba), 4); - EXPECT_EQ(CountEdges(*dba), 3); + EXPECT_EQ(Count(dba->vertices()), 4); + EXPECT_EQ(Count(dba->edges()), 3); EXPECT_FALSE(dba->remove_vertex(vertices[0])); EXPECT_FALSE(dba->remove_vertex(vertices[1])); EXPECT_FALSE(dba->remove_vertex(vertices[2])); - EXPECT_EQ(CountVertices(*dba), 4); - EXPECT_EQ(CountEdges(*dba), 3); + EXPECT_EQ(Count(dba->vertices()), 4); + EXPECT_EQ(Count(dba->edges()), 3); dba->detach_remove_vertex(vertices[2]); + EXPECT_EQ(Count(dba->vertices()), 4); + EXPECT_EQ(Count(dba->vertices(true)), 3); + EXPECT_EQ(Count(dba->edges()), 3); + EXPECT_EQ(Count(dba->edges(true)), 1); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), 3); - EXPECT_EQ(CountEdges(*dba), 1); + EXPECT_EQ(Count(dba->vertices()), 3); + EXPECT_EQ(Count(dba->edges()), 1); EXPECT_TRUE(dba->remove_vertex(vertices[3])); + EXPECT_EQ(Count(dba->vertices(true)), 2); + EXPECT_EQ(Count(dba->vertices()), 3); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), 2); - EXPECT_EQ(CountEdges(*dba), 1); + EXPECT_EQ(Count(dba->vertices()), 2); + EXPECT_EQ(Count(dba->edges()), 1); for (auto va : dba->vertices()) EXPECT_FALSE(dba->remove_vertex(va)); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), 2); - EXPECT_EQ(CountEdges(*dba), 1); + EXPECT_EQ(Count(dba->vertices()), 2); + EXPECT_EQ(Count(dba->edges()), 1); for (auto va : dba->vertices()) { EXPECT_FALSE(dba->remove_vertex(va)); dba->detach_remove_vertex(va); break; } + EXPECT_EQ(Count(dba->vertices(true)), 1); + EXPECT_EQ(Count(dba->vertices()), 2); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), 1); - EXPECT_EQ(CountEdges(*dba), 0); + EXPECT_EQ(Count(dba->vertices()), 1); + EXPECT_EQ(Count(dba->edges()), 0); // remove the last vertex, it has no connections // so that should work for (auto va : dba->vertices()) EXPECT_TRUE(dba->remove_vertex(va)); dba->advance_command(); - EXPECT_EQ(CountVertices(*dba), 0); - EXPECT_EQ(CountEdges(*dba), 0); + EXPECT_EQ(Count(dba->vertices()), 0); + EXPECT_EQ(Count(dba->edges()), 0); } TEST(GraphDbAccessorTest, DetachRemoveVertexMultiple) { @@ -241,30 +258,30 @@ TEST(GraphDbAccessorTest, DetachRemoveVertexMultiple) { dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), N); - EXPECT_EQ(CountEdges(*dba), N * N); + EXPECT_EQ(Count(dba->vertices()), N); + EXPECT_EQ(Count(dba->edges()), N * N); // detach delete one edge dba->detach_remove_vertex(vertices[0]); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), N - 1); - EXPECT_EQ(CountEdges(*dba), (N - 1) * (N - 1)); + EXPECT_EQ(Count(dba->vertices()), N - 1); + EXPECT_EQ(Count(dba->edges()), (N - 1) * (N - 1)); // detach delete two neighboring edges dba->detach_remove_vertex(vertices[1]); dba->detach_remove_vertex(vertices[2]); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), N - 3); - EXPECT_EQ(CountEdges(*dba), (N - 3) * (N - 3)); + EXPECT_EQ(Count(dba->vertices()), N - 3); + EXPECT_EQ(Count(dba->edges()), (N - 3) * (N - 3)); // detach delete everything, buwahahahaha for (int l = 3; l < N; ++l) dba->detach_remove_vertex(vertices[l]); dba->advance_command(); for (auto &vertex : vertices) vertex.Reconstruct(); - EXPECT_EQ(CountVertices(*dba), 0); - EXPECT_EQ(CountEdges(*dba), 0); + EXPECT_EQ(Count(dba->vertices()), 0); + EXPECT_EQ(Count(dba->edges()), 0); } TEST(GraphDbAccessorTest, Labels) { diff --git a/tests/unit/graph_db_accessor_index_api.cpp b/tests/unit/graph_db_accessor_index_api.cpp new file mode 100644 index 000000000..9f09a0e75 --- /dev/null +++ b/tests/unit/graph_db_accessor_index_api.cpp @@ -0,0 +1,126 @@ +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "data_structures/ptr_int.hpp" +#include "database/graph_db_accessor.hpp" +#include "dbms/dbms.hpp" + +using testing::UnorderedElementsAreArray; + +template +auto Count(TIterable iterable) { + return std::distance(iterable.begin(), iterable.end()); +} + +TEST(GraphDbAccessor, VertexCount) { + Dbms dbms; + auto dba = dbms.active(); + auto lab1 = dba->label("lab1"); + auto lab2 = dba->label("lab2"); + + EXPECT_EQ(dba->vertices_count(lab1), 0); + EXPECT_EQ(dba->vertices_count(lab2), 0); + EXPECT_EQ(dba->vertices_count(), 0); + for (int i = 0; i < 11; ++i) dba->insert_vertex().add_label(lab1); + for (int i = 0; i < 17; ++i) dba->insert_vertex().add_label(lab2); + // even though xxx_count functions in GraphDbAccessor can over-estaimate + // in this situation they should be exact (nothing was ever deleted) + EXPECT_EQ(dba->vertices_count(lab1), 11); + EXPECT_EQ(dba->vertices_count(lab2), 17); + EXPECT_EQ(dba->vertices_count(), 28); +} + +TEST(GraphDbAccessor, EdgeCount) { + Dbms dbms; + auto dba = dbms.active(); + auto t1 = dba->edge_type("t1"); + auto t2 = dba->edge_type("t2"); + + EXPECT_EQ(dba->edges_count(t1), 0); + EXPECT_EQ(dba->edges_count(t2), 0); + EXPECT_EQ(dba->edges_count(), 0); + auto v1 = dba->insert_vertex(); + auto v2 = dba->insert_vertex(); + for (int i = 0; i < 11; ++i) dba->insert_edge(v1, v2, t1); + for (int i = 0; i < 17; ++i) dba->insert_edge(v1, v2, t2); + // even though xxx_count functions in GraphDbAccessor can over-estaimate + // in this situation they should be exact (nothing was ever deleted) + EXPECT_EQ(dba->edges_count(t1), 11); + EXPECT_EQ(dba->edges_count(t2), 17); + EXPECT_EQ(dba->edges_count(), 28); +} + +TEST(GraphDbAccessor, VisibilityAfterInsertion) { + + Dbms dbms; + auto dba = dbms.active(); + auto v1 = dba->insert_vertex(); + auto v2 = dba->insert_vertex(); + auto lab1 = dba->label("lab1"); + auto lab2 = dba->label("lab2"); + v1.add_label(lab1); + auto type1 = dba->edge_type("type1"); + auto type2 = dba->edge_type("type2"); + dba->insert_edge(v1, v2, type1); + + EXPECT_EQ(Count(dba->vertices(lab1)), 0); + EXPECT_EQ(Count(dba->vertices(lab1, true)), 1); + EXPECT_EQ(Count(dba->vertices(lab2)), 0); + EXPECT_EQ(Count(dba->vertices(lab2, true)), 0); + EXPECT_EQ(Count(dba->edges(type1)), 0); + EXPECT_EQ(Count(dba->edges(type1, true)), 1); + EXPECT_EQ(Count(dba->edges(type2)), 0); + EXPECT_EQ(Count(dba->edges(type2, true)), 0); + + dba->advance_command(); + + EXPECT_EQ(Count(dba->vertices(lab1)), 1); + EXPECT_EQ(Count(dba->vertices(lab1, true)), 1); + EXPECT_EQ(Count(dba->vertices(lab2)), 0); + EXPECT_EQ(Count(dba->vertices(lab2, true)), 0); + EXPECT_EQ(Count(dba->edges(type1)), 1); + EXPECT_EQ(Count(dba->edges(type1, true)), 1); + EXPECT_EQ(Count(dba->edges(type2)), 0); + EXPECT_EQ(Count(dba->edges(type2, true)), 0); +} + +TEST(GraphDbAccessor, VisibilityAfterDeletion) { + + Dbms dbms; + auto dba = dbms.active(); + auto lab = dba->label("lab"); + for (int i = 0; i < 5; ++i) + dba->insert_vertex().add_label(lab); + dba->advance_command(); + auto type = dba->edge_type("type"); + for (int j = 0; j < 3; ++j) { + auto vertices_it = dba->vertices().begin(); + dba->insert_edge(*vertices_it++, *vertices_it, type); + } + dba->advance_command(); + + EXPECT_EQ(Count(dba->vertices(lab)), 5); + EXPECT_EQ(Count(dba->vertices(lab, true)), 5); + EXPECT_EQ(Count(dba->edges(type)), 3); + EXPECT_EQ(Count(dba->edges(type, true)), 3); + + // delete two edges + auto edges_it = dba->edges().begin(); + for (int k = 0; k < 2; ++k) + dba->remove_edge(*edges_it++); + EXPECT_EQ(Count(dba->edges(type)), 3); + EXPECT_EQ(Count(dba->edges(type, true)), 1); + dba->advance_command(); + EXPECT_EQ(Count(dba->edges(type)), 1); + EXPECT_EQ(Count(dba->edges(type, true)), 1); + + // detach-delete 2 vertices + auto vertices_it = dba->vertices().begin(); + for (int k = 0; k < 2; ++k) + dba->detach_remove_vertex(*vertices_it++); + EXPECT_EQ(Count(dba->vertices(lab)), 5); + EXPECT_EQ(Count(dba->vertices(lab, true)), 3); + dba->advance_command(); + EXPECT_EQ(Count(dba->vertices(lab)), 3); + EXPECT_EQ(Count(dba->vertices(lab, true)), 3); +}