Auto generate vertex/edge ids
Reviewers: buda Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D1406
This commit is contained in:
parent
adda7d1200
commit
c294127065
@ -15,6 +15,18 @@
|
|||||||
#include "utils/atomic.hpp"
|
#include "utils/atomic.hpp"
|
||||||
#include "utils/on_scope_exit.hpp"
|
#include "utils/on_scope_exit.hpp"
|
||||||
|
|
||||||
|
// Autogenerate property with Vertex ID
|
||||||
|
DEFINE_bool(
|
||||||
|
generate_vertex_ids, false,
|
||||||
|
"If enabled database will automatically generate Ids as properties of "
|
||||||
|
"vertices, which can be accessed using the `Id` cypher function.");
|
||||||
|
|
||||||
|
// Autogenerate property with Edge ID
|
||||||
|
DEFINE_bool(
|
||||||
|
generate_edge_ids, false,
|
||||||
|
"If enabled database will automatically generate Ids as properties of "
|
||||||
|
"edges, which can be accessed using the `Id` cypher function.");
|
||||||
|
|
||||||
namespace database {
|
namespace database {
|
||||||
|
|
||||||
GraphDbAccessor::GraphDbAccessor(GraphDb &db)
|
GraphDbAccessor::GraphDbAccessor(GraphDb &db)
|
||||||
@ -65,7 +77,7 @@ VertexAccessor GraphDbAccessor::InsertVertex(
|
|||||||
std::experimental::optional<gid::Gid> requested_gid) {
|
std::experimental::optional<gid::Gid> requested_gid) {
|
||||||
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
DCHECK(!commited_ && !aborted_) << "Accessor committed or aborted";
|
||||||
|
|
||||||
auto gid = db_.storage().vertex_generator_.Next(requested_gid);
|
int64_t gid = db_.storage().vertex_generator_.Next(requested_gid);
|
||||||
auto vertex_vlist = new mvcc::VersionList<Vertex>(transaction_, gid);
|
auto vertex_vlist = new mvcc::VersionList<Vertex>(transaction_, gid);
|
||||||
|
|
||||||
bool success =
|
bool success =
|
||||||
@ -74,7 +86,10 @@ VertexAccessor GraphDbAccessor::InsertVertex(
|
|||||||
<< gid;
|
<< gid;
|
||||||
wal().Emplace(
|
wal().Emplace(
|
||||||
database::StateDelta::CreateVertex(transaction_.id_, vertex_vlist->gid_));
|
database::StateDelta::CreateVertex(transaction_.id_, vertex_vlist->gid_));
|
||||||
return VertexAccessor(vertex_vlist, *this);
|
auto va = VertexAccessor(vertex_vlist, *this);
|
||||||
|
if (FLAGS_generate_vertex_ids)
|
||||||
|
va.PropsSet(Property(PropertyValueStore::IdPropertyName), gid);
|
||||||
|
return va;
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexAccessor GraphDbAccessor::InsertVertexIntoRemote(
|
VertexAccessor GraphDbAccessor::InsertVertexIntoRemote(
|
||||||
@ -391,16 +406,9 @@ EdgeAccessor GraphDbAccessor::InsertEdge(
|
|||||||
|
|
||||||
Vertex *from_updated;
|
Vertex *from_updated;
|
||||||
if (from.is_local()) {
|
if (from.is_local()) {
|
||||||
auto gid = db_.storage().edge_generator_.Next(requested_gid);
|
auto edge_accessor =
|
||||||
edge_address = new mvcc::VersionList<Edge>(
|
InsertOnlyEdge(from.address(), to.address(), edge_type, requested_gid);
|
||||||
transaction_, gid, from.address(), to.address(), edge_type);
|
edge_address = edge_accessor.address(),
|
||||||
// We need to insert edge_address to edges_ before calling update since
|
|
||||||
// update can throw and edge_vlist will not be garbage collected if it is
|
|
||||||
// not in edges_ skiplist.
|
|
||||||
bool success =
|
|
||||||
db_.storage().edges_.access().insert(gid, edge_address.local()).second;
|
|
||||||
CHECK(success) << "Attempting to insert an edge with an existing GID: "
|
|
||||||
<< gid;
|
|
||||||
|
|
||||||
from.SwitchNew();
|
from.SwitchNew();
|
||||||
from_updated = &from.update();
|
from_updated = &from.update();
|
||||||
@ -409,7 +417,7 @@ EdgeAccessor GraphDbAccessor::InsertEdge(
|
|||||||
// `CREATE_EDGE`, but always have it split into 3 parts (edge insertion,
|
// `CREATE_EDGE`, but always have it split into 3 parts (edge insertion,
|
||||||
// in/out modification).
|
// in/out modification).
|
||||||
wal().Emplace(database::StateDelta::CreateEdge(
|
wal().Emplace(database::StateDelta::CreateEdge(
|
||||||
transaction_.id_, gid, from.gid(), to.gid(), edge_type,
|
transaction_.id_, edge_accessor.gid(), from.gid(), to.gid(), edge_type,
|
||||||
EdgeTypeName(edge_type)));
|
EdgeTypeName(edge_type)));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -467,7 +475,7 @@ EdgeAccessor GraphDbAccessor::InsertOnlyEdge(
|
|||||||
std::experimental::optional<gid::Gid> requested_gid) {
|
std::experimental::optional<gid::Gid> requested_gid) {
|
||||||
CHECK(from.is_local())
|
CHECK(from.is_local())
|
||||||
<< "`from` address should be local when calling InsertOnlyEdge";
|
<< "`from` address should be local when calling InsertOnlyEdge";
|
||||||
auto gid = db_.storage().edge_generator_.Next(requested_gid);
|
int64_t gid = db_.storage().edge_generator_.Next(requested_gid);
|
||||||
auto edge_vlist =
|
auto edge_vlist =
|
||||||
new mvcc::VersionList<Edge>(transaction_, gid, from, to, edge_type);
|
new mvcc::VersionList<Edge>(transaction_, gid, from, to, edge_type);
|
||||||
// We need to insert edge_vlist to edges_ before calling update since update
|
// We need to insert edge_vlist to edges_ before calling update since update
|
||||||
@ -476,7 +484,10 @@ EdgeAccessor GraphDbAccessor::InsertOnlyEdge(
|
|||||||
bool success = db_.storage().edges_.access().insert(gid, edge_vlist).second;
|
bool success = db_.storage().edges_.access().insert(gid, edge_vlist).second;
|
||||||
CHECK(success) << "Attempting to insert an edge with an existing GID: "
|
CHECK(success) << "Attempting to insert an edge with an existing GID: "
|
||||||
<< gid;
|
<< gid;
|
||||||
return EdgeAccessor(edge_vlist, *this, from, to, edge_type);
|
auto ea = EdgeAccessor(edge_vlist, *this, from, to, edge_type);
|
||||||
|
if (FLAGS_generate_edge_ids)
|
||||||
|
ea.PropsSet(Property(PropertyValueStore::IdPropertyName), gid);
|
||||||
|
return ea;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t GraphDbAccessor::EdgesCount() const {
|
int64_t GraphDbAccessor::EdgesCount() const {
|
||||||
|
@ -624,16 +624,32 @@ TypedValue WorkerId(const std::vector<TypedValue> &args,
|
|||||||
}
|
}
|
||||||
|
|
||||||
TypedValue Id(const std::vector<TypedValue> &args,
|
TypedValue Id(const std::vector<TypedValue> &args,
|
||||||
database::GraphDbAccessor &) {
|
database::GraphDbAccessor &dba) {
|
||||||
if (args.size() != 1U) {
|
if (args.size() != 1U) {
|
||||||
throw QueryRuntimeException("Id takes one argument");
|
throw QueryRuntimeException("Id takes one argument");
|
||||||
}
|
}
|
||||||
auto &arg = args[0];
|
auto &arg = args[0];
|
||||||
switch (arg.type()) {
|
switch (arg.type()) {
|
||||||
case TypedValue::Type::Vertex:
|
case TypedValue::Type::Vertex: {
|
||||||
return static_cast<int64_t>(arg.ValueVertex().gid());
|
auto id = arg.ValueVertex().PropsAt(
|
||||||
case TypedValue::Type::Edge:
|
dba.Property(PropertyValueStore::IdPropertyName));
|
||||||
return static_cast<int64_t>(arg.ValueEdge().gid());
|
if (id.IsNull()) {
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Ids are not set on vertices, have a look at flags to "
|
||||||
|
"automatically generate them.");
|
||||||
|
}
|
||||||
|
return id.Value<int64_t>();
|
||||||
|
}
|
||||||
|
case TypedValue::Type::Edge: {
|
||||||
|
auto id = arg.ValueEdge().PropsAt(
|
||||||
|
dba.Property(PropertyValueStore::IdPropertyName));
|
||||||
|
if (id.IsNull()) {
|
||||||
|
throw QueryRuntimeException(
|
||||||
|
"Ids are not set on edges, have a look at flags to "
|
||||||
|
"automatically generate them.");
|
||||||
|
}
|
||||||
|
return id.Value<int64_t>();
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw QueryRuntimeException("Id argument must be a vertex or edge");
|
throw QueryRuntimeException("Id argument must be a vertex or edge");
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,10 @@ class PropertyValueStore {
|
|||||||
using Location = storage::Location;
|
using Location = storage::Location;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// Property name which will be used to store vertex/edge ids inside property
|
||||||
|
// value store
|
||||||
|
static constexpr char IdPropertyName[] = "__id__";
|
||||||
|
|
||||||
PropertyValueStore() = default;
|
PropertyValueStore() = default;
|
||||||
|
|
||||||
PropertyValueStore(const PropertyValueStore &old) {
|
PropertyValueStore(const PropertyValueStore &old) {
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "gflags/gflags.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
@ -24,6 +25,9 @@ using query::test_common::ToList;
|
|||||||
using testing::ElementsAre;
|
using testing::ElementsAre;
|
||||||
using testing::UnorderedElementsAre;
|
using testing::UnorderedElementsAre;
|
||||||
|
|
||||||
|
DECLARE_bool(generate_vertex_ids);
|
||||||
|
DECLARE_bool(generate_edge_ids);
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
struct NoContextExpressionEvaluator {
|
struct NoContextExpressionEvaluator {
|
||||||
@ -1364,19 +1368,43 @@ TEST(ExpressionEvaluator, FunctionIndexInfo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ExpressionEvaluator, FunctionUid) {
|
TEST(ExpressionEvaluator, FunctionIdNoGeneration) {
|
||||||
database::SingleNode db;
|
database::SingleNode db;
|
||||||
EXPECT_THROW(EvaluateFunction("ID", {}, db), QueryRuntimeException);
|
EXPECT_THROW(EvaluateFunction("ID", {}, db), QueryRuntimeException);
|
||||||
database::GraphDbAccessor dba(db);
|
database::GraphDbAccessor dba(db);
|
||||||
|
|
||||||
|
auto va = dba.InsertVertex();
|
||||||
|
auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge"));
|
||||||
|
// Unable to get id if they are not set
|
||||||
|
EXPECT_THROW(EvaluateFunction("ID", {va}, db), QueryRuntimeException);
|
||||||
|
EXPECT_THROW(EvaluateFunction("ID", {ea}, db), QueryRuntimeException);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExpressionEvaluator, FunctionIdGenerateVertexIds) {
|
||||||
|
FLAGS_generate_vertex_ids = true;
|
||||||
|
database::SingleNode db;
|
||||||
|
database::GraphDbAccessor dba(db);
|
||||||
auto va = dba.InsertVertex();
|
auto va = dba.InsertVertex();
|
||||||
auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge"));
|
auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge"));
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {va}, db).Value<int64_t>(), 0);
|
EXPECT_EQ(EvaluateFunction("ID", {va}, db).Value<int64_t>(), 0);
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {ea}, db).Value<int64_t>(), 0);
|
EXPECT_THROW(EvaluateFunction("ID", {ea}, db), QueryRuntimeException);
|
||||||
|
|
||||||
auto vb = dba.InsertVertex();
|
auto vb = dba.InsertVertex();
|
||||||
auto eb = dba.InsertEdge(vb, vb, dba.EdgeType("edge"));
|
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {vb}, db).Value<int64_t>(), 1024);
|
EXPECT_EQ(EvaluateFunction("ID", {vb}, db).Value<int64_t>(), 1024);
|
||||||
|
FLAGS_generate_vertex_ids = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ExpressionEvaluator, FunctionIdGenerateEdgeIds) {
|
||||||
|
FLAGS_generate_edge_ids = true;
|
||||||
|
database::SingleNode db;
|
||||||
|
database::GraphDbAccessor dba(db);
|
||||||
|
auto va = dba.InsertVertex();
|
||||||
|
auto ea = dba.InsertEdge(va, va, dba.EdgeType("edge"));
|
||||||
|
EXPECT_THROW(EvaluateFunction("ID", {va}, db), QueryRuntimeException);
|
||||||
|
EXPECT_EQ(EvaluateFunction("ID", {ea}, db).Value<int64_t>(), 0);
|
||||||
|
|
||||||
|
auto eb = dba.InsertEdge(va, va, dba.EdgeType("edge"));
|
||||||
EXPECT_EQ(EvaluateFunction("ID", {eb}, db).Value<int64_t>(), 1024);
|
EXPECT_EQ(EvaluateFunction("ID", {eb}, db).Value<int64_t>(), 1024);
|
||||||
|
FLAGS_generate_edge_ids = false;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
Loading…
Reference in New Issue
Block a user