diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp index 35bbdd5cd..ddeb9a05e 100644 --- a/src/query/frontend/ast/ast.hpp +++ b/src/query/frontend/ast/ast.hpp @@ -1044,10 +1044,16 @@ class EdgeAtom : public PatternAtom { using PatternAtom::PatternAtom; EdgeAtom(int uid, Identifier *identifier, Direction direction) : PatternAtom(uid, identifier), direction_(direction) {} + EdgeAtom(int uid, Identifier *identifier, Direction direction, + const std::vector<GraphDbTypes::EdgeType> &edge_types) + : PatternAtom(uid, identifier), + direction_(direction), + edge_types_(edge_types) {} }; class BreadthFirstAtom : public EdgeAtom { - // TODO: Reconsider inheriting from EdgeAtom, since only `direction_` is used. + // TODO: Reconsider inheriting from EdgeAtom, since only `direction_` and + // `edge_types_` are used. friend class AstTreeStorage; public: @@ -1064,7 +1070,7 @@ class BreadthFirstAtom : public EdgeAtom { BreadthFirstAtom *Clone(AstTreeStorage &storage) const override { return storage.Create<BreadthFirstAtom>( - identifier_->Clone(storage), direction_, + identifier_->Clone(storage), direction_, edge_types_, traversed_edge_identifier_->Clone(storage), next_node_identifier_->Clone(storage), filter_expression_->Clone(storage), max_depth_->Clone(storage)); @@ -1079,10 +1085,11 @@ class BreadthFirstAtom : public EdgeAtom { protected: using EdgeAtom::EdgeAtom; BreadthFirstAtom(int uid, Identifier *identifier, Direction direction, + const std::vector<GraphDbTypes::EdgeType> &edge_types, Identifier *traversed_edge_identifier, Identifier *next_node_identifier, Expression *filter_expression, Expression *max_depth) - : EdgeAtom(uid, identifier, direction), + : EdgeAtom(uid, identifier, direction, edge_types), traversed_edge_identifier_(traversed_edge_identifier), next_node_identifier_(next_node_identifier), filter_expression_(filter_expression), diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp index 70ed54fb9..247999a73 100644 --- a/src/query/frontend/ast/cypher_main_visitor.cpp +++ b/src/query/frontend/ast/cypher_main_visitor.cpp @@ -428,6 +428,12 @@ antlrcpp::Any CypherMainVisitor::visitRelationshipPattern( auto *bf_atom = dynamic_cast<BreadthFirstAtom *>(edge); std::string traversed_edge_variable = ctx->bfsDetail()->traversed_edge->accept(this); + if (ctx->bfsDetail()->relationshipTypes()) { + bf_atom->edge_types_ = ctx->bfsDetail() + ->relationshipTypes() + ->accept(this) + .as<std::vector<GraphDbTypes::EdgeType>>(); + } bf_atom->traversed_edge_identifier_ = storage_.Create<Identifier>(traversed_edge_variable); std::string next_node_variable = ctx->bfsDetail()->next_node->accept(this); diff --git a/src/query/frontend/opencypher/grammar/Cypher.g4 b/src/query/frontend/opencypher/grammar/Cypher.g4 index ce1c513c0..39bcf405b 100644 --- a/src/query/frontend/opencypher/grammar/Cypher.g4 +++ b/src/query/frontend/opencypher/grammar/Cypher.g4 @@ -123,7 +123,7 @@ relationshipPattern : ( leftArrowHead SP? dash SP? ( bfsDetail | relationshipDet | ( dash SP? ( bfsDetail | relationshipDetail )? SP? dash ) ; -bfsDetail : BFS SP? ( '[' SP? ( bfs_variable=variable SP? )? ']' )? SP? '(' SP? traversed_edge=variable SP? ',' SP? next_node=variable SP? '|' SP? expression SP? ',' SP? expression SP? ')' ; +bfsDetail : BFS SP? ( '[' SP? ( bfs_variable=variable SP? )? ( relationshipTypes SP? )? SP? ']' )? SP? '(' SP? traversed_edge=variable SP? ',' SP? next_node=variable SP? '|' SP? expression SP? ',' SP? expression SP? ')' ; relationshipDetail : '[' SP? ( variable SP? )? ( relationshipTypes SP? )? ( rangeLiteral SP? )? properties SP? ']' | '[' SP? ( variable SP? )? ( relationshipTypes SP? )? ( rangeLiteral SP? )? ( properties SP? )? ']' diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index bb06fba50..485dcde84 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -967,12 +967,14 @@ std::unique_ptr<Cursor> ExpandVariable::MakeCursor(GraphDbAccessor &db) { ExpandBreadthFirst::ExpandBreadthFirst( Symbol node_symbol, Symbol edge_list_symbol, EdgeAtom::Direction direction, - Expression *max_depth, Symbol inner_node_symbol, Symbol inner_edge_symbol, - Expression *where, const std::shared_ptr<LogicalOperator> &input, - Symbol input_symbol, bool existing_node, GraphView graph_view) + const GraphDbTypes::EdgeType &edge_type, Expression *max_depth, + Symbol inner_node_symbol, Symbol inner_edge_symbol, Expression *where, + const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol, + bool existing_node, GraphView graph_view) : node_symbol_(node_symbol), edge_list_symbol_(edge_list_symbol), direction_(direction), + edge_type_(edge_type), max_depth_(max_depth), inner_node_symbol_(inner_node_symbol), inner_edge_symbol_(inner_edge_symbol), @@ -1032,12 +1034,24 @@ bool ExpandBreadthFirst::Cursor::Pull(Frame &frame, // from the given vertex. skips expansions that don't satisfy // the "where" condition. auto expand_from_vertex = [this, &expand_pair](VertexAccessor &vertex) { - if (self_.direction_ != EdgeAtom::Direction::IN) - for (const EdgeAccessor &edge : vertex.out()) - expand_pair(edge, edge.to()); - if (self_.direction_ != EdgeAtom::Direction::OUT) - for (const EdgeAccessor &edge : vertex.in()) - expand_pair(edge, edge.from()); + if (self_.direction_ != EdgeAtom::Direction::IN) { + if (self_.edge_type_) { + for (const EdgeAccessor &edge : vertex.out_with_type(self_.edge_type_)) + expand_pair(edge, edge.to()); + } else { + for (const EdgeAccessor &edge : vertex.out()) + expand_pair(edge, edge.to()); + } + } + if (self_.direction_ != EdgeAtom::Direction::OUT) { + if (self_.edge_type_) { + for (const EdgeAccessor &edge : vertex.in_with_type(self_.edge_type_)) + expand_pair(edge, edge.from()); + } else { + for (const EdgeAccessor &edge : vertex.in()) + expand_pair(edge, edge.from()); + } + } }; // do it all in a loop because we skip some elements diff --git a/src/query/plan/operator.hpp b/src/query/plan/operator.hpp index 40108b6f3..ed7d07989 100644 --- a/src/query/plan/operator.hpp +++ b/src/query/plan/operator.hpp @@ -663,9 +663,10 @@ class ExpandVariable : public LogicalOperator, public ExpandCommon { class ExpandBreadthFirst : public LogicalOperator { public: ExpandBreadthFirst(Symbol node_symbol, Symbol edge_list_symbol, - EdgeAtom::Direction direction, Expression *max_depth, - Symbol inner_node_symbol, Symbol inner_edge_symbol, - Expression *where, + EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, + Expression *max_depth, Symbol inner_node_symbol, + Symbol inner_edge_symbol, Expression *where, const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol, bool existing_node, GraphView graph_view = GraphView::AS_IS); @@ -705,6 +706,7 @@ class ExpandBreadthFirst : public LogicalOperator { const Symbol edge_list_symbol_; const EdgeAtom::Direction direction_; + const GraphDbTypes::EdgeType edge_type_ = nullptr; Expression *max_depth_; // symbols for a single node and edge that are currently getting expanded @@ -1394,7 +1396,8 @@ class OrderBy : public LogicalOperator { // first pair element is the order-by list // second pair is the remember list // the cache is filled and sorted (only on first pair elem) on first Pull - std::vector<std::pair<std::vector<TypedValue>, std::vector<TypedValue>>> cache_; + std::vector<std::pair<std::vector<TypedValue>, std::vector<TypedValue>>> + cache_; // iterator over the cache_, maintains state between Pulls decltype(cache_.begin()) cache_it_ = cache_.begin(); }; diff --git a/src/query/plan/rule_based_planner.cpp b/src/query/plan/rule_based_planner.cpp index 0dc0284c4..599f06d03 100644 --- a/src/query/plan/rule_based_planner.cpp +++ b/src/query/plan/rule_based_planner.cpp @@ -132,15 +132,6 @@ bool HasBoundFilterSymbols(const std::unordered_set<Symbol> &bound_symbols, return true; } -template <class TBoolOperator> -Expression *BoolJoin(AstTreeStorage &storage, Expression *expr1, - Expression *expr2) { - if (expr1 && expr2) { - return storage.Create<TBoolOperator>(expr1, expr2); - } - return expr1 ? expr1 : expr2; -} - // Ast tree visitor which collects the context for a return body. // The return body of WITH and RETURN clauses consists of: // @@ -615,8 +606,8 @@ Expression *ExtractFilters(const std::unordered_set<Symbol> &bound_symbols, filters_it != all_filters.end();) { if (HasBoundFilterSymbols(bound_symbols, *filters_it) && predicate(*filters_it)) { - filter_expr = BoolJoin<FilterAndOperator>(storage, filter_expr, - filters_it->expression); + filter_expr = impl::BoolJoin<FilterAndOperator>(storage, filter_expr, + filters_it->expression); filters_it = all_filters.erase(filters_it); } else { filters_it++; @@ -637,13 +628,13 @@ bool BindSymbol(std::unordered_set<Symbol> &bound_symbols, return insertion.second; } -Expression *FindExpandVariableFilter( +Expression *ExtractMultiExpandFilter( const std::unordered_set<Symbol> &bound_symbols, const Symbol &expands_to_node, std::vector<Filters::FilterInfo> &all_filters, AstTreeStorage &storage) { return ExtractFilters(bound_symbols, all_filters, storage, [&](const auto &filter) { - return filter.is_for_expand_variable && + return filter.is_for_multi_expand && filter.used_symbols.find(expands_to_node) == filter.used_symbols.end(); }); @@ -864,6 +855,10 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table, AstTreeStorage &storage) { UsedSymbolsCollector collector(symbol_table); auto add_properties_filter = [&](auto *atom, bool is_variable_path = false) { + debug_assert( + dynamic_cast<BreadthFirstAtom *>(atom) && atom->properties_.empty() || + !dynamic_cast<BreadthFirstAtom *>(atom), + "Property filters are not supported in BFS"); const auto &symbol = symbol_table.at(*atom->identifier_); for (auto &prop_pair : atom->properties_) { collector.symbols_.clear(); @@ -924,6 +919,14 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table, storage.Create<All>(ident_in_all, edge->identifier_, storage.Create<Where>(edge_type_test)), std::unordered_set<Symbol>{edge_symbol}, true}); + } else if (auto *bf_atom = dynamic_cast<BreadthFirstAtom *>(edge)) { + // BFS filters will be inlined inside the filter expression, so create + // EdgeTypeTest which relies on traversed edge identifier. Set of + // used symbols treats this as the original edge symbol. + all_filters_.emplace_back(FilterInfo{ + storage.Create<EdgeTypeTest>(bf_atom->traversed_edge_identifier_, + bf_atom->edge_types_), + std::unordered_set<Symbol>{edge_symbol}, true}); } else { all_filters_.emplace_back(FilterInfo{ storage.Create<EdgeTypeTest>(edge->identifier_, edge->edge_types_), diff --git a/src/query/plan/rule_based_planner.hpp b/src/query/plan/rule_based_planner.hpp index 9820dba93..1d064c59a 100644 --- a/src/query/plan/rule_based_planner.hpp +++ b/src/query/plan/rule_based_planner.hpp @@ -51,8 +51,9 @@ class Filters { /// Set of used symbols by the filter @c expression. std::unordered_set<Symbol> used_symbols; /// True if the filter is to be applied on multiple expanding edges. - /// This is used to inline filtering in an @c ExpandVariable operator. - bool is_for_expand_variable = false; + /// This is used to inline filtering in an @c ExpandVariable and + /// @c ExpandBreadthFirst operators. + bool is_for_multi_expand = false; }; /// List of FilterInfo objects corresponding to all filter expressions that @@ -220,7 +221,7 @@ bool BindSymbol(std::unordered_set<Symbol> &bound_symbols, // `all_filters`. If the expression uses `expands_to_node`, it is skipped. In // such a case, we cannot cut variable expand short, since filtering may be // satisfied by a node deeper in the path. -Expression *FindExpandVariableFilter( +Expression *ExtractMultiExpandFilter( const std::unordered_set<Symbol> &bound_symbols, const Symbol &expands_to_node, std::vector<Filters::FilterInfo> &all_filters, AstTreeStorage &storage); @@ -249,6 +250,15 @@ LogicalOperator *GenWith(With &with, LogicalOperator *input_op, std::unordered_set<Symbol> &bound_symbols, AstTreeStorage &storage); +template <class TBoolOperator> +Expression *BoolJoin(AstTreeStorage &storage, Expression *expr1, + Expression *expr2) { + if (expr1 && expr2) { + return storage.Create<TBoolOperator>(expr1, expr2); + } + return expr1 ? expr1 : expr2; +} + } // namespace impl /// @brief Planner which uses hardcoded rules to produce operators. @@ -522,14 +532,18 @@ class RuleBasedPlanner { symbol_table.at(*bf_atom->traversed_edge_identifier_); const auto &next_node_symbol = symbol_table.at(*bf_atom->next_node_identifier_); + // Inline BFS edge filtering together with its filter expression. + auto *filter_expr = impl::BoolJoin<FilterAndOperator>( + storage, impl::ExtractMultiExpandFilter( + bound_symbols, node_symbol, all_filters, storage), + bf_atom->filter_expression_); last_op = new ExpandBreadthFirst( - node_symbol, edge_symbol, expansion.direction, + node_symbol, edge_symbol, expansion.direction, edge_type, bf_atom->max_depth_, next_node_symbol, traversed_edge_symbol, - bf_atom->filter_expression_, - std::shared_ptr<LogicalOperator>(last_op), node1_symbol, - existing_node, match_context.graph_view); + filter_expr, std::shared_ptr<LogicalOperator>(last_op), + node1_symbol, existing_node, match_context.graph_view); } else if (expansion.edge->has_range_) { - auto *filter_expr = impl::FindExpandVariableFilter( + auto *filter_expr = impl::ExtractMultiExpandFilter( bound_symbols, node_symbol, all_filters, storage); last_op = new ExpandVariable( node_symbol, edge_symbol, expansion.direction, edge_type, diff --git a/tests/public_benchmark/ldbc/test_cases/queries/query_13.oc b/tests/public_benchmark/ldbc/test_cases/queries/query_13.oc index bd4a8b1f2..2a590934d 100644 --- a/tests/public_benchmark/ldbc/test_cases/queries/query_13.oc +++ b/tests/public_benchmark/ldbc/test_cases/queries/query_13.oc @@ -1,5 +1,5 @@ MATCH (person1:Person {id:"17592186055119"}), (person2:Person {id:"8796093025131"}) -OPTIONAL MATCH (person1)-bfs[r](a, b | type(a) = "KNOWS", 15)-(person2) +OPTIONAL MATCH (person1)-bfs[r:KNOWS](a, b | true , 15)-(person2) RETURN CASE r IS NULL WHEN true THEN -1 diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph_bfs.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph_bfs.feature index bbe9a54f4..10cf6fc5a 100644 --- a/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph_bfs.feature +++ b/tests/qa/tck_engine/tests/memgraph_V1/features/memgraph_bfs.feature @@ -43,3 +43,33 @@ Feature: Bfs Then the result should be: | s | r0 | r2 | | 4 | 0 | 2 | + + Scenario: Test match BFS single edge type filtered + Given an empty graph + And having executed: + """ + CREATE ()-[:r0 {id: 0}]->()-[:r1 {id: 1}]->()-[:r2 {id: 2}]->()-[:r3 {id: 3}]->() + """ + When executing query: + """ + MATCH ()-bfs[r :r0](e, m| true, 10)->(m) + RETURN size(r) AS s, (r[0]).id AS r0 + """ + Then the result should be: + | s | r0 | + | 1 | 0 | + + Scenario: Test match BFS multiple edge types filtered + Given an empty graph + And having executed: + """ + CREATE ()-[:r0 {id: 0}]->()-[:r1 {id: 1}]->()-[:r2 {id: 2}]->()-[:r3 {id: 3}]->() + """ + When executing query: + """ + MATCH ()-bfs[r :r0|:r1](e, m| true, 10)->(m) WHERE size(r) > 1 + RETURN size(r) AS s, (r[0]).id AS r0, (r[1]).id AS r1 + """ + Then the result should be: + | s | r0 | r1 | + | 2 | 0 | 1 | diff --git a/tests/unit/cypher_main_visitor.cpp b/tests/unit/cypher_main_visitor.cpp index 6ec4dbd60..92f853c8b 100644 --- a/tests/unit/cypher_main_visitor.cpp +++ b/tests/unit/cypher_main_visitor.cpp @@ -1493,7 +1493,7 @@ TYPED_TEST(CypherMainVisitorTest, ReturnAll) { TYPED_TEST(CypherMainVisitorTest, MatchBfsReturn) { TypeParam ast_generator( - "MATCH (n) -bfs[r](e, n|e.prop = 42, 10)-> (m) RETURN r"); + "MATCH (n) -bfs[r:type1|type2](e, n|e.prop = 42, 10)-> (m) RETURN r"); auto *query = ast_generator.query_; ASSERT_EQ(query->clauses_.size(), 2U); auto *match = dynamic_cast<Match *>(query->clauses_[0]); @@ -1503,6 +1503,10 @@ TYPED_TEST(CypherMainVisitorTest, MatchBfsReturn) { auto *bfs = dynamic_cast<BreadthFirstAtom *>(match->patterns_[0]->atoms_[1]); ASSERT_TRUE(bfs); EXPECT_EQ(bfs->direction_, EdgeAtom::Direction::OUT); + EXPECT_THAT( + bfs->edge_types_, + UnorderedElementsAre(ast_generator.db_accessor_->EdgeType("type1"), + ast_generator.db_accessor_->EdgeType("type2"))); EXPECT_EQ(bfs->identifier_->name_, "r"); EXPECT_EQ(bfs->traversed_edge_identifier_->name_, "e"); EXPECT_EQ(bfs->next_node_identifier_->name_, "n"); diff --git a/tests/unit/query_cost_estimator.cpp b/tests/unit/query_cost_estimator.cpp index fa983f600..64466e867 100644 --- a/tests/unit/query_cost_estimator.cpp +++ b/tests/unit/query_cost_estimator.cpp @@ -160,7 +160,7 @@ TEST_F(QueryCostEstimator, ExpandVariable) { TEST_F(QueryCostEstimator, ExpandBreadthFirst) { MakeOp<ExpandBreadthFirst>( - NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, Literal(3), + NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr, Literal(3), NextSymbol(), NextSymbol(), Literal(true), last_op_, NextSymbol(), false); EXPECT_COST(CardParam::kExpandBreadthFirst * CostParam::kExpandBreadthFirst); } diff --git a/tests/unit/query_plan_match_filter_return.cpp b/tests/unit/query_plan_match_filter_return.cpp index 9fb41aed4..7e0517bd4 100644 --- a/tests/unit/query_plan_match_filter_return.cpp +++ b/tests/unit/query_plan_match_filter_return.cpp @@ -353,10 +353,9 @@ class QueryPlanExpandVariable : public testing::Test { GraphView graph_view = GraphView::AS_IS, bool is_reverse = false) { auto n_from = MakeScanAll(storage, symbol_table, node_from, input_op); auto filter_op = std::make_shared<Filter>( - n_from.op_, - storage.Create<query::LabelsTest>( - n_from.node_->identifier_, - std::vector<GraphDbTypes::Label>{labels[layer]})); + n_from.op_, storage.Create<query::LabelsTest>( + n_from.node_->identifier_, + std::vector<GraphDbTypes::Label>{labels[layer]})); auto n_to = NODE(node_to); auto n_to_sym = symbol_table.CreateSymbol(node_to, true); @@ -697,9 +696,9 @@ class QueryPlanExpandBreadthFirst : public testing::Test { : symbol_table.CreateSymbol("node", true); auto edge_list_sym = symbol_table.CreateSymbol("edgelist_", true); last_op = std::make_shared<ExpandBreadthFirst>( - node_sym, edge_list_sym, direction, LITERAL(max_depth), inner_node, - inner_edge, where, last_op, n.sym_, existing_node_input != nullptr, - graph_view); + node_sym, edge_list_sym, direction, nullptr, LITERAL(max_depth), + inner_node, inner_edge, where, last_op, n.sym_, + existing_node_input != nullptr, graph_view); Frame frame(symbol_table.max_position()); auto cursor = last_op->MakeCursor(*dba); diff --git a/tests/unit/query_planner.cpp b/tests/unit/query_planner.cpp index 3a689fc6f..ca1b9bc57 100644 --- a/tests/unit/query_planner.cpp +++ b/tests/unit/query_planner.cpp @@ -1297,11 +1297,15 @@ TEST(TestLogicalPlanner, UnwindMatchVariable) { } TEST(TestLogicalPlanner, MatchBreadthFirst) { - // Test MATCH (n) -bfs[r](r, n|n, 10)-> (m) RETURN r + // Test MATCH (n) -bfs[r:type](r, n|n, 10)-> (m) RETURN r + Dbms dbms; + auto dba = dbms.active(); + auto edge_type = dba->EdgeType("type"); AstTreeStorage storage; auto *bfs = storage.Create<query::BreadthFirstAtom>( - IDENT("r"), Direction::OUT, IDENT("r"), IDENT("n"), IDENT("n"), - LITERAL(10)); + IDENT("r"), Direction::OUT, + std::vector<GraphDbTypes::EdgeType>{edge_type}, IDENT("r"), IDENT("n"), + IDENT("n"), LITERAL(10)); QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN("r")); CheckPlan(storage, ExpectScanAll(), ExpectExpandBreadthFirst(), ExpectProduce()); diff --git a/tests/unit/query_semantic.cpp b/tests/unit/query_semantic.cpp index 6292f8404..d07e201d3 100644 --- a/tests/unit/query_semantic.cpp +++ b/tests/unit/query_semantic.cpp @@ -1019,6 +1019,7 @@ TEST(TestSymbolGenerator, MatchBfsReturn) { auto *n_prop = PROPERTY_LOOKUP("n", prop); auto *bfs = storage.Create<BreadthFirstAtom>(IDENT("r"), EdgeAtom::Direction::OUT, + std::vector<GraphDbTypes::EdgeType>{}, IDENT("r"), IDENT("n"), r_prop, n_prop); auto *ret_r = IDENT("r"); auto *query = @@ -1043,7 +1044,8 @@ TEST(TestSymbolGenerator, MatchBfsUsesEdgeSymbolError) { // Test MATCH (n) -bfs[r](e, n | r, 10)-> (m) RETURN r AstTreeStorage storage; auto *bfs = storage.Create<BreadthFirstAtom>( - IDENT("r"), EdgeAtom::Direction::OUT, IDENT("e"), IDENT("n"), IDENT("r"), + IDENT("r"), EdgeAtom::Direction::OUT, + std::vector<GraphDbTypes::EdgeType>{}, IDENT("e"), IDENT("n"), IDENT("r"), LITERAL(10)); auto *query = QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN("r")); SymbolTable symbol_table; @@ -1056,7 +1058,8 @@ TEST(TestSymbolGenerator, MatchBfsUsesPreviousOuterSymbol) { AstTreeStorage storage; auto *node_a = NODE("a"); auto *bfs = storage.Create<BreadthFirstAtom>( - IDENT("r"), EdgeAtom::Direction::OUT, IDENT("e"), IDENT("n"), IDENT("a"), + IDENT("r"), EdgeAtom::Direction::OUT, + std::vector<GraphDbTypes::EdgeType>{}, IDENT("e"), IDENT("n"), IDENT("a"), LITERAL(10)); auto *query = QUERY(MATCH(PATTERN(node_a, bfs, NODE("m"))), RETURN("r")); SymbolTable symbol_table; @@ -1070,7 +1073,8 @@ TEST(TestSymbolGenerator, MatchBfsUsesLaterSymbolError) { // Test MATCH (n) -bfs[r](e, n | m, 10)-> (m) RETURN r AstTreeStorage storage; auto *bfs = storage.Create<BreadthFirstAtom>( - IDENT("r"), EdgeAtom::Direction::OUT, IDENT("e"), IDENT("n"), IDENT("m"), + IDENT("r"), EdgeAtom::Direction::OUT, + std::vector<GraphDbTypes::EdgeType>{}, IDENT("e"), IDENT("n"), IDENT("m"), LITERAL(10)); auto *query = QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN("r")); SymbolTable symbol_table; diff --git a/tests/unit/query_variable_start_planner.cpp b/tests/unit/query_variable_start_planner.cpp index 894be87ca..36e413bf9 100644 --- a/tests/unit/query_variable_start_planner.cpp +++ b/tests/unit/query_variable_start_planner.cpp @@ -307,8 +307,9 @@ TEST(TestVariableStartPlanner, MatchBfs) { // Test MATCH (n) -bfs[r](r, n|n.id <> 3, 10)-> (m) RETURN r AstTreeStorage storage; auto *bfs = storage.Create<query::BreadthFirstAtom>( - IDENT("r"), Direction::OUT, IDENT("r"), IDENT("n"), - NEQ(PROPERTY_LOOKUP("n", id), LITERAL(3)), LITERAL(10)); + IDENT("r"), Direction::OUT, std::vector<GraphDbTypes::EdgeType>{}, + IDENT("r"), IDENT("n"), NEQ(PROPERTY_LOOKUP("n", id), LITERAL(3)), + LITERAL(10)); QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN("r")); // We expect to get a single column with the following rows: TypedValue r1_list(std::vector<TypedValue>{r1}); // [r1]