Dump indices in CypherDumpGenerator
Summary: This change introduces dumping of indices keys. During the dump process, an internal label is assigned to each vertex and index on vertex's internal property id is created for faster matching during edge creation. Reviewers: teon.banek Reviewed By: teon.banek Subscribers: msantl, pullbot Differential Revision: https://phabricator.memgraph.io/D2046
This commit is contained in:
parent
d70792f1ce
commit
b09b21b832
@ -23,6 +23,10 @@ namespace {
|
||||
// matching.
|
||||
const char *kInternalPropertyId = "__mg_id__";
|
||||
|
||||
// Label that is attached to each vertex and is used for easier creation of
|
||||
// index on internal property id.
|
||||
const char *kInternalVertexLabel = "__mg_vertex__";
|
||||
|
||||
void DumpPropertyValue(std::ostream *os, const PropertyValue &value) {
|
||||
switch (value.type()) {
|
||||
case PropertyValue::Type::Null:
|
||||
@ -82,10 +86,11 @@ void DumpProperties(std::ostream *os, GraphDbAccessor *dba,
|
||||
void DumpVertex(std::ostream *os, GraphDbAccessor *dba,
|
||||
const VertexAccessor &vertex) {
|
||||
*os << "CREATE (";
|
||||
*os << ":" << kInternalVertexLabel;
|
||||
for (const auto &label : vertex.labels()) {
|
||||
*os << ":" << dba->LabelName(label);
|
||||
}
|
||||
if (!vertex.labels().empty()) *os << " ";
|
||||
*os << " ";
|
||||
DumpProperties(os, dba, vertex.Properties(),
|
||||
std::optional<uint64_t>(vertex.CypherId()));
|
||||
*os << ");";
|
||||
@ -108,30 +113,51 @@ void DumpEdge(std::ostream *os, GraphDbAccessor *dba,
|
||||
*os << "]->(v);";
|
||||
}
|
||||
|
||||
void DumpInternalIndexCleanup(std::ostream *os) {
|
||||
// TODO(tsabolcec): Don't forget to drop the index by internal id.
|
||||
*os << "MATCH (u) REMOVE u." << kInternalPropertyId << ";";
|
||||
void DumpIndexKey(std::ostream *os, GraphDbAccessor *dba,
|
||||
const LabelPropertyIndex::Key &key) {
|
||||
*os << "CREATE ";
|
||||
if (key.unique_) *os << "UNIQUE ";
|
||||
*os << "INDEX ON :" << dba->LabelName(key.label_) << "("
|
||||
<< dba->PropertyName(key.property_) << ");";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
CypherDumpGenerator::CypherDumpGenerator(GraphDbAccessor *dba)
|
||||
: dba_(dba), cleaned_internals_(false) {
|
||||
: dba_(dba),
|
||||
created_internal_index_(false),
|
||||
cleaned_internal_index_(false),
|
||||
cleaned_internal_label_property_(false) {
|
||||
CHECK(dba);
|
||||
indices_state_.emplace(dba->GetIndicesKeys());
|
||||
vertices_state_.emplace(dba->Vertices(false));
|
||||
edges_state_.emplace(dba->Edges(false));
|
||||
}
|
||||
|
||||
bool CypherDumpGenerator::NextQuery(std::ostream *os) {
|
||||
if (!vertices_state_->ReachedEnd()) {
|
||||
if (!indices_state_->ReachedEnd()) {
|
||||
DumpIndexKey(os, dba_, *indices_state_->GetCurrentAndAdvance());
|
||||
return true;
|
||||
} else if (!vertices_state_->Empty() && !created_internal_index_) {
|
||||
*os << "CREATE INDEX ON :" << kInternalVertexLabel << "("
|
||||
<< kInternalPropertyId << ");";
|
||||
created_internal_index_ = true;
|
||||
return true;
|
||||
} else if (!vertices_state_->ReachedEnd()) {
|
||||
DumpVertex(os, dba_, *vertices_state_->GetCurrentAndAdvance());
|
||||
return true;
|
||||
} else if (!edges_state_->ReachedEnd()) {
|
||||
DumpEdge(os, dba_, *edges_state_->GetCurrentAndAdvance());
|
||||
return true;
|
||||
} else if (!vertices_state_->Empty() && !cleaned_internals_) {
|
||||
DumpInternalIndexCleanup(os);
|
||||
cleaned_internals_ = true;
|
||||
} else if (!vertices_state_->Empty() && !cleaned_internal_index_) {
|
||||
*os << "DROP INDEX ON :" << kInternalVertexLabel << "("
|
||||
<< kInternalPropertyId << ");";
|
||||
cleaned_internal_index_ = true;
|
||||
return true;
|
||||
} else if (!vertices_state_->Empty() && !cleaned_internal_label_property_) {
|
||||
*os << "MATCH (u) REMOVE u:" << kInternalVertexLabel << ", u."
|
||||
<< kInternalPropertyId << ";";
|
||||
cleaned_internal_label_property_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,10 @@ namespace database {
|
||||
/// Class which generates sequence of openCypher queries which can be used to
|
||||
/// dump the database state.
|
||||
///
|
||||
/// Currently, only vertices and edges are being dumped, one-by-one in multiple
|
||||
/// queries. Indices keys, constraints, roles, etc. are currently not dumped.
|
||||
/// Currently only dumps index keys, vertices and edges, one-by-one in multiple
|
||||
/// queries.
|
||||
// TODO(tsabolcec): We should also dump constraints once that functionality is
|
||||
// integrated in MemGraph.
|
||||
class CypherDumpGenerator {
|
||||
public:
|
||||
explicit CypherDumpGenerator(GraphDbAccessor *dba);
|
||||
@ -51,8 +53,12 @@ class CypherDumpGenerator {
|
||||
|
||||
GraphDbAccessor *dba_;
|
||||
|
||||
bool cleaned_internals_;
|
||||
bool created_internal_index_;
|
||||
bool cleaned_internal_index_;
|
||||
bool cleaned_internal_label_property_;
|
||||
|
||||
std::optional<ContainerState<std::vector<LabelPropertyIndex::Key>>>
|
||||
indices_state_;
|
||||
std::optional<ContainerState<decltype(dba_->Vertices(false))>>
|
||||
vertices_state_;
|
||||
std::optional<ContainerState<decltype(dba_->Edges(false))>> edges_state_;
|
||||
|
@ -18,6 +18,11 @@ using database::CypherDumpGenerator;
|
||||
|
||||
const char *kPropertyId = "property_id";
|
||||
|
||||
const char *kCreateInternalIndex = "CREATE INDEX ON :__mg_vertex__(__mg_id__);";
|
||||
const char *kDropInternalIndex = "DROP INDEX ON :__mg_vertex__(__mg_id__);";
|
||||
const char *kRemoveInternalLabelProperty =
|
||||
"MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;";
|
||||
|
||||
// A helper struct that contains info about database that is used to compare
|
||||
// two databases (to check if their states are the same). It is assumed that
|
||||
// each vertex and each edge have unique integer property under key
|
||||
@ -35,8 +40,15 @@ struct DatabaseState {
|
||||
std::map<std::string, PropertyValue> props;
|
||||
};
|
||||
|
||||
struct IndexKey {
|
||||
std::string label;
|
||||
std::string property;
|
||||
bool is_unique;
|
||||
};
|
||||
|
||||
std::set<Vertex> vertices;
|
||||
std::set<Edge> edges;
|
||||
std::set<IndexKey> indices;
|
||||
};
|
||||
|
||||
bool operator<(const DatabaseState::Vertex &first,
|
||||
@ -55,6 +67,14 @@ bool operator<(const DatabaseState::Edge &first,
|
||||
return first.props < second.props;
|
||||
}
|
||||
|
||||
bool operator<(const DatabaseState::IndexKey &first,
|
||||
const DatabaseState::IndexKey &second) {
|
||||
if (first.label != second.label) return first.label < second.label;
|
||||
if (first.property != second.property)
|
||||
return first.property < second.property;
|
||||
return first.is_unique < second.is_unique;
|
||||
}
|
||||
|
||||
bool operator==(const DatabaseState::Vertex &first,
|
||||
const DatabaseState::Vertex &second) {
|
||||
return first.id == second.id && first.labels == second.labels &&
|
||||
@ -67,8 +87,15 @@ bool operator==(const DatabaseState::Edge &first,
|
||||
first.edge_type == second.edge_type && first.props == second.props;
|
||||
}
|
||||
|
||||
bool operator==(const DatabaseState::IndexKey &first,
|
||||
const DatabaseState::IndexKey &second) {
|
||||
return first.label == second.label && first.property == second.property &&
|
||||
first.is_unique == second.is_unique;
|
||||
}
|
||||
|
||||
bool operator==(const DatabaseState &first, const DatabaseState &second) {
|
||||
return first.vertices == second.vertices && first.edges == second.edges;
|
||||
return first.vertices == second.vertices && first.edges == second.edges &&
|
||||
first.indices == second.indices;
|
||||
}
|
||||
|
||||
// Returns next query if the end is not reached, otherwise returns an empty
|
||||
@ -166,7 +193,14 @@ class DatabaseEnvironment {
|
||||
edges.insert({from, to, edge_type_name, props});
|
||||
}
|
||||
|
||||
return {vertices, edges};
|
||||
// Capture all indices
|
||||
std::set<DatabaseState::IndexKey> indices;
|
||||
for (const auto &key : dba.GetIndicesKeys()) {
|
||||
indices.insert({dba.LabelName(key.label_),
|
||||
dba.PropertyName(key.property_), key.unique_});
|
||||
}
|
||||
|
||||
return {vertices, edges, indices};
|
||||
}
|
||||
|
||||
private:
|
||||
@ -186,8 +220,10 @@ TEST(DumpTest, SingleVertex) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -197,8 +233,10 @@ TEST(DumpTest, VertexWithSingleLabel) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:Label1 {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__:Label1 {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -208,8 +246,11 @@ TEST(DumpTest, VertexWithMultipleLabels) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:Label1:Label2 {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"CREATE (:__mg_vertex__:Label1:Label2 {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -219,8 +260,11 @@ TEST(DumpTest, VertexWithSingleProperty) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0, prop: 42});");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"CREATE (:__mg_vertex__ {__mg_id__: 0, prop: 42});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -232,10 +276,12 @@ TEST(DumpTest, MultipleVertices) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 2});");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 2});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -247,12 +293,14 @@ TEST(DumpTest, SingleEdge) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"MATCH (u), (v) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE "
|
||||
"(u)-[:EdgeType]->(v);");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -267,9 +315,10 @@ TEST(DumpTest, MultipleEdges) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 2});");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 2});");
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"MATCH (u), (v) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE "
|
||||
"(u)-[:EdgeType]->(v);");
|
||||
@ -279,7 +328,8 @@ TEST(DumpTest, MultipleEdges) {
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"MATCH (u), (v) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE "
|
||||
"(u)-[:EdgeType]->(v);");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -291,12 +341,33 @@ TEST(DumpTest, EdgeWithProperties) {
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE ({__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 0});");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE (:__mg_vertex__ {__mg_id__: 1});");
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"MATCH (u), (v) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE "
|
||||
"(u)-[:EdgeType {prop: 13}]->(v);");
|
||||
EXPECT_EQ(DumpNext(&dump), "MATCH (u) REMOVE u.__mg_id__;");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(hicpp-special-member-functions)
|
||||
TEST(DumpTest, IndicesKeys) {
|
||||
DatabaseEnvironment db;
|
||||
db.CreateVertex({"Label1", "Label2"}, {{"prop", PropertyValue(10)}}, false);
|
||||
db.Execute("CREATE INDEX ON :Label1(prop);");
|
||||
db.Execute("CREATE UNIQUE INDEX ON :Label2(prop);");
|
||||
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE INDEX ON :Label1(prop);");
|
||||
EXPECT_EQ(DumpNext(&dump), "CREATE UNIQUE INDEX ON :Label2(prop);");
|
||||
EXPECT_EQ(DumpNext(&dump), kCreateInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump),
|
||||
"CREATE (:__mg_vertex__:Label1:Label2 {__mg_id__: 0, prop: 10});");
|
||||
EXPECT_EQ(DumpNext(&dump), kDropInternalIndex);
|
||||
EXPECT_EQ(DumpNext(&dump), kRemoveInternalLabelProperty);
|
||||
EXPECT_EQ(DumpNext(&dump), "");
|
||||
}
|
||||
|
||||
@ -331,7 +402,11 @@ TEST(DumpTest, CheckStateSimpleGraph) {
|
||||
db.CreateEdge(z, u, "Knows", {});
|
||||
db.CreateEdge(w, z, "Knows", {{"how", "school"}});
|
||||
db.CreateEdge(w, z, "Likes", {{"how", "very much"}});
|
||||
// Create few indices
|
||||
db.Execute("CREATE UNIQUE INDEX ON :Person(name);");
|
||||
db.Execute("CREATE INDEX ON :Person(unexisting_property);");
|
||||
|
||||
const auto &db_initial_state = db.GetState();
|
||||
DatabaseEnvironment db_dump;
|
||||
auto dba = db.Access();
|
||||
CypherDumpGenerator dump(&dba);
|
||||
@ -340,4 +415,6 @@ TEST(DumpTest, CheckStateSimpleGraph) {
|
||||
db_dump.Execute(cmd);
|
||||
}
|
||||
EXPECT_EQ(db.GetState(), db_dump.GetState());
|
||||
// Make sure that dump function doesn't make changes on the database.
|
||||
EXPECT_EQ(db.GetState(), db_initial_state);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user