Generate All to filter variable length paths

Summary:
Use All to filter variable length paths.
Test filtered variable length paths in qa

Reviewers: florijan, mislav.bradac

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D592
This commit is contained in:
Teon Banek 2017-07-26 11:09:43 +02:00
parent 9ae1a9a585
commit 9e157eb495
4 changed files with 91 additions and 25 deletions

View File

@ -60,7 +60,7 @@ class Filters {
/// for found labels, properties and edge types. The generated expressions are
/// stored in @c all_filters. Also, @c label_filters and @c property_filters
/// are populated.
void CollectPatternFilters(Pattern &, const SymbolTable &, AstTreeStorage &);
void CollectPatternFilters(Pattern &, SymbolTable &, AstTreeStorage &);
/// Collects filtering information from a where expression.
///
/// Takes the where expression and stores it in @c all_filters, then analyzes
@ -203,8 +203,9 @@ class VariableStartPlanner {
///
/// This function will normalize patterns inside @c Match and @c Merge clauses
/// and do some other preprocessing in order to generate multiple @c QueryPart
/// structures.
std::vector<QueryPart> CollectQueryParts(const SymbolTable &, AstTreeStorage &);
/// structures. @c AstTreeStorage and @c SymbolTable may be used to create new
/// AST nodes.
std::vector<QueryPart> CollectQueryParts(SymbolTable &, AstTreeStorage &);
/// @brief Generates the LogicalOperator tree and returns the resulting plan.
///

View File

@ -62,7 +62,7 @@ auto ReducePattern(
return last_res;
}
void ForeachPattern(
void ForEachPattern(
Pattern &pattern, std::function<void(NodeAtom *)> base,
std::function<void(NodeAtom *, EdgeAtom *, NodeAtom *)> collect) {
debug_assert(!pattern.atoms_.empty(), "Missing atoms in pattern");
@ -643,7 +643,7 @@ std::vector<Expansion> NormalizePatterns(
debug_assert(node, "First pattern atom is not a node");
expansions.emplace_back(Expansion{node});
} else {
ForeachPattern(*pattern, ignore_node, collect_expansion);
ForEachPattern(*pattern, ignore_node, collect_expansion);
}
}
return expansions;
@ -655,7 +655,7 @@ std::vector<Expansion> NormalizePatterns(
// will lift them out of a pattern and generate new expressions (just like they
// were in a Where clause).
void AddMatching(const std::vector<Pattern *> &patterns, Where *where,
const SymbolTable &symbol_table, AstTreeStorage &storage,
SymbolTable &symbol_table, AstTreeStorage &storage,
Matching &matching) {
auto expansions = NormalizePatterns(symbol_table, patterns);
std::unordered_set<Symbol> edge_symbols;
@ -676,7 +676,7 @@ void AddMatching(const std::vector<Pattern *> &patterns, Where *where,
matching.filters.CollectWhereFilter(*where, symbol_table);
}
}
void AddMatching(const Match &match, const SymbolTable &symbol_table,
void AddMatching(const Match &match, SymbolTable &symbol_table,
AstTreeStorage &storage, Matching &matching) {
return AddMatching(match.patterns_, match.where_, symbol_table, storage,
matching);
@ -1015,25 +1015,39 @@ void Filters::AnalyzeFilter(Expression *expr, const SymbolTable &symbol_table) {
// as `expr1 < n.prop AND n.prop < expr2`.
}
void Filters::CollectPatternFilters(Pattern &pattern,
const SymbolTable &symbol_table,
void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
AstTreeStorage &storage) {
UsedSymbolsCollector collector(symbol_table);
auto add_properties_filter = [&](auto *atom) {
auto add_properties_filter = [&](auto *atom, bool is_variable_path = false) {
const auto &symbol = symbol_table.at(*atom->identifier_);
for (auto &prop_pair : atom->properties_) {
collector.symbols_.clear();
prop_pair.second->Accept(collector);
// Store a PropertyFilter on the value of the property.
property_filters_[symbol][prop_pair.first].emplace_back(
PropertyFilter{collector.symbols_, prop_pair.second});
auto *identifier = atom->identifier_;
if (is_variable_path) {
// Create a new identifier and a symbol which will be filled in All.
identifier = identifier->Clone(storage);
symbol_table[*identifier] =
symbol_table.CreateSymbol(identifier->name_, false);
} else {
// Store a PropertyFilter on the value of the property.
property_filters_[symbol][prop_pair.first].emplace_back(
PropertyFilter{collector.symbols_, prop_pair.second});
}
// Create an equality expression and store it in all_filters_.
auto *property_lookup =
storage.Create<PropertyLookup>(atom->identifier_, prop_pair.first);
storage.Create<PropertyLookup>(identifier, prop_pair.first);
auto *prop_equal =
storage.Create<EqualOperator>(property_lookup, prop_pair.second);
collector.symbols_.insert(symbol); // PropertyLookup uses the symbol.
all_filters_.emplace_back(prop_equal, collector.symbols_);
if (is_variable_path) {
all_filters_.emplace_back(
storage.Create<All>(identifier, atom->identifier_,
storage.Create<Where>(prop_equal)),
collector.symbols_);
} else {
all_filters_.emplace_back(prop_equal, collector.symbols_);
}
}
};
auto add_node_filter = [&](NodeAtom *node) {
@ -1052,19 +1066,28 @@ void Filters::CollectPatternFilters(Pattern &pattern,
auto add_expand_filter = [&](NodeAtom *prev_node, EdgeAtom *edge,
NodeAtom *node) {
const auto &edge_symbol = symbol_table.at(*edge->identifier_);
if (edge->has_range_ &&
(!edge->edge_types_.empty() || !edge->properties_.empty())) {
throw utils::NotYetImplemented("filtering variable length paths");
}
if (!edge->edge_types_.empty()) {
all_filters_.emplace_back(
storage.Create<EdgeTypeTest>(edge->identifier_, edge->edge_types_),
std::unordered_set<Symbol>{edge_symbol});
if (edge->has_range_) {
// We need a new identifier and symbol for All.
auto *identifier = edge->identifier_->Clone(storage);
symbol_table[*identifier] =
symbol_table.CreateSymbol(identifier->name_, false);
auto *edge_type_test =
storage.Create<EdgeTypeTest>(identifier, edge->edge_types_);
all_filters_.emplace_back(
storage.Create<All>(identifier, edge->identifier_,
storage.Create<Where>(edge_type_test)),
std::unordered_set<Symbol>{edge_symbol});
} else {
all_filters_.emplace_back(
storage.Create<EdgeTypeTest>(edge->identifier_, edge->edge_types_),
std::unordered_set<Symbol>{edge_symbol});
}
}
add_properties_filter(edge);
add_properties_filter(edge, edge->has_range_);
add_node_filter(node);
};
ForeachPattern(pattern, add_node_filter, add_expand_filter);
ForEachPattern(pattern, add_node_filter, add_expand_filter);
}
// Adds the where filter expression to all filters and collects additional
@ -1079,7 +1102,7 @@ void Filters::CollectWhereFilter(Where &where,
// Converts a Query to multiple QueryParts. In the process new Ast nodes may be
// created, e.g. filter expressions.
std::vector<QueryPart> CollectQueryParts(const SymbolTable &symbol_table,
std::vector<QueryPart> CollectQueryParts(SymbolTable &symbol_table,
AstTreeStorage &storage) {
auto query = storage.query();
std::vector<QueryPart> query_parts(1);

View File

@ -457,3 +457,30 @@ Feature: Match
| 2 | 3 |
| 3 | 3 |
Scenario: Test match filtered edge type variable path
Given an empty graph
And having executed:
"""
CREATE ({a: 1}) -[:r1]-> ({a:2}) -[:r2]-> ({a:3})
"""
When executing query:
"""
MATCH (n) -[:r1*]-> (m) RETURN n.a, m.a
"""
Then the result should be:
| n.a | m.a |
| 1 | 2 |
Scenario: Test match filtered properties variable path
Given an empty graph
And having executed:
"""
CREATE ({a: 1}) -[:r {p1: 1, p2: 2}]-> ({a:2}) -[:r {p1: 1, p2: 3}]-> ({a:3})
"""
When executing query:
"""
MATCH (n) -[*{p1: 1, p2:2}]-> (m) RETURN n.a, m.a
"""
Then the result should be:
| n.a | m.a |
| 1 | 2 |

View File

@ -1236,4 +1236,19 @@ TEST(TestLogicalPlanner, MatchExpandVariableNoBounds) {
CheckPlan(storage, ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
}
TEST(TestLogicalPlanner, MatchExpandVariableFiltered) {
// Test MATCH (n) -[r :type * {prop: 42}]-> (m) RETURN r
Dbms dbms;
auto dba = dbms.active();
auto type = dba->edge_type("type");
auto prop = dba->property("prop");
AstTreeStorage storage;
auto edge = EDGE("r", type);
edge->has_range_ = true;
edge->properties_[prop] = LITERAL(42);
QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r"));
CheckPlan(storage, ExpectScanAll(), ExpectExpandVariable(), ExpectFilter(),
ExpectProduce());
}
} // namespace