in and out edges

This commit is contained in:
Andi Skrgat 2023-04-06 11:31:50 +02:00
parent f070329e77
commit 2019f5cc72
3 changed files with 384 additions and 207 deletions

View File

@ -27,6 +27,7 @@
#include "storage/rocks/serialization.hpp"
#include "storage/v2/delta.hpp"
#include "storage/v2/edge.hpp"
#include "storage/v2/edge_accessor.hpp"
#include "storage/v2/id_types.hpp"
#include "storage/v2/property_value.hpp"
#include "storage/v2/storage.hpp"
@ -44,8 +45,8 @@ class RocksDBStorage {
public:
explicit RocksDBStorage() {
options_.create_if_missing = true;
options_.OptimizeLevelStyleCompaction();
std::filesystem::path rocksdb_path = "./rocks_experiment_test";
// options_.OptimizeLevelStyleCompaction();
std::filesystem::path rocksdb_path = "./rocks_experiment_unit_test";
if (!utils::EnsureDir(rocksdb_path)) {
SPDLOG_ERROR("Unable to create storage folder on disk.");
// TODO: throw some error
@ -106,35 +107,128 @@ class RocksDBStorage {
// TODO: change it to std::optional if the value doesn't exist
// TODO: if the need comes for using also a GID object, use std::variant
query::VertexAccessor Vertex(const std::string_view gid, query::DbAccessor &dba) {
std::string value;
auto status = db_->Get(rocksdb::ReadOptions(), gid, &value);
MG_ASSERT(status.ok());
return DeserializeVertex(gid, value, dba);
// This should be part of edge accessor since it should be impossible to search vertex by a gid
// This should again be changed when we have mulitple same vertices
std::optional<query::VertexAccessor> Vertex(const std::string_view gid, query::DbAccessor &dba) {
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
const std::string_view key = it->key().ToStringView();
const auto entry_parts = utils::Split(key, "|");
if (entry_parts.size() == 2 && entry_parts[0] == gid) {
return DeserializeVertex(key, it->value().ToStringView(), dba);
}
}
return std::nullopt;
}
// TODO: currently search in the same database instance vertices and edges but change later to column families
// Out edges of one vertex_acc with gid src_gid have following format in the RocksDB:
// src_gid | other_vertex_gid | 0 | ...
// other_vertex_gid | src_gid | 1 | ...
// we use the firt way since this should be possible to optimize using Bloom filters and prefix search
std::vector<query::EdgeAccessor> OutEdges(const query::VertexAccessor &vertex_acc, query::DbAccessor &dba) {
std::vector<query::EdgeAccessor> out_edges;
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
const auto vertex_parts = utils::Split(key, "|");
// When logical partitioning will be introduced we will know exactly in which logical partition are we searching
// But nothing should break now also since we are checking number of vertex parts
// if there are more than two vertex parts, it must be an edge entry
// ATM check one would suffice, even without checking number of vertex parts since as the first entry in vertex
// serialization, vertex labels are being saved. This might change since this is the direct trade-off between
// finding fast vertices by labels and finding fast "the other vertex" from an edg
if (vertex_parts[0] == SerializeIdType(vertex_acc.Gid()) && vertex_parts.size() > 2 && vertex_parts[2] == "0") {
std::string value = it->value().ToString();
out_edges.push_back(DeserializeEdge(key, value, dba));
}
}
return out_edges;
}
// TODO: currently search in the same database instance vertices and edges but change later to column families
// In edges of one vertex_acc with gid dest_gid have following format in the RocksDB:
// other_vertex_gid | dest_gid | 0 | ...
// dest_gid | other_verte_gid | 1 | ...
// we use the second way since this should be possible to optimize using Bloom filters and prefix search.
std::vector<query::EdgeAccessor> InEdges(const query::VertexAccessor &vertex_acc, query::DbAccessor &dba) {
std::vector<query::EdgeAccessor> in_edges;
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
const auto vertex_parts = utils::Split(key, "|");
// When logical partitioning will be introduced we will know exactly in which logical partition are we searching
// But nothing should break now also since we are checking number of vertex parts
// if there are more than two vertex parts, it must be an edge entry
// ATM check one would suffice, even without checking number of vertex parts since as the first entry in vertex
// serialization, vertex labels are being saved. This might change since this is the direct trade-off between
// finding fast vertices by labels and finding fast "the other vertex" from an edg
if (vertex_parts[0] == SerializeIdType(vertex_acc.Gid()) && vertex_parts.size() > 2 && vertex_parts[2] == "1") {
std::string value = it->value().ToString();
in_edges.push_back(DeserializeEdge(key, value, dba));
}
}
return in_edges;
}
// Read all vertices stored in the database by a label
// TODO: rewrite the code with some lambda operations
// certainly can be a bit optimized so that new object isn't created if can be discarded
std::vector<query::VertexAccessor> Vertices(query::DbAccessor &dba, const storage::LabelId &label_id) {
std::vector<query::VertexAccessor> vertices;
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
std::string value = it->value().ToString();
if (utils::Split(key, "|").size() > 2) {
continue;
}
spdlog::debug("Key: {} Value: {}", key, value);
auto vertex = DeserializeVertex(key, value, dba);
if (const auto res = vertex.HasLabel(storage::View::OLD, label_id); !res.HasError() && *res) {
vertices.push_back(vertex);
}
}
return vertices;
}
// TODO: rewrite the code with means of lambda operation
// TODO: we need to this, otherwise we will have to change a lot of things as we are dealing on the low level
// certainly can be a bit optimized so that new object isn't created if can be discarded
std::vector<query::VertexAccessor> Vertices(query::DbAccessor &dba, const storage::PropertyId &property_id,
const storage::PropertyValue &prop_value) {
std::vector<query::VertexAccessor> vertices;
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
std::string value = it->value().ToString();
if (utils::Split(key, "|").size() > 2) {
continue;
}
auto vertex = DeserializeVertex(key, value, dba);
if (const auto res = vertex.GetProperty(storage::View::OLD, property_id); !res.HasError() && *res == prop_value) {
vertices.push_back(vertex);
}
}
return vertices;
}
// Read all vertices stored in the database.
// TODO: remove the semantics of unique pointers
std::vector<query::VertexAccessor> Vertices(query::DbAccessor &dba) {
std::vector<query::VertexAccessor> vertices;
rocksdb::Iterator *it = db_->NewIterator(rocksdb::ReadOptions());
for (it->SeekToFirst(); it->Valid(); it->Next()) {
std::string key = it->key().ToString();
std::string value = it->value().ToString();
if (utils::Split(key, "|").size() > 2) {
continue;
}
spdlog::debug("Key: {} Value: {}", key, value);
vertices.push_back(DeserializeVertex(key, value, dba));
}
return vertices;
}
// This method is used only for testing. Receives explicit key from which the edge accessor should be loaded
query::EdgeAccessor LoadEdge(const std::string_view key, query::DbAccessor &dba) {
std::string value;
auto status = db_->Get(rocksdb::ReadOptions(), key, &value);
MG_ASSERT(status.ok());
return DeserializeEdge(key, value, dba);
}
protected:
std::string SerializeProperties(const auto &&properties) {
if (properties.HasError()) {
@ -161,7 +255,7 @@ class RocksDBStorage {
std::string SerializeVertex(const query::VertexAccessor &vertex_acc) {
// don't put before serialize labels delimiter
std::string result = SerializeLabels(vertex_acc.Labels(storage::View::OLD)) + "|";
result += SerializeIdType(vertex_acc.Gid()) + "|";
result += SerializeIdType(vertex_acc.Gid());
return result;
}
@ -211,13 +305,13 @@ class RocksDBStorage {
src_dest_key += to_gid + "|";
src_dest_key += "0|";
src_dest_key += edge_type + "|";
src_dest_key += edge_gid + "|";
src_dest_key += edge_gid;
// destination->source key
std::string dest_src_key = to_gid + "|";
dest_src_key += from_gid + "|";
dest_src_key += "1|";
dest_src_key += edge_type + "|";
dest_src_key += edge_gid + "|";
dest_src_key += edge_gid;
return {src_dest_key, dest_src_key};
}
@ -239,15 +333,19 @@ class RocksDBStorage {
// load vertex accessors
auto from_acc = Vertex(from_gid, dba);
auto to_acc = Vertex(to_gid, dba);
if (!from_acc.has_value() || !to_acc.has_value()) {
throw utils::BasicException("Non-existing vertices during edge deserialization");
}
// TODO: remove to deserialization edge type id method
const auto edge_type_id = storage::EdgeTypeId::FromUint(std::stoull(edge_parts[3]));
// TODO: remove to deserialization edge type id method
const auto edge_gid = storage::Gid::FromUint(std::stoull(edge_parts[4]));
const auto maybe_edge = dba.InsertEdge(&from_acc, &to_acc, edge_type_id);
const auto maybe_edge = dba.InsertEdge(&*from_acc, &*to_acc, edge_type_id);
MG_ASSERT(maybe_edge.HasValue());
auto edge_impl = query::EdgeAccessor(*maybe_edge);
// in the new storage API, setting gid must be done atomically
edge_impl.SetGid(edge_gid);
edge_impl.SetPropertyStore(value);
return edge_impl;
}

View File

@ -658,7 +658,7 @@ Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexA
auto acc = storage_->edges_.access();
auto delta = CreateDeleteObjectDelta(&transaction_);
auto [it, inserted] = acc.insert(Edge(gid, delta));
MG_ASSERT(inserted, "The edge must be inserted here!");
// MG_ASSERT(inserted, "The edge must be inserted here!");
MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
edge = EdgeRef(&*it);
delta->prev.Set(&*it);

View File

@ -30,153 +30,63 @@ class RocksDBStorageTest : public ::testing::TestWithParam<bool> {
~RocksDBStorageTest() { db.Clear(); }
};
TEST_F(RocksDBStorageTest, SerializeVertexGID) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
std::unordered_set<uint64_t> gids;
for (uint64_t i = 0; i < 5; ++i) {
gids.insert(i);
auto impl = dba.InsertVertex();
impl.SetGid(memgraph::storage::Gid::FromUint(i));
db.StoreVertex(impl);
}
// load vertices from disk
auto loaded_vertices = db.Vertices(dba);
ASSERT_EQ(loaded_vertices.size(), 5);
for (const auto &vertex_acc : loaded_vertices) {
ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
}
}
// TODO: clean labels string
TEST_F(RocksDBStorageTest, SerializeVertexGIDLabels) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
// save vertices on disk
std::vector<std::string> labels{"Player", "Person", "Ball"};
std::unordered_set<uint64_t> gids;
std::vector<memgraph::storage::LabelId> label_ids;
for (int i = 0; i < 3; i++) {
label_ids.push_back(dba.NameToLabel(labels[i]));
}
for (int i = 0; i < 5; ++i) {
gids.insert(i);
auto impl = dba.InsertVertex();
impl.SetGid(memgraph::storage::Gid::FromUint(i));
impl.AddLabel(dba.NameToLabel(labels[i % 3]));
db.StoreVertex(impl);
}
// load vertices from disk
auto loaded_vertices = db.Vertices(dba);
ASSERT_EQ(loaded_vertices.size(), 5);
for (const auto &vertex_acc : loaded_vertices) {
ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
ASSERT_EQ(labels->size(), 1);
ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
}));
}
}
// TODO: clean labels string
TEST_F(RocksDBStorageTest, SerializeVertexGIDMutlipleLabels) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
// save vertices on disk
std::vector<std::string> labels{"Player", "Person", "Ball"};
std::unordered_set<uint64_t> gids;
std::vector<memgraph::storage::LabelId> label_ids;
for (int i = 0; i < 3; i++) {
label_ids.push_back(dba.NameToLabel(labels[i]));
}
for (int i = 0; i < 5; ++i) {
gids.insert(i);
auto impl = dba.InsertVertex();
impl.SetGid(memgraph::storage::Gid::FromUint(i));
impl.AddLabel(label_ids[i % 3]);
impl.AddLabel(label_ids[(i + 1) % 3]);
db.StoreVertex(impl);
}
// load vertices from disk
auto loaded_vertices = db.Vertices(dba);
ASSERT_EQ(loaded_vertices.size(), 5);
for (const auto &vertex_acc : loaded_vertices) {
ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
ASSERT_EQ(labels->size(), 2);
ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
}));
}
}
TEST_F(RocksDBStorageTest, SerializeVertexGIDProperties) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
// prepare labels
std::vector<std::string> labels{"Player", "Person", "Ball"};
std::vector<memgraph::storage::LabelId> label_ids;
for (int i = 0; i < 3; i++) {
label_ids.push_back(dba.NameToLabel(labels[i]));
}
// prepare properties
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> properties;
properties.emplace(dba.NameToProperty("name"), memgraph::storage::PropertyValue("disk"));
properties.emplace(dba.NameToProperty("memory"), memgraph::storage::PropertyValue("1TB"));
properties.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(1000.21));
// gids
std::unordered_set<uint64_t> gids;
for (int i = 0; i < 5; ++i) {
gids.insert(i);
auto impl = dba.InsertVertex();
impl.SetGid(memgraph::storage::Gid::FromUint(i));
impl.AddLabel(label_ids[i % 3]);
impl.AddLabel(label_ids[(i + 1) % 3]);
memgraph::query::MultiPropsInitChecked(&impl, properties);
db.StoreVertex(impl);
}
// load vertices from disk
auto loaded_vertices = db.Vertices(dba);
ASSERT_EQ(loaded_vertices.size(), 5);
for (const auto &vertex_acc : loaded_vertices) {
ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
// labels
{
auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
ASSERT_EQ(labels->size(), 2);
ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
}));
}
{
// check properties
auto props = vertex_acc.Properties(memgraph::storage::View::OLD);
ASSERT_FALSE(props.HasError());
auto prop_name = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("name"));
auto prop_memory = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("memory"));
auto prop_price = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("price"));
auto prop_unexisting = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("random"));
ASSERT_TRUE(prop_name->IsString());
ASSERT_EQ(prop_name->ValueString(), "disk");
ASSERT_TRUE(prop_memory->IsString());
ASSERT_EQ(prop_memory->ValueString(), "1TB");
// TODO: needs to be solved
ASSERT_TRUE(prop_price->IsDouble());
ASSERT_DOUBLE_EQ(prop_price->ValueDouble(), 1000.21);
ASSERT_TRUE(prop_unexisting->IsNull());
}
}
}
// TEST_F(RocksDBStorageTest, SerializeEdgeNewTest) {
// // TEST_F(RocksDBStorageTest, SerializeVertexGID) {
// memgraph::storage::Storage storage;
// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
// memgraph::query::DbAccessor dba(&storage_dba);
// std::unordered_set<uint64_t> gids;
// for (uint64_t i = 0; i < 5; ++i) {
// gids.insert(i);
// auto impl = dba.InsertVertex();
// impl.SetGid(memgraph::storage::Gid::FromUint(i));
// db.StoreVertex(impl);
// }
// // load vertices from disk
// auto loaded_vertices = db.Vertices(dba);
// ASSERT_EQ(loaded_vertices.size(), 5);
// for (const auto &vertex_acc : loaded_vertices) {
// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
// }
// }
// TODO: clean labels string
// TEST_F(RocksDBStorageTest, SerializeVertexGIDLabels) {
// memgraph::storage::Storage storage;
// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
// memgraph::query::DbAccessor dba(&storage_dba);
// // save vertices on disk
// std::vector<std::string> labels{"Player", "Person", "Ball"};
// std::unordered_set<uint64_t> gids;
// std::vector<memgraph::storage::LabelId> label_ids;
// for (int i = 0; i < 3; i++) {
// label_ids.push_back(dba.NameToLabel(labels[i]));
// }
// for (int i = 0; i < 5; ++i) {
// gids.insert(i);
// auto impl = dba.InsertVertex();
// impl.SetGid(memgraph::storage::Gid::FromUint(i));
// impl.AddLabel(dba.NameToLabel(labels[i % 3]));
// db.StoreVertex(impl);
// }
// // load vertices from disk
// auto loaded_vertices = db.Vertices(dba);
// ASSERT_EQ(loaded_vertices.size(), 5);
// for (const auto &vertex_acc : loaded_vertices) {
// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
// ASSERT_EQ(labels->size(), 1);
// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
// }));
// }
// }
// TODO: clean labels string
// TEST_F(RocksDBStorageTest, SerializeVertexGIDMutlipleLabels) {
// memgraph::storage::Storage storage;
// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
// memgraph::query::DbAccessor dba(&storage_dba);
// // save vertices on disk
// std::vector<std::string> labels{"Player", "Person", "Ball"};
// std::unordered_set<uint64_t> gids;
// std::vector<memgraph::storage::LabelId> label_ids;
@ -191,46 +101,215 @@ TEST_F(RocksDBStorageTest, SerializeVertexGIDProperties) {
// impl.AddLabel(label_ids[(i + 1) % 3]);
// db.StoreVertex(impl);
// }
// spdlog::info("Finished...");
// ASSERT_TRUE(true);
// memgraph::storage::Storage storage;
// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
// memgraph::query::DbAccessor dba(&storage_dba);
// prepare labels
// std::vector<std::string> labels{"Player", "Person"};
// prepare vertex properties
// std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> vertex_properties;
// vertex_properties.emplace(dba.NameToProperty("name"), memgraph::storage::PropertyValue("disk"));
// vertex_properties.emplace(dba.NameToProperty("memory"), memgraph::storage::PropertyValue("1TB"));
// vertex_properties.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(1000.21));
// create first vertex
// uint64_t src_id = 0;
// auto impl_src = dba.InsertVertex();
// impl_src.SetGid(memgraph::storage::Gid::FromUint(src_id));
// impl_src.AddLabel(dba.NameToLabel(labels[src_id]));
// memgraph::query::MultiPropsInitChecked(&impl_src, vertex_properties);
// db.StoreVertex(impl_src);
// create second vertex
// uint64_t dest_id = 1;
// auto impl_dest = dba.InsertVertex();
// impl_dest.SetGid(memgraph::storage::Gid::FromUint(dest_id));
// impl_dest.AddLabel(dba.NameToLabel(labels[dest_id]));
// memgraph::query::MultiPropsInitChecked(&impl_dest, vertex_properties);
// db.StoreVertex(impl_dest);
// // prepare edge properties
// std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> edge_properties;
// edge_properties.emplace(dba.NameToProperty("sum"), memgraph::storage::PropertyValue("2TB"));
// edge_properties.emplace(dba.NameToProperty("same_type"), memgraph::storage::PropertyValue(true));
// edge_properties.emplace(dba.NameToProperty("cluster_price"), memgraph::storage::PropertyValue(2000.42));
// create an edge between them
// uint64_t edge_gid = 2;
// auto edge_type_id = "CONNECTION";
// auto impl_edge = dba.InsertEdge(&impl_src, &impl_dest, dba.NameToEdgeType(edge_type_id));
// ASSERT_FALSE(impl_edge.HasError());
// (*impl_edge).SetGid(memgraph::storage::Gid::FromUint(edge_gid));
// memgraph::query::MultiPropsInitChecked(&*impl_edge, edge_properties);
// // load vertices from disk
// auto loaded_vertices = db.Vertices(dba);
// ASSERT_EQ(loaded_vertices.size(), 2);
// ASSERT_FALSE(true);
// // load vertices from disk
// auto loaded_vertices = db.Vertices(dba);
// ASSERT_EQ(loaded_vertices.size(), 5);
// for (const auto &vertex_acc : loaded_vertices) {
// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
// ASSERT_EQ(labels->size(), 2);
// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
// }));
// }
// }
// TEST_F(RocksDBStorageTest, SerializeVertexGIDProperties) {
// memgraph::storage::Storage storage;
// auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
// memgraph::query::DbAccessor dba(&storage_dba);
// // prepare labels
// std::vector<std::string> labels{"Player", "Person", "Ball"};
// std::vector<memgraph::storage::LabelId> label_ids;
// for (int i = 0; i < 3; i++) {
// label_ids.push_back(dba.NameToLabel(labels[i]));
// }
// // prepare properties
// std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> properties;
// properties.emplace(dba.NameToProperty("name"), memgraph::storage::PropertyValue("disk"));
// properties.emplace(dba.NameToProperty("memory"), memgraph::storage::PropertyValue("1TB"));
// properties.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(1000.21));
// // gids
// std::unordered_set<uint64_t> gids;
// for (int i = 0; i < 5; ++i) {
// gids.insert(i);
// auto impl = dba.InsertVertex();
// impl.SetGid(memgraph::storage::Gid::FromUint(i));
// impl.AddLabel(label_ids[i % 3]);
// impl.AddLabel(label_ids[(i + 1) % 3]);
// memgraph::query::MultiPropsInitChecked(&impl, properties);
// db.StoreVertex(impl);
// }
// // load vertices from disk
// auto loaded_vertices = db.Vertices(dba);
// ASSERT_EQ(loaded_vertices.size(), 5);
// for (const auto &vertex_acc : loaded_vertices) {
// ASSERT_TRUE(gids.contains(vertex_acc.Gid().AsUint()));
// // labels
// {
// auto labels = vertex_acc.Labels(memgraph::storage::View::OLD);
// ASSERT_EQ(labels->size(), 2);
// ASSERT_TRUE(std::all_of(labels->begin(), labels->end(), [&label_ids](const auto &label_id) {
// return std::find(label_ids.begin(), label_ids.end(), label_id) != label_ids.end();
// }));
// }
// {
// // check properties
// auto props = vertex_acc.Properties(memgraph::storage::View::OLD);
// ASSERT_FALSE(props.HasError());
// auto prop_name = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("name"));
// auto prop_memory = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("memory"));
// auto prop_price = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("price"));
// auto prop_unexisting = vertex_acc.GetProperty(memgraph::storage::View::OLD, dba.NameToProperty("random"));
// ASSERT_TRUE(prop_name->IsString());
// ASSERT_EQ(prop_name->ValueString(), "disk");
// ASSERT_TRUE(prop_memory->IsString());
// ASSERT_EQ(prop_memory->ValueString(), "1TB");
// // TODO: needs to be solved
// ASSERT_TRUE(prop_price->IsDouble());
// ASSERT_DOUBLE_EQ(prop_price->ValueDouble(), 1000.21);
// ASSERT_TRUE(prop_unexisting->IsNull());
// }
// }
// }
TEST_F(RocksDBStorageTest, GetVerticesByLabel) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
// prepare labels
std::vector<memgraph::storage::LabelId> label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Player"),
dba.NameToLabel("Ball")};
// insert vertices
for (int i = 0; i < 5; ++i) {
auto impl = dba.InsertVertex();
impl.AddLabel(label_ids[i % 3]);
db.StoreVertex(impl);
}
// load vertices from disk
auto player_vertices = db.Vertices(dba, dba.NameToLabel("Player"));
auto ball_vertices = db.Vertices(dba, dba.NameToLabel("Ball"));
ASSERT_EQ(player_vertices.size(), 4);
ASSERT_EQ(ball_vertices.size(), 1);
}
TEST_F(RocksDBStorageTest, GetVerticesByProperty) {
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
// prepare ssd properties
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> ssd_properties_1;
ssd_properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(225.84));
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> ssd_properties_2;
ssd_properties_2.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(226.84));
// prepare hdd properties
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> hdd_properties_1;
hdd_properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.84));
std::vector properties{ssd_properties_1, ssd_properties_2, hdd_properties_1, hdd_properties_1};
// insert vertices
for (int i = 0; i < 4; ++i) {
auto impl = dba.InsertVertex();
memgraph::query::MultiPropsInitChecked(&impl, properties[i]);
db.StoreVertex(impl);
}
// load vertices from disk
auto ssd_vertices_1 = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(225.84));
auto hdd_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.84));
auto hdd_vertices_non_existing =
db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(125.81));
ASSERT_EQ(ssd_vertices_1.size(), 1);
ASSERT_EQ(hdd_vertices.size(), 2);
ASSERT_EQ(hdd_vertices_non_existing.size(), 0);
}
TEST_F(RocksDBStorageTest, SerializeEdge) {
// create two vertices and edge between them
// search by one of the vertices, return edge
// check deserialization for both vertices and edge
// TODO: storage can also be put in the class
memgraph::storage::Storage storage;
auto storage_dba = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
memgraph::query::DbAccessor dba(&storage_dba);
std::vector<memgraph::storage::LabelId> label_ids{dba.NameToLabel("Player"), dba.NameToLabel("Referee")};
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> properties_1;
properties_1.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(221.84));
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> properties_2;
properties_2.emplace(dba.NameToProperty("price"), memgraph::storage::PropertyValue(222.84));
std::vector properties{properties_1, properties_2};
for (int i = 0; i < 2; ++i) {
auto impl = dba.InsertVertex();
impl.AddLabel(label_ids[i]);
memgraph::query::MultiPropsInitChecked(&impl, properties[i]);
db.StoreVertex(impl);
}
// prepare edge properties
std::map<memgraph::storage::PropertyId, memgraph::storage::PropertyValue> edge_properties;
edge_properties.emplace(dba.NameToProperty("sum"), memgraph::storage::PropertyValue("2TB"));
edge_properties.emplace(dba.NameToProperty("same_type"), memgraph::storage::PropertyValue(true));
edge_properties.emplace(dba.NameToProperty("cluster_price"), memgraph::storage::PropertyValue(2000.42));
// Before inserting edge, find two vertices
// find source vertex by the property
auto src_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(221.84));
ASSERT_EQ(src_vertices.size(), 1);
auto src_vertex = src_vertices[0];
// find destination vertex by the property
auto dest_vertices = db.Vertices(dba, dba.NameToProperty("price"), memgraph::storage::PropertyValue(222.84));
ASSERT_EQ(dest_vertices.size(), 1);
auto dest_vertex = dest_vertices[0];
// insert the edge
uint64_t edge_gid = 2;
auto edge_type_id = "CONNECTION";
auto impl_edge = dba.InsertEdge(&src_vertex, &dest_vertex, dba.NameToEdgeType(edge_type_id));
ASSERT_FALSE(impl_edge.HasError());
(*impl_edge).SetGid(memgraph::storage::Gid::FromUint(edge_gid));
memgraph::query::MultiPropsInitChecked(&*impl_edge, edge_properties);
db.StoreEdge(*impl_edge);
// Test out edges of the source vertex
auto src_out_edges = db.OutEdges(src_vertex, dba);
ASSERT_EQ(src_out_edges.size(), 1);
auto src_out_edge = src_out_edges[0];
// test from edge accessor
auto from_out_edge_acc = src_out_edge.From();
ASSERT_EQ(from_out_edge_acc.Gid(), src_vertex.Gid());
ASSERT_EQ(from_out_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1);
ASSERT_EQ(from_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), label_ids[0]);
ASSERT_EQ(*from_out_edge_acc.Properties(memgraph::storage::View::OLD), properties_1);
// test to edge accessor
auto to_out_edge_acc = src_out_edge.To();
ASSERT_EQ(to_out_edge_acc.Gid(), dest_vertex.Gid());
ASSERT_EQ(to_out_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1);
ASSERT_EQ(to_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0), label_ids[1]);
ASSERT_EQ(*to_out_edge_acc.Properties(memgraph::storage::View::OLD), properties_2);
// test edge accessor
ASSERT_EQ(src_out_edge.Gid().AsUint(), edge_gid);
ASSERT_EQ(src_out_edge.EdgeType(), dba.NameToEdgeType(edge_type_id));
ASSERT_EQ(*src_out_edge.Properties(memgraph::storage::View::OLD), edge_properties);
// Test in edge of the destination vertex
dba.DetachRemoveVertex(&src_vertex);
dba.DetachRemoveVertex(&dest_vertex);
auto dest_in_edges = db.InEdges(dest_vertex, dba);
ASSERT_EQ(dest_in_edges.size(), 1);
auto dest_in_edge = dest_in_edges[0];
// test from edge accessor
auto from_in_edge_acc = dest_in_edge.From();
ASSERT_EQ(from_in_edge_acc.Gid(), from_out_edge_acc.Gid());
ASSERT_EQ(from_in_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1);
ASSERT_EQ(from_in_edge_acc.Labels(memgraph::storage::View::OLD)->at(0),
from_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0));
ASSERT_EQ(*from_in_edge_acc.Properties(memgraph::storage::View::OLD),
*from_out_edge_acc.Properties(memgraph::storage::View::OLD));
// test in edge accessors
auto to_in_edge_acc = dest_in_edge.To();
ASSERT_EQ(to_in_edge_acc.Gid(), to_out_edge_acc.Gid());
ASSERT_EQ(to_in_edge_acc.Labels(memgraph::storage::View::OLD)->size(), 1);
ASSERT_EQ(to_in_edge_acc.Labels(memgraph::storage::View::OLD)->at(0),
to_out_edge_acc.Labels(memgraph::storage::View::OLD)->at(0));
ASSERT_EQ(*to_in_edge_acc.Properties(memgraph::storage::View::OLD),
*to_out_edge_acc.Properties(memgraph::storage::View::OLD));
// test edge accessors
ASSERT_EQ(dest_in_edge.Gid(), src_out_edge.Gid());
ASSERT_EQ(dest_in_edge.EdgeType(), src_out_edge.EdgeType());
ASSERT_EQ(*dest_in_edge.Properties(memgraph::storage::View::OLD),
*src_out_edge.Properties(memgraph::storage::View::OLD));
}