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:
Teon Banek 2019-11-12 15:41:16 +01:00
parent 9c095501d8
commit edaa03a960
8 changed files with 73 additions and 23 deletions

View File

@ -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;
}

View File

@ -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 ;

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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")));

View File

@ -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_) {