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:
parent
8aa4b5aa74
commit
35f726dfd2
@ -312,10 +312,10 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyRange::MakeCursor(
|
||||
ExpressionEvaluator evaluator(frame, symbol_table, db, graph_view_);
|
||||
auto convert = [&evaluator](const auto &bound)
|
||||
-> std::experimental::optional<utils::Bound<PropertyValue>> {
|
||||
if (!bound) return std::experimental::nullopt;
|
||||
return std::experimental::make_optional(utils::Bound<PropertyValue>(
|
||||
bound.value().value()->Accept(evaluator), bound.value().type()));
|
||||
};
|
||||
if (!bound) return std::experimental::nullopt;
|
||||
return std::experimental::make_optional(utils::Bound<PropertyValue>(
|
||||
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<Cursor> ScanAllByLabelPropertyValue::MakeCursor(
|
||||
|
||||
ExpandCommon::ExpandCommon(Symbol node_symbol, Symbol edge_symbol,
|
||||
EdgeAtom::Direction direction,
|
||||
const GraphDbTypes::EdgeType &edge_type,
|
||||
const std::shared_ptr<LogicalOperator> &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<Once>()),
|
||||
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<LogicalOperator> &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 <accessor, direction>
|
||||
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<decltype(wrapper(direction, vertex.in()))> 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<decltype(ExpandFromVertex(std::declval<VertexAccessor>(),
|
||||
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<int64_t>(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());
|
||||
}
|
||||
|
||||
|
@ -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<LogicalOperator> &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<LogicalOperator> &input,
|
||||
Symbol input_symbol, bool existing_node, bool existing_edge,
|
||||
|
@ -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);
|
||||
|
@ -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<FilterInfo> all_filters_;
|
||||
std::unordered_map<Symbol, std::set<GraphDbTypes::Label>> label_filters_;
|
||||
std::unordered_map<
|
||||
Symbol, std::map<GraphDbTypes::Property, std::vector<PropertyFilter>>>
|
||||
std::unordered_map<Symbol, std::unordered_set<GraphDbTypes::Label>>
|
||||
label_filters_;
|
||||
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_;
|
||||
};
|
||||
|
||||
@ -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<bool, int64_t> FindBestLabelPropertyIndex(
|
||||
const std::set<GraphDbTypes::Label> &labels,
|
||||
const std::map<GraphDbTypes::Property,
|
||||
std::vector<Filters::PropertyFilter>> &property_filters,
|
||||
const std::unordered_set<GraphDbTypes::Label> &labels,
|
||||
const std::unordered_map<GraphDbTypes::Property,
|
||||
std::vector<Filters::PropertyFilter>>
|
||||
&property_filters,
|
||||
const Symbol &symbol, const std::unordered_set<Symbol> &bound_symbols,
|
||||
GraphDbTypes::Label &best_label,
|
||||
std::pair<GraphDbTypes::Property, Filters::PropertyFilter>
|
||||
@ -379,7 +386,7 @@ class RuleBasedPlanner {
|
||||
}
|
||||
|
||||
const GraphDbTypes::Label &FindBestLabelIndex(
|
||||
const std::set<GraphDbTypes::Label> &labels) {
|
||||
const std::unordered_set<GraphDbTypes::Label> &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<int64_t>
|
||||
&max_vertex_count = std::experimental::nullopt) {
|
||||
const auto labels = FindOr(match_ctx.matching.filters.label_filters(),
|
||||
node_symbol, std::set<GraphDbTypes::Label>())
|
||||
.first;
|
||||
const auto labels =
|
||||
FindOr(match_ctx.matching.filters.label_filters(), node_symbol,
|
||||
std::unordered_set<GraphDbTypes::Label>())
|
||||
.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<GraphDbTypes::Property,
|
||||
std::vector<Filters::PropertyFilter>>())
|
||||
std::unordered_map<GraphDbTypes::Property,
|
||||
std::vector<Filters::PropertyFilter>>())
|
||||
.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<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)) {
|
||||
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<LogicalOperator>(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<LogicalOperator>(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<LogicalOperator>(last_op), node1_symbol,
|
||||
existing_node, existing_edge, match_context.graph_view);
|
||||
}
|
||||
if (!existing_edge) {
|
||||
// Ensure Cyphermorphism (different edge symbols always map to
|
||||
|
@ -4,6 +4,8 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
/**
|
||||
* Outputs a collection of items to the given stream, separating them with the
|
||||
* given delimiter.
|
||||
@ -55,8 +57,32 @@ template <class TMap, class TKey, class TVal>
|
||||
std::pair<TVal, bool> 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<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;
|
||||
}
|
||||
|
@ -146,15 +146,15 @@ TEST_F(QueryCostEstimator, ScanAllByLabelPropertyRangeNonLiteral) {
|
||||
}
|
||||
|
||||
TEST_F(QueryCostEstimator, Expand) {
|
||||
MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, last_op_,
|
||||
NextSymbol(), false, false);
|
||||
MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr,
|
||||
last_op_, NextSymbol(), false, false);
|
||||
EXPECT_COST(CardParam::kExpand * CostParam::kExpand);
|
||||
}
|
||||
|
||||
TEST_F(QueryCostEstimator, ExpandVariable) {
|
||||
MakeOp<ExpandVariable>(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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -172,8 +172,10 @@ struct ExpandTuple {
|
||||
ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table,
|
||||
std::shared_ptr<LogicalOperator> 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<Expand>(node_sym, edge_sym, direction, input,
|
||||
input_symbol, existing_node, existing_edge,
|
||||
graph_view);
|
||||
auto op = std::make_shared<Expand>(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};
|
||||
}
|
||||
|
@ -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<Identifier>("r");
|
||||
symbol_table[*r_get] = r_m.edge_sym_;
|
||||
auto delete_op = std::make_shared<plan::Delete>(
|
||||
@ -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<Identifier>("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<PropertyLookup>(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<Once>(), n.sym_,
|
||||
"r", EdgeAtom::Direction::BOTH, false, "m", false);
|
||||
auto r_m =
|
||||
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);
|
||||
symbol_table[*m_p->expression_] = r_m.node_sym_;
|
||||
auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, m_p, LITERAL(1));
|
||||
|
@ -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<LogicalOperator> AddMatch(
|
||||
std::shared_ptr<LogicalOperator> input_op, const std::string &node_from,
|
||||
int layer, EdgeAtom::Direction direction,
|
||||
const GraphDbTypes::EdgeType &edge_type,
|
||||
std::experimental::optional<size_t> lower,
|
||||
std::experimental::optional<size_t> 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<int64_t>(bound.value())) : nullptr;
|
||||
};
|
||||
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,
|
||||
graph_view);
|
||||
} else
|
||||
return std::make_shared<Expand>(n_to_sym, edge_sym, direction, filter_op,
|
||||
n_from.sym_, false, existing_edge,
|
||||
graph_view);
|
||||
return std::make_shared<Expand>(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<size_t> upper,
|
||||
bool reverse) {
|
||||
auto e = Edge("r", direction);
|
||||
return GetResults(
|
||||
AddMatch<ExpandVariable>(nullptr, "n", layer, direction, lower, upper,
|
||||
e, false, "m", GraphView::AS_IS, reverse),
|
||||
e);
|
||||
return GetResults(AddMatch<ExpandVariable>(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<Expand>(last_op, "n0", layer, direction, lower, upper,
|
||||
symbols.back(), false, "m0");
|
||||
last_op = AddMatch<Expand>(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<ExpandVariable>(last_op, "n1", layer, direction, lower,
|
||||
upper, var_length_sym, false, "m1");
|
||||
last_op =
|
||||
AddMatch<ExpandVariable>(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<Expand>(last_op, "n2", layer, direction, lower, upper,
|
||||
symbols.back(), false, "m2");
|
||||
last_op = AddMatch<Expand>(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<size_t> upper,
|
||||
bool add_uniqueness_check) {
|
||||
auto e1 = Edge("r1", direction);
|
||||
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction,
|
||||
lower, upper, e1, false, "m1");
|
||||
auto first =
|
||||
AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, nullptr,
|
||||
lower, upper, e1, false, "m1");
|
||||
auto e2 = Edge("r2", direction);
|
||||
auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction,
|
||||
lower, upper, e2, false, "m2");
|
||||
auto last_op = AddMatch<ExpandVariable>(
|
||||
first, "n2", layer, direction, nullptr, lower, upper, e2, false, "m2");
|
||||
if (add_uniqueness_check) {
|
||||
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(
|
||||
last_op, e2, std::vector<Symbol>{e1});
|
||||
@ -542,11 +545,13 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) {
|
||||
std::experimental::optional<size_t> upper,
|
||||
bool same_edge_symbol) {
|
||||
auto e1 = Edge("r1", direction);
|
||||
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction,
|
||||
lower, upper, e1, false, "m1");
|
||||
auto first =
|
||||
AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, nullptr,
|
||||
lower, upper, e1, false, "m1");
|
||||
auto e2 = same_edge_symbol ? e1 : Edge("r2", direction);
|
||||
auto second = AddMatch<ExpandVariable>(first, "n2", layer, direction, lower,
|
||||
upper, e2, same_edge_symbol, "m2");
|
||||
auto second =
|
||||
AddMatch<ExpandVariable>(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<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, 2,
|
||||
2, e, false, "m", graph_view),
|
||||
AddMatch<ExpandVariable>(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<plan::Optional>(
|
||||
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);
|
||||
// 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<plan::Expand>(
|
||||
with_n_sym, edge_sym, edge_direction, m.op_, m.sym_, true, false);
|
||||
auto expand =
|
||||
std::make_shared<plan::Expand>(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<LabelsTest>(n.node_->identifier_, n.node_->labels_);
|
||||
auto node_filter = std::make_shared<Filter>(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<plan::Optional>(
|
||||
nullptr, r_m.op_,
|
||||
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_sym = symbol_table.CreateSymbol("b", true);
|
||||
symbol_table[*node->identifier_] = node_sym;
|
||||
auto expand = std::make_shared<plan::Expand>(
|
||||
node_sym, with_r_sym, edge_direction, a.op_, a.sym_, false, true);
|
||||
auto expand =
|
||||
std::make_shared<plan::Expand>(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<Expand>(n.sym_, r_n.edge_sym_, r_n.edge_->direction_,
|
||||
n.op_, n.sym_, with_existing, false);
|
||||
r_n.op_ = std::make_shared<Expand>(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<Expand>(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<Expand>(
|
||||
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<EdgeTypeTest>(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<Filter>(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<LogicalOperator> last_op = r1_n2.op_;
|
||||
if (vertex_uniqueness)
|
||||
last_op = std::make_shared<ExpandUniquenessFilter<VertexAccessor>>(
|
||||
last_op, r1_n2.node_sym_, std::vector<Symbol>{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<ExpandUniquenessFilter<EdgeAccessor>>(
|
||||
|
Loading…
Reference in New Issue
Block a user