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
This commit is contained in:
Teon Banek 2017-09-11 08:51:33 +02:00 committed by florijan
parent 8aa4b5aa74
commit 35f726dfd2
10 changed files with 235 additions and 123 deletions

View File

@ -312,10 +312,10 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyRange::MakeCursor(
ExpressionEvaluator evaluator(frame, symbol_table, db, graph_view_); ExpressionEvaluator evaluator(frame, symbol_table, db, graph_view_);
auto convert = [&evaluator](const auto &bound) auto convert = [&evaluator](const auto &bound)
-> std::experimental::optional<utils::Bound<PropertyValue>> { -> std::experimental::optional<utils::Bound<PropertyValue>> {
if (!bound) return std::experimental::nullopt; if (!bound) return std::experimental::nullopt;
return std::experimental::make_optional(utils::Bound<PropertyValue>( return std::experimental::make_optional(utils::Bound<PropertyValue>(
bound.value().value()->Accept(evaluator), bound.value().type())); bound.value().value()->Accept(evaluator), bound.value().type()));
}; };
return db.Vertices(label_, property_, convert(lower_bound()), return db.Vertices(label_, property_, convert(lower_bound()),
convert(upper_bound()), graph_view_ == GraphView::NEW); convert(upper_bound()), graph_view_ == GraphView::NEW);
}; };
@ -391,12 +391,14 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyValue::MakeCursor(
ExpandCommon::ExpandCommon(Symbol node_symbol, Symbol edge_symbol, ExpandCommon::ExpandCommon(Symbol node_symbol, Symbol edge_symbol,
EdgeAtom::Direction direction, EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
const std::shared_ptr<LogicalOperator> &input, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, Symbol input_symbol, bool existing_node,
bool existing_edge, GraphView graph_view) bool existing_edge, GraphView graph_view)
: node_symbol_(node_symbol), : node_symbol_(node_symbol),
edge_symbol_(edge_symbol), edge_symbol_(edge_symbol),
direction_(direction), direction_(direction),
edge_type_(edge_type),
input_(input ? input : std::make_shared<Once>()), input_(input ? input : std::make_shared<Once>()),
input_symbol_(input_symbol), input_symbol_(input_symbol),
existing_node_(existing_node), existing_node_(existing_node),
@ -547,6 +549,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame,
in_edges_.emplace( in_edges_.emplace(
vertex.in_with_destination(existing_node.ValueVertex())); vertex.in_with_destination(existing_node.ValueVertex()));
} }
} else if (self_.edge_type_) {
in_edges_.emplace(vertex.in_with_type(self_.edge_type_));
} else { } else {
in_edges_.emplace(vertex.in()); in_edges_.emplace(vertex.in());
} }
@ -564,6 +568,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame,
out_edges_.emplace( out_edges_.emplace(
vertex.out_with_destination(existing_node.ValueVertex())); vertex.out_with_destination(existing_node.ValueVertex()));
} }
} else if (self_.edge_type_) {
out_edges_.emplace(vertex.out_with_type(self_.edge_type_));
} else { } else {
out_edges_.emplace(vertex.out()); out_edges_.emplace(vertex.out());
} }
@ -580,14 +586,16 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame,
} }
ExpandVariable::ExpandVariable(Symbol node_symbol, Symbol edge_symbol, ExpandVariable::ExpandVariable(Symbol node_symbol, Symbol edge_symbol,
EdgeAtom::Direction direction, bool is_reverse, EdgeAtom::Direction direction,
Expression *lower_bound, Expression *upper_bound, const GraphDbTypes::EdgeType &edge_type,
bool is_reverse, Expression *lower_bound,
Expression *upper_bound,
const std::shared_ptr<LogicalOperator> &input, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, Symbol input_symbol, bool existing_node,
bool existing_edge, GraphView graph_view, bool existing_edge, GraphView graph_view,
Expression *filter) Expression *filter)
: ExpandCommon(node_symbol, edge_symbol, direction, input, input_symbol, : ExpandCommon(node_symbol, edge_symbol, direction, edge_type, input,
existing_node, existing_edge, graph_view), input_symbol, existing_node, existing_edge, graph_view),
lower_bound_(lower_bound), lower_bound_(lower_bound),
upper_bound_(upper_bound), upper_bound_(upper_bound),
is_reverse_(is_reverse), is_reverse_(is_reverse),
@ -607,7 +615,8 @@ namespace {
* @return See above. * @return See above.
*/ */
auto ExpandFromVertex(const VertexAccessor &vertex, auto ExpandFromVertex(const VertexAccessor &vertex,
EdgeAtom::Direction direction) { EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type) {
// wraps an EdgeAccessor into a pair <accessor, direction> // wraps an EdgeAccessor into a pair <accessor, direction>
auto wrapper = [](EdgeAtom::Direction direction, auto &&vertices) { auto wrapper = [](EdgeAtom::Direction direction, auto &&vertices) {
return iter::imap( return iter::imap(
@ -620,11 +629,30 @@ auto ExpandFromVertex(const VertexAccessor &vertex,
// prepare a vector of elements we'll pass to the itertools // prepare a vector of elements we'll pass to the itertools
std::vector<decltype(wrapper(direction, vertex.in()))> chain_elements; std::vector<decltype(wrapper(direction, vertex.in()))> chain_elements;
if (direction != EdgeAtom::Direction::OUT && vertex.in_degree() > 0) if (direction != EdgeAtom::Direction::OUT && vertex.in_degree() > 0) {
chain_elements.emplace_back(wrapper(EdgeAtom::Direction::IN, vertex.in())); if (edge_type) {
if (direction != EdgeAtom::Direction::IN && vertex.out_degree() > 0) auto edges = vertex.in_with_type(edge_type);
chain_elements.emplace_back( if (edges.begin() != edges.end()) {
wrapper(EdgeAtom::Direction::OUT, vertex.out())); 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)); 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 // a stack of edge iterables corresponding to the level/depth of
// the expansion currently being Pulled // the expansion currently being Pulled
std::vector<decltype(ExpandFromVertex(std::declval<VertexAccessor>(), std::vector<decltype(ExpandFromVertex(std::declval<VertexAccessor>(),
EdgeAtom::Direction::IN))> EdgeAtom::Direction::IN,
self_.edge_type_))>
edges_; edges_;
// an iterator indicating the possition in the corresponding edges_ element // an iterator indicating the possition in the corresponding edges_ element
@ -745,7 +774,8 @@ class ExpandVariableCursor : public Cursor {
if (upper_bound_ > 0) { if (upper_bound_ > 0) {
SwitchAccessor(vertex, self_.graph_view_); 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()); 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 // edge's expansions onto the stack, if we should continue to expand
if (upper_bound_ > static_cast<int64_t>(edges_.size())) { if (upper_bound_ > static_cast<int64_t>(edges_.size())) {
SwitchAccessor(current_vertex, self_.graph_view_); 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()); edges_it_.emplace_back(edges_.back().begin());
} }

View File

@ -471,6 +471,8 @@ class ExpandCommon {
* @param direction EdgeAtom::Direction determining the direction of edge * @param direction EdgeAtom::Direction determining the direction of edge
* expansion. The direction is relative to the starting vertex for each * expansion. The direction is relative to the starting vertex for each
* expansion. * expansion.
* @param edge_type Optional GraphDbTypes::EdgeType specifying which edges we
* want to expand.
* @param input Optional LogicalOperator that preceeds this one. * @param input Optional LogicalOperator that preceeds this one.
* @param input_symbol Symbol that points to a VertexAccessor in the Frame * @param input_symbol Symbol that points to a VertexAccessor in the Frame
* that expansion should emanate from. * that expansion should emanate from.
@ -480,6 +482,7 @@ class ExpandCommon {
*/ */
ExpandCommon(Symbol node_symbol, Symbol edge_symbol, ExpandCommon(Symbol node_symbol, Symbol edge_symbol,
EdgeAtom::Direction direction, EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
const std::shared_ptr<LogicalOperator> &input, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, bool existing_edge, Symbol input_symbol, bool existing_node, bool existing_edge,
GraphView graph_view = GraphView::AS_IS); GraphView graph_view = GraphView::AS_IS);
@ -488,12 +491,14 @@ class ExpandCommon {
const auto &node_symbol() const { return node_symbol_; } const auto &node_symbol() const { return node_symbol_; }
const auto &edge_symbol() const { return edge_symbol_; } const auto &edge_symbol() const { return edge_symbol_; }
const auto &direction() const { return direction_; } const auto &direction() const { return direction_; }
const auto &edge_type() const { return edge_type_; }
protected: protected:
// info on what's getting expanded // info on what's getting expanded
const Symbol node_symbol_; const Symbol node_symbol_;
const Symbol edge_symbol_; const Symbol edge_symbol_;
const EdgeAtom::Direction direction_; const EdgeAtom::Direction direction_;
const GraphDbTypes::EdgeType edge_type_ = nullptr;
// the input op and the symbol under which the op's result // the input op and the symbol under which the op's result
// can be found in the frame // can be found in the frame
@ -609,7 +614,8 @@ class ExpandVariable : public LogicalOperator, public ExpandCommon {
* that get expanded (inclusive). * that get expanded (inclusive).
*/ */
ExpandVariable(Symbol node_symbol, Symbol edge_symbol, 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, Expression *lower_bound, Expression *upper_bound,
const std::shared_ptr<LogicalOperator> &input, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, bool existing_edge, Symbol input_symbol, bool existing_node, bool existing_edge,

View File

@ -911,6 +911,8 @@ void Filters::CollectPatternFilters(Pattern &pattern, SymbolTable &symbol_table,
auto add_expand_filter = [&](NodeAtom *, EdgeAtom *edge, NodeAtom *node) { auto add_expand_filter = [&](NodeAtom *, EdgeAtom *edge, NodeAtom *node) {
const auto &edge_symbol = symbol_table.at(*edge->identifier_); const auto &edge_symbol = symbol_table.at(*edge->identifier_);
if (!edge->edge_types_.empty()) { if (!edge->edge_types_.empty()) {
edge_type_filters_[edge_symbol].insert(edge->edge_types_.begin(),
edge->edge_types_.end());
if (edge->has_range_) { if (edge->has_range_) {
// We need a new identifier and symbol for All. // We need a new identifier and symbol for All.
auto *ident_in_all = edge->identifier_->Clone(storage); auto *ident_in_all = edge->identifier_->Clone(storage);

View File

@ -62,6 +62,9 @@ class Filters {
/// Mapping from a symbol to labels that are filtered on it. These should be /// Mapping from a symbol to labels that are filtered on it. These should be
/// used only for generating indexed scans. /// used only for generating indexed scans.
const auto &label_filters() const { return label_filters_; } 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 /// Mapping from a symbol to properties that are filtered on it. These should
/// be used only for generating indexed scans. /// be used only for generating indexed scans.
const auto &property_filters() const { return property_filters_; } const auto &property_filters() const { return property_filters_; }
@ -85,9 +88,12 @@ class Filters {
void AnalyzeFilter(Expression *, const SymbolTable &); void AnalyzeFilter(Expression *, const SymbolTable &);
std::vector<FilterInfo> all_filters_; std::vector<FilterInfo> all_filters_;
std::unordered_map<Symbol, std::set<GraphDbTypes::Label>> label_filters_; std::unordered_map<Symbol, std::unordered_set<GraphDbTypes::Label>>
std::unordered_map< label_filters_;
Symbol, std::map<GraphDbTypes::Property, std::vector<PropertyFilter>>> std::unordered_map<Symbol, std::unordered_set<GraphDbTypes::EdgeType>>
edge_type_filters_;
std::unordered_map<Symbol, std::unordered_map<GraphDbTypes::Property,
std::vector<PropertyFilter>>>
property_filters_; property_filters_;
}; };
@ -330,9 +336,10 @@ class RuleBasedPlanner {
// cannot be found, the function will return (`false`, maximum int64_t), while // cannot be found, the function will return (`false`, maximum int64_t), while
// leaving `best_label` and `best_property` unchanged. // leaving `best_label` and `best_property` unchanged.
std::pair<bool, int64_t> FindBestLabelPropertyIndex( std::pair<bool, int64_t> FindBestLabelPropertyIndex(
const std::set<GraphDbTypes::Label> &labels, const std::unordered_set<GraphDbTypes::Label> &labels,
const std::map<GraphDbTypes::Property, const std::unordered_map<GraphDbTypes::Property,
std::vector<Filters::PropertyFilter>> &property_filters, std::vector<Filters::PropertyFilter>>
&property_filters,
const Symbol &symbol, const std::unordered_set<Symbol> &bound_symbols, const Symbol &symbol, const std::unordered_set<Symbol> &bound_symbols,
GraphDbTypes::Label &best_label, GraphDbTypes::Label &best_label,
std::pair<GraphDbTypes::Property, Filters::PropertyFilter> std::pair<GraphDbTypes::Property, Filters::PropertyFilter>
@ -379,7 +386,7 @@ class RuleBasedPlanner {
} }
const GraphDbTypes::Label &FindBestLabelIndex( const GraphDbTypes::Label &FindBestLabelIndex(
const std::set<GraphDbTypes::Label> &labels) { const std::unordered_set<GraphDbTypes::Label> &labels) {
debug_assert(!labels.empty(), debug_assert(!labels.empty(),
"Trying to find the best label without any labels."); "Trying to find the best label without any labels.");
return *std::min_element(labels.begin(), labels.end(), return *std::min_element(labels.begin(), labels.end(),
@ -400,17 +407,18 @@ class RuleBasedPlanner {
const MatchContext &match_ctx, const MatchContext &match_ctx,
const std::experimental::optional<int64_t> const std::experimental::optional<int64_t>
&max_vertex_count = std::experimental::nullopt) { &max_vertex_count = std::experimental::nullopt) {
const auto labels = FindOr(match_ctx.matching.filters.label_filters(), const auto labels =
node_symbol, std::set<GraphDbTypes::Label>()) FindOr(match_ctx.matching.filters.label_filters(), node_symbol,
.first; std::unordered_set<GraphDbTypes::Label>())
.first;
if (labels.empty()) { if (labels.empty()) {
// Without labels, we cannot generated any indexed ScanAll. // Without labels, we cannot generated any indexed ScanAll.
return nullptr; return nullptr;
} }
const auto properties = const auto properties =
FindOr(match_ctx.matching.filters.property_filters(), node_symbol, FindOr(match_ctx.matching.filters.property_filters(), node_symbol,
std::map<GraphDbTypes::Property, std::unordered_map<GraphDbTypes::Property,
std::vector<Filters::PropertyFilter>>()) std::vector<Filters::PropertyFilter>>())
.first; .first;
// First, try to see if we can use label+property index. If not, use just // First, try to see if we can use label+property index. If not, use just
// the label index (which ought to exist). // the label index (which ought to exist).
@ -499,6 +507,16 @@ class RuleBasedPlanner {
} else { } else {
match_context.new_symbols.emplace_back(edge_symbol); 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<GraphDbTypes::EdgeType>())
.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<BreadthFirstAtom *>(expansion.edge)) { if (auto *bf_atom = dynamic_cast<BreadthFirstAtom *>(expansion.edge)) {
const auto &traversed_edge_symbol = const auto &traversed_edge_symbol =
symbol_table.at(*bf_atom->traversed_edge_identifier_); symbol_table.at(*bf_atom->traversed_edge_identifier_);
@ -514,7 +532,7 @@ class RuleBasedPlanner {
auto *filter_expr = impl::FindExpandVariableFilter( auto *filter_expr = impl::FindExpandVariableFilter(
bound_symbols, node_symbol, all_filters, storage); bound_symbols, node_symbol, all_filters, storage);
last_op = new ExpandVariable( 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.is_flipped, expansion.edge->lower_bound_,
expansion.edge->upper_bound_, expansion.edge->upper_bound_,
std::shared_ptr<LogicalOperator>(last_op), node1_symbol, std::shared_ptr<LogicalOperator>(last_op), node1_symbol,
@ -537,10 +555,10 @@ class RuleBasedPlanner {
existing_node = true; existing_node = true;
} }
} }
last_op = new Expand(node_symbol, edge_symbol, expansion.direction, last_op = new Expand(
std::shared_ptr<LogicalOperator>(last_op), node_symbol, edge_symbol, expansion.direction, edge_type,
node1_symbol, existing_node, existing_edge, std::shared_ptr<LogicalOperator>(last_op), node1_symbol,
match_context.graph_view); existing_node, existing_edge, match_context.graph_view);
} }
if (!existing_edge) { if (!existing_edge) {
// Ensure Cyphermorphism (different edge symbols always map to // Ensure Cyphermorphism (different edge symbols always map to

View File

@ -4,6 +4,8 @@
#include <string> #include <string>
#include <utility> #include <utility>
#include "utils/exceptions.hpp"
/** /**
* Outputs a collection of items to the given stream, separating them with the * Outputs a collection of items to the given stream, separating them with the
* given delimiter. * given delimiter.
@ -55,8 +57,32 @@ template <class TMap, class TKey, class TVal>
std::pair<TVal, bool> FindOr(const TMap &map, const TKey &key, std::pair<TVal, bool> FindOr(const TMap &map, const TKey &key,
TVal &&or_value) { TVal &&or_value) {
auto it = map.find(key); auto it = map.find(key);
if (it != map.end()) { if (it != map.end()) return {it->second, true};
return {it->second, true};
}
return {std::forward<TVal>(or_value), false}; return {std::forward<TVal>(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 <class TIterable>
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 <class TVal, class TIterable>
TVal First(TIterable &&iterable, TVal &&empty_value) {
if (iterable.begin() != iterable.end()) return *iterable.begin();
return empty_value;
}

View File

@ -146,15 +146,15 @@ TEST_F(QueryCostEstimator, ScanAllByLabelPropertyRangeNonLiteral) {
} }
TEST_F(QueryCostEstimator, Expand) { TEST_F(QueryCostEstimator, Expand) {
MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, last_op_, MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr,
NextSymbol(), false, false); last_op_, NextSymbol(), false, false);
EXPECT_COST(CardParam::kExpand * CostParam::kExpand); EXPECT_COST(CardParam::kExpand * CostParam::kExpand);
} }
TEST_F(QueryCostEstimator, ExpandVariable) { TEST_F(QueryCostEstimator, ExpandVariable) {
MakeOp<ExpandVariable>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, MakeOp<ExpandVariable>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN,
false, nullptr, nullptr, last_op_, NextSymbol(), false, nullptr, false, nullptr, nullptr, last_op_,
false); NextSymbol(), false, false);
EXPECT_COST(CardParam::kExpandVariable * CostParam::kExpandVariable); EXPECT_COST(CardParam::kExpandVariable * CostParam::kExpandVariable);
} }

View File

@ -48,8 +48,9 @@ TEST(QueryPlan, Accumulate) {
SymbolTable symbol_table; SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", auto r_m =
EdgeAtom::Direction::BOTH, false, "m", false); MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
auto one = LITERAL(1); auto one = LITERAL(1);
auto n_p = PROPERTY_LOOKUP("n", prop); auto n_p = PROPERTY_LOOKUP("n", prop);

View File

@ -172,8 +172,10 @@ struct ExpandTuple {
ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table, ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table,
std::shared_ptr<LogicalOperator> input, std::shared_ptr<LogicalOperator> input,
Symbol input_symbol, const std::string &edge_identifier, Symbol input_symbol, const std::string &edge_identifier,
EdgeAtom::Direction direction, bool existing_edge, EdgeAtom::Direction direction,
const std::string &node_identifier, bool existing_node, const GraphDbTypes::EdgeType &edge_type,
bool existing_edge, const std::string &node_identifier,
bool existing_node,
GraphView graph_view = GraphView::AS_IS) { GraphView graph_view = GraphView::AS_IS) {
auto edge = EDGE(edge_identifier, direction); auto edge = EDGE(edge_identifier, direction);
auto edge_sym = symbol_table.CreateSymbol(edge_identifier, true); 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); auto node_sym = symbol_table.CreateSymbol(node_identifier, true);
symbol_table[*node->identifier_] = node_sym; symbol_table[*node->identifier_] = node_sym;
auto op = std::make_shared<Expand>(node_sym, edge_sym, direction, input, auto op = std::make_shared<Expand>(node_sym, edge_sym, direction, edge_type,
input_symbol, existing_node, existing_edge, input, input_symbol, existing_node,
graph_view); existing_edge, graph_view);
return ExpandTuple{edge, edge_sym, node, node_sym, op}; return ExpandTuple{edge, edge_sym, node, node_sym, op};
} }

View File

@ -300,7 +300,7 @@ TEST(QueryPlan, Delete) {
{ {
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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<Identifier>("r"); auto r_get = storage.Create<Identifier>("r");
symbol_table[*r_get] = r_m.edge_sym_; symbol_table[*r_get] = r_m.edge_sym_;
auto delete_op = std::make_shared<plan::Delete>( auto delete_op = std::make_shared<plan::Delete>(
@ -352,8 +352,9 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
SymbolTable symbol_table; SymbolTable symbol_table;
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", auto r_m =
EdgeAtom::Direction::BOTH, false, "m", false); MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
// getter expressions for deletion // getter expressions for deletion
auto n_get = storage.Create<Identifier>("n"); auto n_get = storage.Create<Identifier>("n");
@ -476,7 +477,7 @@ TEST(QueryPlan, SetProperty) {
// scan (n)-[r]->(m) // scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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 // set prop1 to 42 on n and r
auto prop1 = dba->Property("prop1"); auto prop1 = dba->Property("prop1");
@ -527,7 +528,7 @@ TEST(QueryPlan, SetProperties) {
// scan (n)-[r]->(m) // scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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 auto op = update ? plan::SetProperties::Op::UPDATE
: plan::SetProperties::Op::REPLACE; : plan::SetProperties::Op::REPLACE;
@ -630,7 +631,7 @@ TEST(QueryPlan, RemoveProperty) {
// scan (n)-[r]->(m) // scan (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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); auto n_p = PROPERTY_LOOKUP("n", prop1);
symbol_table[*n_p->expression_] = n.sym_; symbol_table[*n_p->expression_] = n.sym_;
@ -706,8 +707,9 @@ TEST(QueryPlan, NodeFilterSet) {
// MATCH (n {prop: 42}) -[r]- (m) // MATCH (n {prop: 42}) -[r]- (m)
auto scan_all = MakeScanAll(storage, symbol_table, "n"); auto scan_all = MakeScanAll(storage, symbol_table, "n");
scan_all.node_->properties_[prop] = LITERAL(42); scan_all.node_->properties_[prop] = LITERAL(42);
auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, auto expand =
"r", EdgeAtom::Direction::BOTH, false, "m", false); MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
auto *filter_expr = auto *filter_expr =
EQ(storage.Create<PropertyLookup>(scan_all.node_->identifier_, prop), EQ(storage.Create<PropertyLookup>(scan_all.node_->identifier_, prop),
LITERAL(42)); LITERAL(42));
@ -745,8 +747,9 @@ TEST(QueryPlan, FilterRemove) {
// MATCH (n) -[r]- (m) WHERE n.prop < 43 // MATCH (n) -[r]- (m) WHERE n.prop < 43
auto scan_all = MakeScanAll(storage, symbol_table, "n"); auto scan_all = MakeScanAll(storage, symbol_table, "n");
scan_all.node_->properties_[prop] = LITERAL(42); scan_all.node_->properties_[prop] = LITERAL(42);
auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, auto expand =
"r", EdgeAtom::Direction::BOTH, false, "m", false); 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); auto filter_prop = PROPERTY_LOOKUP("n", prop);
symbol_table[*filter_prop->expression_] = scan_all.sym_; symbol_table[*filter_prop->expression_] = scan_all.sym_;
auto filter = auto filter =
@ -808,8 +811,9 @@ TEST(QueryPlan, Merge) {
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
// merge_match branch // merge_match branch
auto r_m = MakeExpand(storage, symbol_table, std::make_shared<Once>(), n.sym_, auto r_m =
"r", EdgeAtom::Direction::BOTH, false, "m", false); MakeExpand(storage, symbol_table, std::make_shared<Once>(), n.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
auto m_p = PROPERTY_LOOKUP("m", prop); auto m_p = PROPERTY_LOOKUP("m", prop);
symbol_table[*m_p->expression_] = r_m.node_sym_; symbol_table[*m_p->expression_] = r_m.node_sym_;
auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, m_p, LITERAL(1)); auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, m_p, LITERAL(1));

View File

@ -240,7 +240,7 @@ TEST(QueryPlan, Expand) {
auto test_expand = [&](EdgeAtom::Direction direction, GraphView graph_view) { auto test_expand = [&](EdgeAtom::Direction direction, GraphView graph_view) {
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", direction, 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 // make a named expression and a produce
auto output = NEXPR("m", IDENT("m")); auto output = NEXPR("m", IDENT("m"));
@ -346,6 +346,7 @@ class QueryPlanExpandVariable : public testing::Test {
std::shared_ptr<LogicalOperator> AddMatch( std::shared_ptr<LogicalOperator> AddMatch(
std::shared_ptr<LogicalOperator> input_op, const std::string &node_from, std::shared_ptr<LogicalOperator> input_op, const std::string &node_from,
int layer, EdgeAtom::Direction direction, int layer, EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
std::experimental::optional<size_t> lower, std::experimental::optional<size_t> lower,
std::experimental::optional<size_t> upper, Symbol edge_sym, std::experimental::optional<size_t> upper, Symbol edge_sym,
bool existing_edge, const std::string &node_to, bool existing_edge, const std::string &node_to,
@ -367,13 +368,13 @@ class QueryPlanExpandVariable : public testing::Test {
return bound ? LITERAL(static_cast<int64_t>(bound.value())) : nullptr; return bound ? LITERAL(static_cast<int64_t>(bound.value())) : nullptr;
}; };
return std::make_shared<ExpandVariable>( return std::make_shared<ExpandVariable>(
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, convert(upper), filter_op, n_from.sym_, false, existing_edge,
graph_view); graph_view);
} else } else
return std::make_shared<Expand>(n_to_sym, edge_sym, direction, filter_op, return std::make_shared<Expand>(n_to_sym, edge_sym, direction, edge_type,
n_from.sym_, false, existing_edge, filter_op, n_from.sym_, false,
graph_view); existing_edge, graph_view);
} }
/* Creates an edge (in the frame and symbol table). Returns the symbol. */ /* Creates an edge (in the frame and symbol table). Returns the symbol. */
@ -412,10 +413,10 @@ TEST_F(QueryPlanExpandVariable, OneVariableExpansion) {
std::experimental::optional<size_t> upper, std::experimental::optional<size_t> upper,
bool reverse) { bool reverse) {
auto e = Edge("r", direction); auto e = Edge("r", direction);
return GetResults( return GetResults(AddMatch<ExpandVariable>(nullptr, "n", layer, direction,
AddMatch<ExpandVariable>(nullptr, "n", layer, direction, lower, upper, nullptr, lower, upper, e, false,
e, false, "m", GraphView::AS_IS, reverse), "m", GraphView::AS_IS, reverse),
e); e);
}; };
for (int reverse = 0; reverse < 2; ++reverse) { for (int reverse = 0; reverse < 2; ++reverse) {
@ -476,19 +477,20 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
if (single_expansion_before) { if (single_expansion_before) {
symbols.push_back(Edge("r0", direction)); symbols.push_back(Edge("r0", direction));
last_op = AddMatch<Expand>(last_op, "n0", layer, direction, lower, upper, last_op = AddMatch<Expand>(last_op, "n0", layer, direction, nullptr,
symbols.back(), false, "m0"); lower, upper, symbols.back(), false, "m0");
} }
auto var_length_sym = Edge("r1", direction); auto var_length_sym = Edge("r1", direction);
symbols.push_back(var_length_sym); symbols.push_back(var_length_sym);
last_op = AddMatch<ExpandVariable>(last_op, "n1", layer, direction, lower, last_op =
upper, var_length_sym, false, "m1"); AddMatch<ExpandVariable>(last_op, "n1", layer, direction, nullptr,
lower, upper, var_length_sym, false, "m1");
if (!single_expansion_before) { if (!single_expansion_before) {
symbols.push_back(Edge("r2", direction)); symbols.push_back(Edge("r2", direction));
last_op = AddMatch<Expand>(last_op, "n2", layer, direction, lower, upper, last_op = AddMatch<Expand>(last_op, "n2", layer, direction, nullptr,
symbols.back(), false, "m2"); lower, upper, symbols.back(), false, "m2");
} }
if (add_uniqueness_check) { if (add_uniqueness_check) {
@ -517,11 +519,12 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) {
std::experimental::optional<size_t> upper, std::experimental::optional<size_t> upper,
bool add_uniqueness_check) { bool add_uniqueness_check) {
auto e1 = Edge("r1", direction); auto e1 = Edge("r1", direction);
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, auto first =
lower, upper, e1, false, "m1"); AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, nullptr,
lower, upper, e1, false, "m1");
auto e2 = Edge("r2", direction); auto e2 = Edge("r2", direction);
auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction, auto last_op = AddMatch<ExpandVariable>(
lower, upper, e2, false, "m2"); first, "n2", layer, direction, nullptr, lower, upper, e2, false, "m2");
if (add_uniqueness_check) { if (add_uniqueness_check) {
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>( last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(
last_op, e2, std::vector<Symbol>{e1}); last_op, e2, std::vector<Symbol>{e1});
@ -542,11 +545,13 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) {
std::experimental::optional<size_t> upper, std::experimental::optional<size_t> upper,
bool same_edge_symbol) { bool same_edge_symbol) {
auto e1 = Edge("r1", direction); auto e1 = Edge("r1", direction);
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, auto first =
lower, upper, e1, false, "m1"); AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, nullptr,
lower, upper, e1, false, "m1");
auto e2 = same_edge_symbol ? e1 : Edge("r2", direction); auto e2 = same_edge_symbol ? e1 : Edge("r2", direction);
auto second = AddMatch<ExpandVariable>(first, "n2", layer, direction, lower, auto second =
upper, e2, same_edge_symbol, "m2"); AddMatch<ExpandVariable>(first, "n2", layer, direction, nullptr, lower,
upper, e2, same_edge_symbol, "m2");
return GetResults(second, e2); return GetResults(second, e2);
}; };
@ -572,30 +577,40 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) {
} }
TEST_F(QueryPlanExpandVariable, GraphState) { 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); auto e = Edge("r", EdgeAtom::Direction::OUT);
return GetResults( return GetResults(
AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, 2, AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT,
2, e, false, "m", graph_view), edge_type, 2, 2, e, false, "m", graph_view),
e); 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 // add two vertices branching out from the second layer
for (VertexAccessor &vertex : dba->Vertices(true)) for (VertexAccessor &vertex : dba->Vertices(true))
if (vertex.has_label(labels[1])) { if (vertex.has_label(labels[1])) {
auto new_vertex = dba->InsertVertex(); 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(false)), 6);
ASSERT_EQ(CountIterable(dba->Vertices(true)), 8); ASSERT_EQ(CountIterable(dba->Vertices(true)), 8);
EXPECT_EQ(test_expand(GraphView::OLD), (map_int{{2, 8}})); EXPECT_EQ(test_expand(GraphView::OLD, nullptr), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(GraphView::NEW), (map_int{{2, 12}})); 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(); dba->AdvanceCommand();
EXPECT_EQ(test_expand(GraphView::OLD), (map_int{{2, 12}})); for (const auto graph_view : {GraphView::OLD, GraphView::NEW}) {
EXPECT_EQ(test_expand(GraphView::NEW), (map_int{{2, 12}})); 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 { namespace std {
@ -851,7 +866,7 @@ TEST(QueryPlan, ExpandOptional) {
// MATCH (n) OPTIONAL MATCH (n)-[r]->(m) // MATCH (n) OPTIONAL MATCH (n)-[r]->(m)
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, nullptr, n.sym_, "r", 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<plan::Optional>( auto optional = std::make_shared<plan::Optional>(
n.op_, r_m.op_, std::vector<Symbol>{r_m.edge_sym_, r_m.node_sym_}); n.op_, r_m.op_, std::vector<Symbol>{r_m.edge_sym_, r_m.node_sym_});
@ -926,7 +941,7 @@ TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) {
auto with = MakeProduce(optional, n_ne); auto with = MakeProduce(optional, n_ne);
// MATCH (n) -[r]-> (m) // MATCH (n) -[r]-> (m)
auto r_m = MakeExpand(storage, symbol_table, with, with_n_sym, "r", 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 // RETURN m
auto m_ne = NEXPR("m", IDENT("m")); auto m_ne = NEXPR("m", IDENT("m"));
symbol_table[*m_ne->expression_] = r_m.node_sym_; symbol_table[*m_ne->expression_] = r_m.node_sym_;
@ -973,8 +988,9 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
symbol_table[*edge->identifier_] = edge_sym; symbol_table[*edge->identifier_] = edge_sym;
auto node = NODE("n"); auto node = NODE("n");
symbol_table[*node->identifier_] = with_n_sym; symbol_table[*node->identifier_] = with_n_sym;
auto expand = std::make_shared<plan::Expand>( auto expand =
with_n_sym, edge_sym, edge_direction, m.op_, m.sym_, true, false); std::make_shared<plan::Expand>(with_n_sym, edge_sym, edge_direction,
nullptr, m.op_, m.sym_, true, false);
// RETURN m // RETURN m
auto m_ne = NEXPR("m", IDENT("m")); auto m_ne = NEXPR("m", IDENT("m"));
symbol_table[*m_ne->expression_] = m.sym_; symbol_table[*m_ne->expression_] = m.sym_;
@ -1005,7 +1021,7 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingEdge) {
storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_); storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
auto node_filter = std::make_shared<Filter>(n.op_, filter_expr); auto node_filter = std::make_shared<Filter>(n.op_, filter_expr);
auto r_m = MakeExpand(storage, symbol_table, node_filter, n.sym_, "r", 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<plan::Optional>( auto optional = std::make_shared<plan::Optional>(
nullptr, r_m.op_, nullptr, r_m.op_,
std::vector<Symbol>{n.sym_, r_m.edge_sym_, r_m.node_sym_}); std::vector<Symbol>{n.sym_, r_m.edge_sym_, r_m.node_sym_});
@ -1023,8 +1039,9 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingEdge) {
auto node = NODE("n"); auto node = NODE("n");
auto node_sym = symbol_table.CreateSymbol("b", true); auto node_sym = symbol_table.CreateSymbol("b", true);
symbol_table[*node->identifier_] = node_sym; symbol_table[*node->identifier_] = node_sym;
auto expand = std::make_shared<plan::Expand>( auto expand =
node_sym, with_r_sym, edge_direction, a.op_, a.sym_, false, true); std::make_shared<plan::Expand>(node_sym, with_r_sym, edge_direction,
nullptr, a.op_, a.sym_, false, true);
// RETURN a // RETURN a
auto a_ne = NEXPR("a", IDENT("a")); auto a_ne = NEXPR("a", IDENT("a"));
symbol_table[*a_ne->expression_] = a.sym_; 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 test_existing = [&](bool with_existing, int expected_result_count) {
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_n = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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) if (with_existing)
r_n.op_ = r_n.op_ = std::make_shared<Expand>(n.sym_, r_n.edge_sym_,
std::make_shared<Expand>(n.sym_, r_n.edge_sym_, r_n.edge_->direction_, r_n.edge_->direction_, nullptr, n.op_,
n.op_, n.sym_, with_existing, false); n.sym_, with_existing, false);
// make a named expression and a produce // make a named expression and a produce
auto output = NEXPR("n", IDENT("n")); auto output = NEXPR("n", IDENT("n"));
@ -1095,14 +1113,16 @@ TEST(QueryPlan, ExpandExistingEdge) {
auto test_existing = [&](bool with_existing, int expected_result_count) { auto test_existing = [&](bool with_existing, int expected_result_count) {
auto i = MakeScanAll(storage, symbol_table, "i"); auto i = MakeScanAll(storage, symbol_table, "i");
auto r_j = MakeExpand(storage, symbol_table, i.op_, i.sym_, "r", auto r_j =
EdgeAtom::Direction::BOTH, false, "j", false); 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", 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) if (with_existing)
r_k.op_ = std::make_shared<Expand>(r_k.node_sym_, r_j.edge_sym_, r_k.op_ = std::make_shared<Expand>(
r_k.edge_->direction_, r_j.op_, r_k.node_sym_, r_j.edge_sym_, r_k.edge_->direction_, nullptr, r_j.op_,
r_j.node_sym_, false, with_existing); r_j.node_sym_, false, with_existing);
// make a named expression and a produce // make a named expression and a produce
auto output = NEXPR("r", IDENT("r")); auto output = NEXPR("r", IDENT("r"));
@ -1135,7 +1155,7 @@ TEST(QueryPlan, ExpandBothCycleEdgeCase) {
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_ = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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)); EXPECT_EQ(1, PullAll(r_.op_, *dba, symbol_table));
} }
@ -1177,17 +1197,17 @@ TEST(QueryPlan, EdgeFilter) {
auto test_filter = [&]() { auto test_filter = [&]() {
// define an operator tree for query // 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 n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", const auto &edge_type = edge_types[0];
EdgeAtom::Direction::OUT, false, "m", false); auto r_m =
r_m.edge_->edge_types_.push_back(edge_types[0]); 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); r_m.edge_->properties_[prop] = LITERAL(42);
auto *filter_expr = auto *filter_expr =
AND(storage.Create<EdgeTypeTest>(r_m.edge_->identifier_, EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42));
r_m.edge_->edge_types_),
EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42)));
auto edge_filter = std::make_shared<Filter>(r_m.op_, filter_expr); auto edge_filter = std::make_shared<Filter>(r_m.op_, filter_expr);
// make a named expression and a produce // make a named expression and a produce
@ -1228,7 +1248,7 @@ TEST(QueryPlan, EdgeFilterMultipleTypes) {
// make a scan all // make a scan all
auto n = MakeScanAll(storage, symbol_table, "n"); auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", 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 // add an edge type filter
r_m.edge_->edge_types_.push_back(type_1); r_m.edge_->edge_types_.push_back(type_1);
r_m.edge_->edge_types_.push_back(type_2); r_m.edge_->edge_types_.push_back(type_2);
@ -1295,14 +1315,16 @@ TEST(QueryPlan, ExpandUniquenessFilter) {
SymbolTable symbol_table; SymbolTable symbol_table;
auto n1 = MakeScanAll(storage, symbol_table, "n1"); auto n1 = MakeScanAll(storage, symbol_table, "n1");
auto r1_n2 = MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1", auto r1_n2 =
EdgeAtom::Direction::OUT, false, "n2", false); MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1",
EdgeAtom::Direction::OUT, nullptr, false, "n2", false);
std::shared_ptr<LogicalOperator> last_op = r1_n2.op_; std::shared_ptr<LogicalOperator> last_op = r1_n2.op_;
if (vertex_uniqueness) if (vertex_uniqueness)
last_op = std::make_shared<ExpandUniquenessFilter<VertexAccessor>>( last_op = std::make_shared<ExpandUniquenessFilter<VertexAccessor>>(
last_op, r1_n2.node_sym_, std::vector<Symbol>{n1.sym_}); last_op, r1_n2.node_sym_, std::vector<Symbol>{n1.sym_});
auto r2_n3 = MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, auto r2_n3 =
"r2", EdgeAtom::Direction::OUT, false, "n3", false); MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, "r2",
EdgeAtom::Direction::OUT, nullptr, false, "n3", false);
last_op = r2_n3.op_; last_op = r2_n3.op_;
if (edge_uniqueness) if (edge_uniqueness)
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>( last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(