From 35f726dfd22a0890be375c48305d8d330dcf2a1d Mon Sep 17 00:00:00 2001 From: Teon Banek Date: Mon, 11 Sep 2017 08:51:33 +0200 Subject: [PATCH] Use EdgeType in Expand and ExpandVariable Summary: Add function First to utils. Insert EdgeType into Expand during planning. Reviewers: florijan, mislav.bradac Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D769 --- src/query/plan/operator.cpp | 65 ++++++-- src/query/plan/operator.hpp | 8 +- src/query/plan/rule_based_planner.cpp | 2 + src/query/plan/rule_based_planner.hpp | 52 +++++-- src/utils/algorithm.hpp | 32 +++- tests/unit/query_cost_estimator.cpp | 8 +- .../unit/query_plan_accumulate_aggregate.cpp | 5 +- tests/unit/query_plan_common.hpp | 12 +- .../query_plan_create_set_remove_delete.cpp | 28 ++-- tests/unit/query_plan_match_filter_return.cpp | 146 ++++++++++-------- 10 files changed, 235 insertions(+), 123 deletions(-) diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp index ccbe9772f..6a2fdfbee 100644 --- a/src/query/plan/operator.cpp +++ b/src/query/plan/operator.cpp @@ -312,10 +312,10 @@ std::unique_ptr ScanAllByLabelPropertyRange::MakeCursor( ExpressionEvaluator evaluator(frame, symbol_table, db, graph_view_); auto convert = [&evaluator](const auto &bound) -> std::experimental::optional> { - if (!bound) return std::experimental::nullopt; - return std::experimental::make_optional(utils::Bound( - bound.value().value()->Accept(evaluator), bound.value().type())); - }; + if (!bound) return std::experimental::nullopt; + return std::experimental::make_optional(utils::Bound( + bound.value().value()->Accept(evaluator), bound.value().type())); + }; return db.Vertices(label_, property_, convert(lower_bound()), convert(upper_bound()), graph_view_ == GraphView::NEW); }; @@ -391,12 +391,14 @@ std::unique_ptr ScanAllByLabelPropertyValue::MakeCursor( ExpandCommon::ExpandCommon(Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, const std::shared_ptr &input, Symbol input_symbol, bool existing_node, bool existing_edge, GraphView graph_view) : node_symbol_(node_symbol), edge_symbol_(edge_symbol), direction_(direction), + edge_type_(edge_type), input_(input ? input : std::make_shared()), input_symbol_(input_symbol), existing_node_(existing_node), @@ -547,6 +549,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, in_edges_.emplace( vertex.in_with_destination(existing_node.ValueVertex())); } + } else if (self_.edge_type_) { + in_edges_.emplace(vertex.in_with_type(self_.edge_type_)); } else { in_edges_.emplace(vertex.in()); } @@ -564,6 +568,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, out_edges_.emplace( vertex.out_with_destination(existing_node.ValueVertex())); } + } else if (self_.edge_type_) { + out_edges_.emplace(vertex.out_with_type(self_.edge_type_)); } else { out_edges_.emplace(vertex.out()); } @@ -580,14 +586,16 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, } ExpandVariable::ExpandVariable(Symbol node_symbol, Symbol edge_symbol, - EdgeAtom::Direction direction, bool is_reverse, - Expression *lower_bound, Expression *upper_bound, + EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, + bool is_reverse, Expression *lower_bound, + Expression *upper_bound, const std::shared_ptr &input, Symbol input_symbol, bool existing_node, bool existing_edge, GraphView graph_view, Expression *filter) - : ExpandCommon(node_symbol, edge_symbol, direction, input, input_symbol, - existing_node, existing_edge, graph_view), + : ExpandCommon(node_symbol, edge_symbol, direction, edge_type, input, + input_symbol, existing_node, existing_edge, graph_view), lower_bound_(lower_bound), upper_bound_(upper_bound), is_reverse_(is_reverse), @@ -607,7 +615,8 @@ namespace { * @return See above. */ auto ExpandFromVertex(const VertexAccessor &vertex, - EdgeAtom::Direction direction) { + EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type) { // wraps an EdgeAccessor into a pair auto wrapper = [](EdgeAtom::Direction direction, auto &&vertices) { return iter::imap( @@ -620,11 +629,30 @@ auto ExpandFromVertex(const VertexAccessor &vertex, // prepare a vector of elements we'll pass to the itertools std::vector chain_elements; - if (direction != EdgeAtom::Direction::OUT && vertex.in_degree() > 0) - chain_elements.emplace_back(wrapper(EdgeAtom::Direction::IN, vertex.in())); - if (direction != EdgeAtom::Direction::IN && vertex.out_degree() > 0) - chain_elements.emplace_back( - wrapper(EdgeAtom::Direction::OUT, vertex.out())); + if (direction != EdgeAtom::Direction::OUT && vertex.in_degree() > 0) { + if (edge_type) { + auto edges = vertex.in_with_type(edge_type); + if (edges.begin() != edges.end()) { + chain_elements.emplace_back( + wrapper(EdgeAtom::Direction::IN, std::move(edges))); + } + } else { + chain_elements.emplace_back( + wrapper(EdgeAtom::Direction::IN, vertex.in())); + } + } + if (direction != EdgeAtom::Direction::IN && vertex.out_degree() > 0) { + if (edge_type) { + auto edges = vertex.out_with_type(edge_type); + if (edges.begin() != edges.end()) { + chain_elements.emplace_back( + wrapper(EdgeAtom::Direction::OUT, std::move(edges))); + } + } else { + chain_elements.emplace_back( + wrapper(EdgeAtom::Direction::OUT, vertex.out())); + } + } return iter::chain.from_iterable(std::move(chain_elements)); } @@ -702,7 +730,8 @@ class ExpandVariableCursor : public Cursor { // a stack of edge iterables corresponding to the level/depth of // the expansion currently being Pulled std::vector(), - EdgeAtom::Direction::IN))> + EdgeAtom::Direction::IN, + self_.edge_type_))> edges_; // an iterator indicating the possition in the corresponding edges_ element @@ -745,7 +774,8 @@ class ExpandVariableCursor : public Cursor { if (upper_bound_ > 0) { SwitchAccessor(vertex, self_.graph_view_); - edges_.emplace_back(ExpandFromVertex(vertex, self_.direction_)); + edges_.emplace_back( + ExpandFromVertex(vertex, self_.direction_, self_.edge_type_)); edges_it_.emplace_back(edges_.back().begin()); } @@ -913,7 +943,8 @@ class ExpandVariableCursor : public Cursor { // edge's expansions onto the stack, if we should continue to expand if (upper_bound_ > static_cast(edges_.size())) { SwitchAccessor(current_vertex, self_.graph_view_); - edges_.emplace_back(ExpandFromVertex(current_vertex, self_.direction_)); + edges_.emplace_back(ExpandFromVertex(current_vertex, self_.direction_, + self_.edge_type_)); edges_it_.emplace_back(edges_.back().begin()); } diff --git a/src/query/plan/operator.hpp b/src/query/plan/operator.hpp index 315342a43..7ee1f2fb3 100644 --- a/src/query/plan/operator.hpp +++ b/src/query/plan/operator.hpp @@ -471,6 +471,8 @@ class ExpandCommon { * @param direction EdgeAtom::Direction determining the direction of edge * expansion. The direction is relative to the starting vertex for each * expansion. + * @param edge_type Optional GraphDbTypes::EdgeType specifying which edges we + * want to expand. * @param input Optional LogicalOperator that preceeds this one. * @param input_symbol Symbol that points to a VertexAccessor in the Frame * that expansion should emanate from. @@ -480,6 +482,7 @@ class ExpandCommon { */ ExpandCommon(Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, const std::shared_ptr &input, Symbol input_symbol, bool existing_node, bool existing_edge, GraphView graph_view = GraphView::AS_IS); @@ -488,12 +491,14 @@ class ExpandCommon { const auto &node_symbol() const { return node_symbol_; } const auto &edge_symbol() const { return edge_symbol_; } const auto &direction() const { return direction_; } + const auto &edge_type() const { return edge_type_; } protected: // info on what's getting expanded const Symbol node_symbol_; const Symbol edge_symbol_; const EdgeAtom::Direction direction_; + const GraphDbTypes::EdgeType edge_type_ = nullptr; // the input op and the symbol under which the op's result // can be found in the frame @@ -609,7 +614,8 @@ class ExpandVariable : public LogicalOperator, public ExpandCommon { * that get expanded (inclusive). */ ExpandVariable(Symbol node_symbol, Symbol edge_symbol, - EdgeAtom::Direction direction, bool is_reverse, + EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, bool is_reverse, Expression *lower_bound, Expression *upper_bound, const std::shared_ptr &input, Symbol input_symbol, bool existing_node, bool existing_edge, diff --git a/src/query/plan/rule_based_planner.cpp b/src/query/plan/rule_based_planner.cpp index 67413f746..0dc0284c4 100644 --- a/src/query/plan/rule_based_planner.cpp +++ b/src/query/plan/rule_based_planner.cpp @@ -911,6 +911,8 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table, auto add_expand_filter = [&](NodeAtom *, EdgeAtom *edge, NodeAtom *node) { const auto &edge_symbol = symbol_table.at(*edge->identifier_); if (!edge->edge_types_.empty()) { + edge_type_filters_[edge_symbol].insert(edge->edge_types_.begin(), + edge->edge_types_.end()); if (edge->has_range_) { // We need a new identifier and symbol for All. auto *ident_in_all = edge->identifier_->Clone(storage); diff --git a/src/query/plan/rule_based_planner.hpp b/src/query/plan/rule_based_planner.hpp index ae78f449c..9820dba93 100644 --- a/src/query/plan/rule_based_planner.hpp +++ b/src/query/plan/rule_based_planner.hpp @@ -62,6 +62,9 @@ class Filters { /// Mapping from a symbol to labels that are filtered on it. These should be /// used only for generating indexed scans. const auto &label_filters() const { return label_filters_; } + /// Mapping from a symbol to edge types that are filtered on it. These should + /// be used for generating indexed expansions. + const auto &edge_type_filters() const { return edge_type_filters_; } /// Mapping from a symbol to properties that are filtered on it. These should /// be used only for generating indexed scans. const auto &property_filters() const { return property_filters_; } @@ -85,9 +88,12 @@ class Filters { void AnalyzeFilter(Expression *, const SymbolTable &); std::vector all_filters_; - std::unordered_map> label_filters_; - std::unordered_map< - Symbol, std::map>> + std::unordered_map> + label_filters_; + std::unordered_map> + edge_type_filters_; + std::unordered_map>> property_filters_; }; @@ -330,9 +336,10 @@ class RuleBasedPlanner { // cannot be found, the function will return (`false`, maximum int64_t), while // leaving `best_label` and `best_property` unchanged. std::pair FindBestLabelPropertyIndex( - const std::set &labels, - const std::map> &property_filters, + const std::unordered_set &labels, + const std::unordered_map> + &property_filters, const Symbol &symbol, const std::unordered_set &bound_symbols, GraphDbTypes::Label &best_label, std::pair @@ -379,7 +386,7 @@ class RuleBasedPlanner { } const GraphDbTypes::Label &FindBestLabelIndex( - const std::set &labels) { + const std::unordered_set &labels) { debug_assert(!labels.empty(), "Trying to find the best label without any labels."); return *std::min_element(labels.begin(), labels.end(), @@ -400,17 +407,18 @@ class RuleBasedPlanner { const MatchContext &match_ctx, const std::experimental::optional &max_vertex_count = std::experimental::nullopt) { - const auto labels = FindOr(match_ctx.matching.filters.label_filters(), - node_symbol, std::set()) - .first; + const auto labels = + FindOr(match_ctx.matching.filters.label_filters(), node_symbol, + std::unordered_set()) + .first; if (labels.empty()) { // Without labels, we cannot generated any indexed ScanAll. return nullptr; } const auto properties = FindOr(match_ctx.matching.filters.property_filters(), node_symbol, - std::map>()) + std::unordered_map>()) .first; // First, try to see if we can use label+property index. If not, use just // the label index (which ought to exist). @@ -499,6 +507,16 @@ class RuleBasedPlanner { } else { match_context.new_symbols.emplace_back(edge_symbol); } + GraphDbTypes::EdgeType edge_type = nullptr; + const auto &edge_types = + FindOr(matching.filters.edge_type_filters(), edge_symbol, + std::unordered_set()) + .first; + if (edge_types.size() == 1U) { + // With exactly one edge type filter, we can generate a more efficient + // expand. + edge_type = First(edge_types); + } if (auto *bf_atom = dynamic_cast(expansion.edge)) { const auto &traversed_edge_symbol = symbol_table.at(*bf_atom->traversed_edge_identifier_); @@ -514,7 +532,7 @@ class RuleBasedPlanner { auto *filter_expr = impl::FindExpandVariableFilter( bound_symbols, node_symbol, all_filters, storage); last_op = new ExpandVariable( - node_symbol, edge_symbol, expansion.direction, + node_symbol, edge_symbol, expansion.direction, edge_type, expansion.is_flipped, expansion.edge->lower_bound_, expansion.edge->upper_bound_, std::shared_ptr(last_op), node1_symbol, @@ -537,10 +555,10 @@ class RuleBasedPlanner { existing_node = true; } } - last_op = new Expand(node_symbol, edge_symbol, expansion.direction, - std::shared_ptr(last_op), - node1_symbol, existing_node, existing_edge, - match_context.graph_view); + last_op = new Expand( + node_symbol, edge_symbol, expansion.direction, edge_type, + std::shared_ptr(last_op), node1_symbol, + existing_node, existing_edge, match_context.graph_view); } if (!existing_edge) { // Ensure Cyphermorphism (different edge symbols always map to diff --git a/src/utils/algorithm.hpp b/src/utils/algorithm.hpp index feeb3e5e8..d8943d993 100644 --- a/src/utils/algorithm.hpp +++ b/src/utils/algorithm.hpp @@ -4,6 +4,8 @@ #include #include +#include "utils/exceptions.hpp" + /** * Outputs a collection of items to the given stream, separating them with the * given delimiter. @@ -55,8 +57,32 @@ template std::pair FindOr(const TMap &map, const TKey &key, TVal &&or_value) { auto it = map.find(key); - if (it != map.end()) { - return {it->second, true}; - } + if (it != map.end()) return {it->second, true}; return {std::forward(or_value), false}; } + +/** + * Returns the *copy* of the first element from an iterable. + * + * @param iterable An iterable collection of values. + * @return The first element of the `iterable`. + * @exception BasicException is thrown if the `iterable` is empty. + */ +template +auto First(TIterable &&iterable) { + if (iterable.begin() != iterable.end()) return *iterable.begin(); + throw utils::BasicException("Empty iterable"); +} + +/** + * Returns the *copy* of the first element from an iterable. + * + * @param iterable An iterable collection of values. + * @param empty_value Value to return if the `iterable` is empty. + * @return The first element of the `iterable` or the `empty_value`. + */ +template +TVal First(TIterable &&iterable, TVal &&empty_value) { + if (iterable.begin() != iterable.end()) return *iterable.begin(); + return empty_value; +} diff --git a/tests/unit/query_cost_estimator.cpp b/tests/unit/query_cost_estimator.cpp index da5cfaed9..fa983f600 100644 --- a/tests/unit/query_cost_estimator.cpp +++ b/tests/unit/query_cost_estimator.cpp @@ -146,15 +146,15 @@ TEST_F(QueryCostEstimator, ScanAllByLabelPropertyRangeNonLiteral) { } TEST_F(QueryCostEstimator, Expand) { - MakeOp(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, last_op_, - NextSymbol(), false, false); + MakeOp(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr, + last_op_, NextSymbol(), false, false); EXPECT_COST(CardParam::kExpand * CostParam::kExpand); } TEST_F(QueryCostEstimator, ExpandVariable) { MakeOp(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, - false, nullptr, nullptr, last_op_, NextSymbol(), false, - false); + nullptr, false, nullptr, nullptr, last_op_, + NextSymbol(), false, false); EXPECT_COST(CardParam::kExpandVariable * CostParam::kExpandVariable); } diff --git a/tests/unit/query_plan_accumulate_aggregate.cpp b/tests/unit/query_plan_accumulate_aggregate.cpp index 4e0559515..c313dfc06 100644 --- a/tests/unit/query_plan_accumulate_aggregate.cpp +++ b/tests/unit/query_plan_accumulate_aggregate.cpp @@ -48,8 +48,9 @@ TEST(QueryPlan, Accumulate) { SymbolTable symbol_table; auto n = MakeScanAll(storage, symbol_table, "n"); - auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::BOTH, false, "m", false); + auto r_m = + MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); auto one = LITERAL(1); auto n_p = PROPERTY_LOOKUP("n", prop); diff --git a/tests/unit/query_plan_common.hpp b/tests/unit/query_plan_common.hpp index 8069c7f83..ccea813c2 100644 --- a/tests/unit/query_plan_common.hpp +++ b/tests/unit/query_plan_common.hpp @@ -172,8 +172,10 @@ struct ExpandTuple { ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table, std::shared_ptr input, Symbol input_symbol, const std::string &edge_identifier, - EdgeAtom::Direction direction, bool existing_edge, - const std::string &node_identifier, bool existing_node, + EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, + bool existing_edge, const std::string &node_identifier, + bool existing_node, GraphView graph_view = GraphView::AS_IS) { auto edge = EDGE(edge_identifier, direction); auto edge_sym = symbol_table.CreateSymbol(edge_identifier, true); @@ -183,9 +185,9 @@ ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table, auto node_sym = symbol_table.CreateSymbol(node_identifier, true); symbol_table[*node->identifier_] = node_sym; - auto op = std::make_shared(node_sym, edge_sym, direction, input, - input_symbol, existing_node, existing_edge, - graph_view); + auto op = std::make_shared(node_sym, edge_sym, direction, edge_type, + input, input_symbol, existing_node, + existing_edge, graph_view); return ExpandTuple{edge, edge_sym, node, node_sym, op}; } diff --git a/tests/unit/query_plan_create_set_remove_delete.cpp b/tests/unit/query_plan_create_set_remove_delete.cpp index eefb1ed48..68eb55541 100644 --- a/tests/unit/query_plan_create_set_remove_delete.cpp +++ b/tests/unit/query_plan_create_set_remove_delete.cpp @@ -300,7 +300,7 @@ TEST(QueryPlan, Delete) { { auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); auto r_get = storage.Create("r"); symbol_table[*r_get] = r_m.edge_sym_; auto delete_op = std::make_shared( @@ -352,8 +352,9 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) { SymbolTable symbol_table; auto n = MakeScanAll(storage, symbol_table, "n"); - auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::BOTH, false, "m", false); + auto r_m = + MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); // getter expressions for deletion auto n_get = storage.Create("n"); @@ -476,7 +477,7 @@ TEST(QueryPlan, SetProperty) { // scan (n)-[r]->(m) auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); // set prop1 to 42 on n and r auto prop1 = dba->Property("prop1"); @@ -527,7 +528,7 @@ TEST(QueryPlan, SetProperties) { // scan (n)-[r]->(m) auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); auto op = update ? plan::SetProperties::Op::UPDATE : plan::SetProperties::Op::REPLACE; @@ -630,7 +631,7 @@ TEST(QueryPlan, RemoveProperty) { // scan (n)-[r]->(m) auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); auto n_p = PROPERTY_LOOKUP("n", prop1); symbol_table[*n_p->expression_] = n.sym_; @@ -706,8 +707,9 @@ TEST(QueryPlan, NodeFilterSet) { // MATCH (n {prop: 42}) -[r]- (m) auto scan_all = MakeScanAll(storage, symbol_table, "n"); scan_all.node_->properties_[prop] = LITERAL(42); - auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, - "r", EdgeAtom::Direction::BOTH, false, "m", false); + auto expand = + MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); auto *filter_expr = EQ(storage.Create(scan_all.node_->identifier_, prop), LITERAL(42)); @@ -745,8 +747,9 @@ TEST(QueryPlan, FilterRemove) { // MATCH (n) -[r]- (m) WHERE n.prop < 43 auto scan_all = MakeScanAll(storage, symbol_table, "n"); scan_all.node_->properties_[prop] = LITERAL(42); - auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, - "r", EdgeAtom::Direction::BOTH, false, "m", false); + auto expand = + MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); auto filter_prop = PROPERTY_LOOKUP("n", prop); symbol_table[*filter_prop->expression_] = scan_all.sym_; auto filter = @@ -808,8 +811,9 @@ TEST(QueryPlan, Merge) { auto n = MakeScanAll(storage, symbol_table, "n"); // merge_match branch - auto r_m = MakeExpand(storage, symbol_table, std::make_shared(), n.sym_, - "r", EdgeAtom::Direction::BOTH, false, "m", false); + auto r_m = + MakeExpand(storage, symbol_table, std::make_shared(), n.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); auto m_p = PROPERTY_LOOKUP("m", prop); symbol_table[*m_p->expression_] = r_m.node_sym_; auto m_set = std::make_shared(r_m.op_, m_p, LITERAL(1)); diff --git a/tests/unit/query_plan_match_filter_return.cpp b/tests/unit/query_plan_match_filter_return.cpp index eb28b2d90..9fb41aed4 100644 --- a/tests/unit/query_plan_match_filter_return.cpp +++ b/tests/unit/query_plan_match_filter_return.cpp @@ -240,7 +240,7 @@ TEST(QueryPlan, Expand) { auto test_expand = [&](EdgeAtom::Direction direction, GraphView graph_view) { auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", direction, - false, "m", false, graph_view); + nullptr, false, "m", false, graph_view); // make a named expression and a produce auto output = NEXPR("m", IDENT("m")); @@ -346,6 +346,7 @@ class QueryPlanExpandVariable : public testing::Test { std::shared_ptr AddMatch( std::shared_ptr input_op, const std::string &node_from, int layer, EdgeAtom::Direction direction, + const GraphDbTypes::EdgeType &edge_type, std::experimental::optional lower, std::experimental::optional upper, Symbol edge_sym, bool existing_edge, const std::string &node_to, @@ -367,13 +368,13 @@ class QueryPlanExpandVariable : public testing::Test { return bound ? LITERAL(static_cast(bound.value())) : nullptr; }; return std::make_shared( - n_to_sym, edge_sym, direction, is_reverse, convert(lower), + n_to_sym, edge_sym, direction, edge_type, is_reverse, convert(lower), convert(upper), filter_op, n_from.sym_, false, existing_edge, graph_view); } else - return std::make_shared(n_to_sym, edge_sym, direction, filter_op, - n_from.sym_, false, existing_edge, - graph_view); + return std::make_shared(n_to_sym, edge_sym, direction, edge_type, + filter_op, n_from.sym_, false, + existing_edge, graph_view); } /* Creates an edge (in the frame and symbol table). Returns the symbol. */ @@ -412,10 +413,10 @@ TEST_F(QueryPlanExpandVariable, OneVariableExpansion) { std::experimental::optional upper, bool reverse) { auto e = Edge("r", direction); - return GetResults( - AddMatch(nullptr, "n", layer, direction, lower, upper, - e, false, "m", GraphView::AS_IS, reverse), - e); + return GetResults(AddMatch(nullptr, "n", layer, direction, + nullptr, lower, upper, e, false, + "m", GraphView::AS_IS, reverse), + e); }; for (int reverse = 0; reverse < 2; ++reverse) { @@ -476,19 +477,20 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) { if (single_expansion_before) { symbols.push_back(Edge("r0", direction)); - last_op = AddMatch(last_op, "n0", layer, direction, lower, upper, - symbols.back(), false, "m0"); + last_op = AddMatch(last_op, "n0", layer, direction, nullptr, + lower, upper, symbols.back(), false, "m0"); } auto var_length_sym = Edge("r1", direction); symbols.push_back(var_length_sym); - last_op = AddMatch(last_op, "n1", layer, direction, lower, - upper, var_length_sym, false, "m1"); + last_op = + AddMatch(last_op, "n1", layer, direction, nullptr, + lower, upper, var_length_sym, false, "m1"); if (!single_expansion_before) { symbols.push_back(Edge("r2", direction)); - last_op = AddMatch(last_op, "n2", layer, direction, lower, upper, - symbols.back(), false, "m2"); + last_op = AddMatch(last_op, "n2", layer, direction, nullptr, + lower, upper, symbols.back(), false, "m2"); } if (add_uniqueness_check) { @@ -517,11 +519,12 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) { std::experimental::optional upper, bool add_uniqueness_check) { auto e1 = Edge("r1", direction); - auto first = AddMatch(nullptr, "n1", layer, direction, - lower, upper, e1, false, "m1"); + auto first = + AddMatch(nullptr, "n1", layer, direction, nullptr, + lower, upper, e1, false, "m1"); auto e2 = Edge("r2", direction); - auto last_op = AddMatch(first, "n2", layer, direction, - lower, upper, e2, false, "m2"); + auto last_op = AddMatch( + first, "n2", layer, direction, nullptr, lower, upper, e2, false, "m2"); if (add_uniqueness_check) { last_op = std::make_shared>( last_op, e2, std::vector{e1}); @@ -542,11 +545,13 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) { std::experimental::optional upper, bool same_edge_symbol) { auto e1 = Edge("r1", direction); - auto first = AddMatch(nullptr, "n1", layer, direction, - lower, upper, e1, false, "m1"); + auto first = + AddMatch(nullptr, "n1", layer, direction, nullptr, + lower, upper, e1, false, "m1"); auto e2 = same_edge_symbol ? e1 : Edge("r2", direction); - auto second = AddMatch(first, "n2", layer, direction, lower, - upper, e2, same_edge_symbol, "m2"); + auto second = + AddMatch(first, "n2", layer, direction, nullptr, lower, + upper, e2, same_edge_symbol, "m2"); return GetResults(second, e2); }; @@ -572,30 +577,40 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) { } TEST_F(QueryPlanExpandVariable, GraphState) { - auto test_expand = [&](GraphView graph_view) { + auto test_expand = [&](GraphView graph_view, const auto &edge_type) { auto e = Edge("r", EdgeAtom::Direction::OUT); return GetResults( - AddMatch(nullptr, "n", 0, EdgeAtom::Direction::OUT, 2, - 2, e, false, "m", graph_view), + AddMatch(nullptr, "n", 0, EdgeAtom::Direction::OUT, + edge_type, 2, 2, e, false, "m", graph_view), e); }; - EXPECT_EQ(test_expand(GraphView::OLD), (map_int{{2, 8}})); + EXPECT_EQ(test_expand(GraphView::OLD, dba->EdgeType("edge_type")), + (map_int{{2, 8}})); + auto new_edge_type = dba->EdgeType("some_type"); // add two vertices branching out from the second layer for (VertexAccessor &vertex : dba->Vertices(true)) if (vertex.has_label(labels[1])) { auto new_vertex = dba->InsertVertex(); - dba->InsertEdge(vertex, new_vertex, dba->EdgeType("some_type")); + dba->InsertEdge(vertex, new_vertex, new_edge_type); } ASSERT_EQ(CountIterable(dba->Vertices(false)), 6); ASSERT_EQ(CountIterable(dba->Vertices(true)), 8); - EXPECT_EQ(test_expand(GraphView::OLD), (map_int{{2, 8}})); - EXPECT_EQ(test_expand(GraphView::NEW), (map_int{{2, 12}})); + EXPECT_EQ(test_expand(GraphView::OLD, nullptr), (map_int{{2, 8}})); + EXPECT_EQ(test_expand(GraphView::OLD, new_edge_type), (map_int{})); + EXPECT_EQ(test_expand(GraphView::NEW, nullptr), (map_int{{2, 12}})); + EXPECT_EQ(test_expand(GraphView::NEW, dba->EdgeType("edge_type")), + (map_int{{2, 8}})); + EXPECT_EQ(test_expand(GraphView::NEW, new_edge_type), (map_int{})); dba->AdvanceCommand(); - EXPECT_EQ(test_expand(GraphView::OLD), (map_int{{2, 12}})); - EXPECT_EQ(test_expand(GraphView::NEW), (map_int{{2, 12}})); + for (const auto graph_view : {GraphView::OLD, GraphView::NEW}) { + EXPECT_EQ(test_expand(graph_view, nullptr), (map_int{{2, 12}})); + EXPECT_EQ(test_expand(graph_view, dba->EdgeType("edge_type")), + (map_int{{2, 8}})); + EXPECT_EQ(test_expand(graph_view, new_edge_type), (map_int{})); + } } namespace std { @@ -851,7 +866,7 @@ TEST(QueryPlan, ExpandOptional) { // MATCH (n) OPTIONAL MATCH (n)-[r]->(m) auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, nullptr, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); auto optional = std::make_shared( n.op_, r_m.op_, std::vector{r_m.edge_sym_, r_m.node_sym_}); @@ -926,7 +941,7 @@ TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) { auto with = MakeProduce(optional, n_ne); // MATCH (n) -[r]-> (m) auto r_m = MakeExpand(storage, symbol_table, with, with_n_sym, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); // RETURN m auto m_ne = NEXPR("m", IDENT("m")); symbol_table[*m_ne->expression_] = r_m.node_sym_; @@ -973,8 +988,9 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) { symbol_table[*edge->identifier_] = edge_sym; auto node = NODE("n"); symbol_table[*node->identifier_] = with_n_sym; - auto expand = std::make_shared( - with_n_sym, edge_sym, edge_direction, m.op_, m.sym_, true, false); + auto expand = + std::make_shared(with_n_sym, edge_sym, edge_direction, + nullptr, m.op_, m.sym_, true, false); // RETURN m auto m_ne = NEXPR("m", IDENT("m")); symbol_table[*m_ne->expression_] = m.sym_; @@ -1005,7 +1021,7 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingEdge) { storage.Create(n.node_->identifier_, n.node_->labels_); auto node_filter = std::make_shared(n.op_, filter_expr); auto r_m = MakeExpand(storage, symbol_table, node_filter, n.sym_, "r", - EdgeAtom::Direction::BOTH, false, "m", false); + EdgeAtom::Direction::BOTH, nullptr, false, "m", false); auto optional = std::make_shared( nullptr, r_m.op_, std::vector{n.sym_, r_m.edge_sym_, r_m.node_sym_}); @@ -1023,8 +1039,9 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingEdge) { auto node = NODE("n"); auto node_sym = symbol_table.CreateSymbol("b", true); symbol_table[*node->identifier_] = node_sym; - auto expand = std::make_shared( - node_sym, with_r_sym, edge_direction, a.op_, a.sym_, false, true); + auto expand = + std::make_shared(node_sym, with_r_sym, edge_direction, + nullptr, a.op_, a.sym_, false, true); // RETURN a auto a_ne = NEXPR("a", IDENT("a")); symbol_table[*a_ne->expression_] = a.sym_; @@ -1053,11 +1070,12 @@ TEST(QueryPlan, ExpandExistingNode) { auto test_existing = [&](bool with_existing, int expected_result_count) { auto n = MakeScanAll(storage, symbol_table, "n"); auto r_n = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "n", with_existing); + EdgeAtom::Direction::OUT, nullptr, false, "n", + with_existing); if (with_existing) - r_n.op_ = - std::make_shared(n.sym_, r_n.edge_sym_, r_n.edge_->direction_, - n.op_, n.sym_, with_existing, false); + r_n.op_ = std::make_shared(n.sym_, r_n.edge_sym_, + r_n.edge_->direction_, nullptr, n.op_, + n.sym_, with_existing, false); // make a named expression and a produce auto output = NEXPR("n", IDENT("n")); @@ -1095,14 +1113,16 @@ TEST(QueryPlan, ExpandExistingEdge) { auto test_existing = [&](bool with_existing, int expected_result_count) { auto i = MakeScanAll(storage, symbol_table, "i"); - auto r_j = MakeExpand(storage, symbol_table, i.op_, i.sym_, "r", - EdgeAtom::Direction::BOTH, false, "j", false); + auto r_j = + MakeExpand(storage, symbol_table, i.op_, i.sym_, "r", + EdgeAtom::Direction::BOTH, nullptr, false, "j", false); auto r_k = MakeExpand(storage, symbol_table, r_j.op_, r_j.node_sym_, "r", - EdgeAtom::Direction::BOTH, with_existing, "k", false); + EdgeAtom::Direction::BOTH, nullptr, with_existing, + "k", false); if (with_existing) - r_k.op_ = std::make_shared(r_k.node_sym_, r_j.edge_sym_, - r_k.edge_->direction_, r_j.op_, - r_j.node_sym_, false, with_existing); + r_k.op_ = std::make_shared( + r_k.node_sym_, r_j.edge_sym_, r_k.edge_->direction_, nullptr, r_j.op_, + r_j.node_sym_, false, with_existing); // make a named expression and a produce auto output = NEXPR("r", IDENT("r")); @@ -1135,7 +1155,7 @@ TEST(QueryPlan, ExpandBothCycleEdgeCase) { auto n = MakeScanAll(storage, symbol_table, "n"); auto r_ = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::BOTH, false, "_", false); + EdgeAtom::Direction::BOTH, nullptr, false, "_", false); EXPECT_EQ(1, PullAll(r_.op_, *dba, symbol_table)); } @@ -1177,17 +1197,17 @@ TEST(QueryPlan, EdgeFilter) { auto test_filter = [&]() { // define an operator tree for query - // MATCH (n)-[r]->(m) RETURN m + // MATCH (n)-[r :et0 {property: 42}]->(m) RETURN m auto n = MakeScanAll(storage, symbol_table, "n"); - auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); - r_m.edge_->edge_types_.push_back(edge_types[0]); + const auto &edge_type = edge_types[0]; + auto r_m = + MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", + EdgeAtom::Direction::OUT, edge_type, false, "m", false); + r_m.edge_->edge_types_.push_back(edge_type); r_m.edge_->properties_[prop] = LITERAL(42); auto *filter_expr = - AND(storage.Create(r_m.edge_->identifier_, - r_m.edge_->edge_types_), - EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42))); + EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42)); auto edge_filter = std::make_shared(r_m.op_, filter_expr); // make a named expression and a produce @@ -1228,7 +1248,7 @@ TEST(QueryPlan, EdgeFilterMultipleTypes) { // make a scan all auto n = MakeScanAll(storage, symbol_table, "n"); auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", - EdgeAtom::Direction::OUT, false, "m", false); + EdgeAtom::Direction::OUT, nullptr, false, "m", false); // add an edge type filter r_m.edge_->edge_types_.push_back(type_1); r_m.edge_->edge_types_.push_back(type_2); @@ -1295,14 +1315,16 @@ TEST(QueryPlan, ExpandUniquenessFilter) { SymbolTable symbol_table; auto n1 = MakeScanAll(storage, symbol_table, "n1"); - auto r1_n2 = MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1", - EdgeAtom::Direction::OUT, false, "n2", false); + auto r1_n2 = + MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1", + EdgeAtom::Direction::OUT, nullptr, false, "n2", false); std::shared_ptr last_op = r1_n2.op_; if (vertex_uniqueness) last_op = std::make_shared>( last_op, r1_n2.node_sym_, std::vector{n1.sym_}); - auto r2_n3 = MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, - "r2", EdgeAtom::Direction::OUT, false, "n3", false); + auto r2_n3 = + MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, "r2", + EdgeAtom::Direction::OUT, nullptr, false, "n3", false); last_op = r2_n3.op_; if (edge_uniqueness) last_op = std::make_shared>(