List Pattern Comprehension planner (#1686)
This commit is contained in:
committed by
GitHub
parent
02325f8673
commit
a099417c56
@@ -291,3 +291,45 @@ Feature: List operators
|
||||
# Then the result should be:
|
||||
# | years |
|
||||
# | [2021,2003,2003,1999] |
|
||||
|
||||
Scenario: Multiple entries with list pattern comprehension
|
||||
Given graph "graph_keanu"
|
||||
When executing query:
|
||||
"""
|
||||
MATCH (n:Person)
|
||||
RETURN n.name, [(n)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years
|
||||
"""
|
||||
Then an error should be raised
|
||||
|
||||
Scenario: Multiple list pattern comprehensions in Return
|
||||
Given graph "graph_keanu"
|
||||
When executing query:
|
||||
"""
|
||||
MATCH (n:Person)
|
||||
RETURN n.name,
|
||||
[(n)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.released] AS years,
|
||||
[(n)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | b.title] AS titles
|
||||
"""
|
||||
Then an error should be raised
|
||||
|
||||
Scenario: Function inside pattern comprehension's expression
|
||||
Given graph "graph_keanu"
|
||||
When executing query:
|
||||
"""
|
||||
MATCH (keanu:Person {name: 'Keanu Reeves'})
|
||||
RETURN [p = (keanu)-->(b:Movie) WHERE b.title CONTAINS 'Matrix' | size(nodes(p))] AS nodes
|
||||
"""
|
||||
Then an error should be raised
|
||||
|
||||
Scenario: Multiple list pattern comprehensions in With
|
||||
Given graph "graph_keanu"
|
||||
When executing query:
|
||||
"""
|
||||
MATCH (n) WHERE size(n.name) > 5
|
||||
WITH
|
||||
n AS actor,
|
||||
[(n)-->(m) WHERE m.released > 2000 | m.title] AS titles,
|
||||
[(n)-->(m) WHERE m.released > 2000 | m.released] AS years
|
||||
RETURN actor.name, years, titles;
|
||||
"""
|
||||
Then an error should be raised
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
CREATE
|
||||
(keanu:Person {name: 'Keanu Reeves'}),
|
||||
(trinity:Person {name: 'Carrie-Anne Moss'}),
|
||||
(morpheus:Person {name: 'Laurence Fishburne'}),
|
||||
(johnnyMnemonic:Movie {title: 'Johnny Mnemonic', released: 1995}),
|
||||
(theMatrixRevolutions:Movie {title: 'The Matrix Revolutions', released: 2003}),
|
||||
(theMatrixReloaded:Movie {title: 'The Matrix Reloaded', released: 2003}),
|
||||
@@ -13,4 +15,7 @@ CREATE
|
||||
(keanu)-[:ACTED_IN]->(theReplacements),
|
||||
(keanu)-[:ACTED_IN]->(theMatrix),
|
||||
(keanu)-[:ACTED_IN]->(theDevilsAdvocate),
|
||||
(keanu)-[:ACTED_IN]->(theMatrixResurrections);
|
||||
(keanu)-[:ACTED_IN]->(theMatrixResurrections),
|
||||
(trinity)-[:ACTED_IN]->(theMatrix),
|
||||
(trinity)-[:ACTED_IN]->(theMatrixReloaded),
|
||||
(morpheus)-[:ACTED_IN]->(theMatrix);
|
||||
|
||||
@@ -4624,3 +4624,101 @@ TEST_P(CypherMainVisitorTest, CallSubquery) {
|
||||
ASSERT_TRUE(nested_match);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_P(CypherMainVisitorTest, PatternComprehension) {
|
||||
auto &ast_generator = *GetParam();
|
||||
{
|
||||
const auto *query =
|
||||
dynamic_cast<CypherQuery *>(ast_generator.ParseQuery("MATCH (n) RETURN [(n)-->(b) | b.val] AS res;"));
|
||||
const auto *ret = dynamic_cast<Return *>(query->single_query_->clauses_[1]);
|
||||
|
||||
const auto *pc = dynamic_cast<PatternComprehension *>(ret->body_.named_expressions[0]->expression_);
|
||||
ASSERT_TRUE(pc);
|
||||
|
||||
// Check for variable_
|
||||
EXPECT_EQ(pc->variable_, nullptr);
|
||||
|
||||
// Check for pattern_
|
||||
const auto pattern = pc->pattern_;
|
||||
ASSERT_TRUE(pattern->atoms_.size() == 3);
|
||||
|
||||
const auto *node1 = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
|
||||
const auto *edge = dynamic_cast<EdgeAtom *>(pattern->atoms_[1]);
|
||||
const auto *node2 = dynamic_cast<NodeAtom *>(pattern->atoms_[2]);
|
||||
|
||||
ASSERT_TRUE(node1);
|
||||
ASSERT_TRUE(edge);
|
||||
ASSERT_TRUE(node2);
|
||||
|
||||
// Check for filter_
|
||||
EXPECT_EQ(pc->filter_, nullptr);
|
||||
|
||||
// Check for resultExpr_
|
||||
const auto *result_expr = pc->resultExpr_;
|
||||
ASSERT_TRUE(result_expr);
|
||||
}
|
||||
{
|
||||
const auto *query = dynamic_cast<CypherQuery *>(
|
||||
ast_generator.ParseQuery("MATCH (n) RETURN [(n)-->(b) WHERE b.id=1 | b.val] AS res;"));
|
||||
const auto *ret = dynamic_cast<Return *>(query->single_query_->clauses_[1]);
|
||||
|
||||
const auto *pc = dynamic_cast<PatternComprehension *>(ret->body_.named_expressions[0]->expression_);
|
||||
ASSERT_TRUE(pc);
|
||||
|
||||
// Check for variable_
|
||||
EXPECT_EQ(pc->variable_, nullptr);
|
||||
|
||||
// Check for pattern_
|
||||
const auto pattern = pc->pattern_;
|
||||
ASSERT_TRUE(pattern->atoms_.size() == 3);
|
||||
|
||||
const auto *node1 = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
|
||||
const auto *edge = dynamic_cast<EdgeAtom *>(pattern->atoms_[1]);
|
||||
const auto *node2 = dynamic_cast<NodeAtom *>(pattern->atoms_[2]);
|
||||
|
||||
ASSERT_TRUE(node1);
|
||||
ASSERT_TRUE(edge);
|
||||
ASSERT_TRUE(node2);
|
||||
|
||||
// Check for filter_
|
||||
const auto *filter = pc->filter_;
|
||||
ASSERT_TRUE(filter);
|
||||
ASSERT_TRUE(filter->expression_);
|
||||
|
||||
// Check for resultExpr_
|
||||
const auto *result_expr = pc->resultExpr_;
|
||||
ASSERT_TRUE(result_expr);
|
||||
}
|
||||
{
|
||||
const auto *query = dynamic_cast<CypherQuery *>(
|
||||
ast_generator.ParseQuery("MATCH (n) RETURN [p = (n)-->(b) WHERE b.id=1 | b.val] AS res;"));
|
||||
const auto *ret = dynamic_cast<Return *>(query->single_query_->clauses_[1]);
|
||||
|
||||
const auto *pc = dynamic_cast<PatternComprehension *>(ret->body_.named_expressions[0]->expression_);
|
||||
ASSERT_TRUE(pc);
|
||||
|
||||
// Check for variable_
|
||||
ASSERT_TRUE(pc->variable_);
|
||||
|
||||
// Check for pattern_
|
||||
const auto pattern = pc->pattern_;
|
||||
ASSERT_TRUE(pattern->atoms_.size() == 3);
|
||||
|
||||
const auto *node1 = dynamic_cast<NodeAtom *>(pattern->atoms_[0]);
|
||||
const auto *edge = dynamic_cast<EdgeAtom *>(pattern->atoms_[1]);
|
||||
const auto *node2 = dynamic_cast<NodeAtom *>(pattern->atoms_[2]);
|
||||
|
||||
ASSERT_TRUE(node1);
|
||||
ASSERT_TRUE(edge);
|
||||
ASSERT_TRUE(node2);
|
||||
|
||||
// Check for filter_
|
||||
const auto *filter = pc->filter_;
|
||||
ASSERT_TRUE(filter);
|
||||
ASSERT_TRUE(filter->expression_);
|
||||
|
||||
// Check for resultExpr_
|
||||
const auto *result_expr = pc->resultExpr_;
|
||||
ASSERT_TRUE(result_expr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// Use of this software is governed by the Business Source License
|
||||
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
|
||||
@@ -635,3 +635,5 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
|
||||
#define DROP_USER(usernames) storage.Create<memgraph::query::DropUser>((usernames))
|
||||
#define CALL_PROCEDURE(...) memgraph::query::test_common::GetCallProcedure(storage, __VA_ARGS__)
|
||||
#define CALL_SUBQUERY(...) memgraph::query::test_common::GetCallSubquery(this->storage, __VA_ARGS__)
|
||||
#define PATTERN_COMPREHENSION(variable, pattern, filter, resultExpr) \
|
||||
this->storage.template Create<memgraph::query::PatternComprehension>(variable, pattern, filter, resultExpr)
|
||||
|
||||
@@ -1442,3 +1442,27 @@ TYPED_TEST(TestSymbolGenerator, PropertyCachingMixedLookups2) {
|
||||
ASSERT_TRUE(prop3_eval_mode == PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES);
|
||||
ASSERT_TRUE(prop4_eval_mode == PropertyLookup::EvaluationMode::GET_ALL_PROPERTIES);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestSymbolGenerator, PatternComprehension) {
|
||||
auto prop = this->dba.NameToProperty("prop");
|
||||
|
||||
// MATCH (n) RETURN [(n)-[edge]->(m) | m.prop] AS alias
|
||||
auto query = QUERY(SINGLE_QUERY(
|
||||
MATCH(PATTERN(NODE("n"))),
|
||||
RETURN(NEXPR("alias", PATTERN_COMPREHENSION(nullptr,
|
||||
PATTERN(NODE("n"), EDGE("edge", EdgeAtom::Direction::BOTH, {}, false),
|
||||
NODE("m", std::nullopt, false)),
|
||||
nullptr, PROPERTY_LOOKUP(this->dba, "m", prop))))));
|
||||
|
||||
auto symbol_table = MakeSymbolTable(query);
|
||||
ASSERT_EQ(symbol_table.max_position(), 7);
|
||||
|
||||
memgraph::query::plan::UsedSymbolsCollector collector(symbol_table);
|
||||
auto *ret = dynamic_cast<Return *>(query->single_query_->clauses_[1]);
|
||||
auto *pc = dynamic_cast<PatternComprehension *>(ret->body_.named_expressions[0]->expression_);
|
||||
|
||||
pc->Accept(collector);
|
||||
|
||||
// n, edge, m, Path
|
||||
ASSERT_EQ(collector.symbols_.size(), 4);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user