Expose explicit Label index creation through Cypher
Reviewers: mferencevic, ipaljak Reviewed By: mferencevic Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2547
This commit is contained in:
parent
9c095501d8
commit
edaa03a960
@ -154,8 +154,10 @@ antlrcpp::Any CypherMainVisitor::visitCreateIndex(
|
||||
auto *index_query = storage_->Create<IndexQuery>();
|
||||
index_query->action_ = IndexQuery::Action::CREATE;
|
||||
index_query->label_ = AddLabel(ctx->labelName()->accept(this));
|
||||
PropertyIx name_key = ctx->propertyKeyName()->accept(this);
|
||||
index_query->properties_ = {name_key};
|
||||
if (ctx->propertyKeyName()) {
|
||||
PropertyIx name_key = ctx->propertyKeyName()->accept(this);
|
||||
index_query->properties_ = {name_key};
|
||||
}
|
||||
return index_query;
|
||||
}
|
||||
|
||||
@ -163,8 +165,10 @@ antlrcpp::Any CypherMainVisitor::visitDropIndex(
|
||||
MemgraphCypher::DropIndexContext *ctx) {
|
||||
auto *index_query = storage_->Create<IndexQuery>();
|
||||
index_query->action_ = IndexQuery::Action::DROP;
|
||||
PropertyIx key = ctx->propertyKeyName()->accept(this);
|
||||
index_query->properties_ = {key};
|
||||
if (ctx->propertyKeyName()) {
|
||||
PropertyIx key = ctx->propertyKeyName()->accept(this);
|
||||
index_query->properties_ = {key};
|
||||
}
|
||||
index_query->label_ = AddLabel(ctx->labelName()->accept(this));
|
||||
return index_query;
|
||||
}
|
||||
|
@ -306,9 +306,9 @@ integerLiteral : DecimalLiteral
|
||||
| HexadecimalLiteral
|
||||
;
|
||||
|
||||
createIndex : CREATE INDEX ON ':' labelName '(' propertyKeyName ')' ;
|
||||
createIndex : CREATE INDEX ON ':' labelName ( '(' propertyKeyName ')' )? ;
|
||||
|
||||
dropIndex : DROP INDEX ON ':' labelName '(' propertyKeyName ')' ;
|
||||
dropIndex : DROP INDEX ON ':' labelName ( '(' propertyKeyName ')' )? ;
|
||||
|
||||
doubleLiteral : FloatingLiteral ;
|
||||
|
||||
|
@ -850,15 +850,21 @@ PreparedQuery PrepareIndexQuery(
|
||||
#ifdef MG_SINGLE_NODE_V2
|
||||
handler = [interpreter_context, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
CHECK(properties.size() == 1);
|
||||
interpreter_context->db->CreateIndex(label, properties[0]);
|
||||
if (properties.empty()) {
|
||||
interpreter_context->db->CreateIndex(label);
|
||||
} else {
|
||||
CHECK(properties.size() == 1U);
|
||||
interpreter_context->db->CreateIndex(label, properties[0]);
|
||||
}
|
||||
invalidate_plan_cache();
|
||||
};
|
||||
#else
|
||||
handler = [dba, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
// Old storage creates label index by default.
|
||||
if (properties.empty()) return;
|
||||
try {
|
||||
CHECK(properties.size() == 1);
|
||||
CHECK(properties.size() == 1U);
|
||||
dba->CreateIndex(label, properties[0]);
|
||||
invalidate_plan_cache();
|
||||
} catch (const database::ConstraintViolationException &e) {
|
||||
@ -876,15 +882,21 @@ PreparedQuery PrepareIndexQuery(
|
||||
#ifdef MG_SINGLE_NODE_V2
|
||||
handler = [interpreter_context, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
CHECK(properties.size() == 1);
|
||||
interpreter_context->db->DropIndex(label, properties[0]);
|
||||
if (properties.empty()) {
|
||||
interpreter_context->db->DropIndex(label);
|
||||
} else {
|
||||
CHECK(properties.size() == 1U);
|
||||
interpreter_context->db->DropIndex(label, properties[0]);
|
||||
}
|
||||
invalidate_plan_cache();
|
||||
};
|
||||
#else
|
||||
handler = [dba, label, properties = std::move(properties),
|
||||
invalidate_plan_cache = std::move(invalidate_plan_cache)] {
|
||||
if (properties.empty())
|
||||
throw QueryRuntimeException("Label index cannot be dropped!");
|
||||
try {
|
||||
CHECK(properties.size() == 1);
|
||||
CHECK(properties.size() == 1U);
|
||||
dba->DropIndex(label, properties[0]);
|
||||
invalidate_plan_cache();
|
||||
} catch (const database::TransactionException &e) {
|
||||
|
@ -435,14 +435,22 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
return db_->NameToProperty(prop.name);
|
||||
}
|
||||
|
||||
LabelIx FindBestLabelIndex(const std::unordered_set<LabelIx> &labels) {
|
||||
std::optional<LabelIx> FindBestLabelIndex(
|
||||
const std::unordered_set<LabelIx> &labels) {
|
||||
CHECK(!labels.empty())
|
||||
<< "Trying to find the best label without any labels.";
|
||||
return *std::min_element(labels.begin(), labels.end(),
|
||||
[this](const auto &label1, const auto &label2) {
|
||||
return db_->VerticesCount(GetLabel(label1)) <
|
||||
db_->VerticesCount(GetLabel(label2));
|
||||
});
|
||||
std::optional<LabelIx> best_label;
|
||||
for (const auto &label : labels) {
|
||||
if (!db_->LabelIndexExists(GetLabel(label))) continue;
|
||||
if (!best_label) {
|
||||
best_label = label;
|
||||
continue;
|
||||
}
|
||||
if (db_->VerticesCount(GetLabel(label)) <
|
||||
db_->VerticesCount(GetLabel(*best_label)))
|
||||
best_label = label;
|
||||
}
|
||||
return best_label;
|
||||
}
|
||||
|
||||
// Finds the label-property combination which has indexed the lowest amount of
|
||||
@ -560,7 +568,9 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
prop_filter.value_, view);
|
||||
}
|
||||
}
|
||||
auto label = FindBestLabelIndex(labels);
|
||||
auto maybe_label = FindBestLabelIndex(labels);
|
||||
if (!maybe_label) return nullptr;
|
||||
const auto &label = *maybe_label;
|
||||
if (max_vertex_count &&
|
||||
db_->VerticesCount(GetLabel(label)) > *max_vertex_count) {
|
||||
// Don't create an indexed lookup, since we have more labeled vertices
|
||||
|
@ -69,6 +69,10 @@ class VertexCountCache {
|
||||
return bounds_vertex_count.at(bounds);
|
||||
}
|
||||
|
||||
bool LabelIndexExists(storage::Label label) {
|
||||
return db_->LabelIndexExists(label);
|
||||
}
|
||||
|
||||
bool LabelPropertyIndexExists(storage::Label label,
|
||||
storage::Property property) {
|
||||
return db_->LabelPropertyIndexExists(label, property);
|
||||
|
@ -200,6 +200,8 @@ class InteractiveDbAccessor {
|
||||
"' in range " + range_string.str());
|
||||
}
|
||||
|
||||
bool LabelIndexExists(storage::Label label) { return true; }
|
||||
|
||||
bool LabelPropertyIndexExists(storage::Label label_id,
|
||||
storage::Property property_id) {
|
||||
auto label = dba_->LabelName(label_id);
|
||||
|
@ -167,10 +167,21 @@ TYPED_TEST(TestPlanner, MatchLabeledNodes) {
|
||||
auto *as_n = NEXPR("n", IDENT("n"));
|
||||
auto *query =
|
||||
QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", label))), RETURN(as_n)));
|
||||
auto symbol_table = query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(),
|
||||
ExpectProduce());
|
||||
{
|
||||
// Without created label index
|
||||
auto symbol_table = query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(),
|
||||
ExpectProduce());
|
||||
}
|
||||
{
|
||||
// With created label index
|
||||
dba.SetIndexCount(dba.Label(label), 0);
|
||||
auto symbol_table = query::MakeSymbolTable(query);
|
||||
auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
|
||||
CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(),
|
||||
ExpectProduce());
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST(TestPlanner, MatchPathReturn) {
|
||||
@ -1130,6 +1141,7 @@ TYPED_TEST(TestPlanner, UnableToUsePropertyIndex) {
|
||||
FakeDbAccessor dba;
|
||||
auto label = dba.Label("label");
|
||||
auto property = dba.Property("property");
|
||||
dba.SetIndexCount(label, 0);
|
||||
dba.SetIndexCount(label, property, 0);
|
||||
AstStorage storage;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
@ -1149,6 +1161,7 @@ TYPED_TEST(TestPlanner, SecondPropertyIndex) {
|
||||
FakeDbAccessor dba;
|
||||
auto label = dba.Label("label");
|
||||
auto property = PROPERTY_PAIR("property");
|
||||
dba.SetIndexCount(label, 0);
|
||||
dba.SetIndexCount(label, dba.Property("property"), 0);
|
||||
AstStorage storage;
|
||||
auto n_prop = PROPERTY_LOOKUP("n", property);
|
||||
@ -1296,6 +1309,7 @@ TYPED_TEST(TestPlanner, MatchDoubleScanToExpandExisting) {
|
||||
// Test MATCH (n) -[r]- (m :label) RETURN r
|
||||
FakeDbAccessor dba;
|
||||
auto label = "label";
|
||||
dba.SetIndexCount(dba.Label(label), 0);
|
||||
AstStorage storage;
|
||||
auto *query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m", label))), RETURN("r")));
|
||||
|
@ -397,6 +397,10 @@ class FakeDbAccessor {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool LabelIndexExists(storage::Label label) const {
|
||||
return label_index_.find(label) != label_index_.end();
|
||||
}
|
||||
|
||||
bool LabelPropertyIndexExists(storage::Label label,
|
||||
storage::Property property) const {
|
||||
for (auto &index : label_property_index_) {
|
||||
|
Loading…
Reference in New Issue
Block a user