Edges data structure now supports multiple edge filtering (implicit OR)

Summary: - modified all utils/algorithm functions to be inline and in the utils namespace

Reviewers: teon.banek

Reviewed By: teon.banek

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D830
This commit is contained in:
florijan 2017-09-26 12:51:52 +02:00
parent f91aa7b8fe
commit a9381df09e
26 changed files with 447 additions and 421 deletions

View File

@ -5,4 +5,5 @@ Standard: "C++11"
UseTab: Never
DerivePointerAlignment: false
PointerAlignment: Right
ColumnLimit : 80
...

View File

@ -1,21 +1,19 @@
#include "gtest/gtest.h"
#include "reactors_local.hpp"
#include "gtest/gtest.h"
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <future>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <future>
TEST(SystemTest, ReturnWithoutThrowing) {
struct Master : public Reactor {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
CloseChannel("main");
}
virtual void Run() { CloseChannel("main"); }
};
System &system = System::GetInstance();
@ -23,7 +21,6 @@ TEST(SystemTest, ReturnWithoutThrowing) {
ASSERT_NO_THROW(system.AwaitShutdown());
}
TEST(ChannelCreationTest, ThrowOnReusingChannelName) {
struct Master : public Reactor {
Master(std::string name) : Reactor(name) {}
@ -40,13 +37,13 @@ TEST(ChannelCreationTest, ThrowOnReusingChannelName) {
system.AwaitShutdown();
}
TEST(ChannelSetUpTest, CheckMainChannelIsSet) {
struct Master : public Reactor {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
CloseChannel("main");
@ -57,7 +54,8 @@ TEST(ChannelSetUpTest, CheckMainChannelIsSet) {
Worker(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("master", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("master", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::this_thread::sleep_for(std::chrono::milliseconds(300));
CloseChannel("main");
@ -80,7 +78,8 @@ TEST(SimpleSendTest, OneCallback) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(888);
CloseChannel("main");
@ -90,12 +89,13 @@ TEST(SimpleSendTest, OneCallback) {
struct Worker : public Reactor {
Worker(std::string name) : Reactor(name) {}
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEvent<MessageInt>([this](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 888);
CloseChannel("main");
});
stream->OnEvent<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 888);
CloseChannel("main");
});
}
};
@ -105,7 +105,6 @@ TEST(SimpleSendTest, OneCallback) {
system.AwaitShutdown();
}
TEST(SimpleSendTest, IgnoreAfterClose) {
struct MessageInt : public Message {
MessageInt(int xx) : x(xx) {}
@ -116,26 +115,29 @@ TEST(SimpleSendTest, IgnoreAfterClose) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(101);
channel_writer->Send<MessageInt>(102); // should be ignored
channel_writer->Send<MessageInt>(102); // should be ignored
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(103); // should be ignored
channel_writer->Send<MessageInt>(104); // should be ignored
CloseChannel("main"); // Write-end doesn't need to be closed because it's in RAII.
channel_writer->Send<MessageInt>(103); // should be ignored
channel_writer->Send<MessageInt>(104); // should be ignored
CloseChannel(
"main"); // Write-end doesn't need to be closed because it's in RAII.
}
};
struct Worker : public Reactor {
Worker(std::string name) : Reactor(name) {}
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEvent<MessageInt>([this](const MessageInt& msg, const Subscription&) {
CloseChannel("main");
ASSERT_EQ(msg.x, 101);
});
stream->OnEvent<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
CloseChannel("main");
ASSERT_EQ(msg.x, 101);
});
}
};
@ -152,20 +154,21 @@ TEST(SimpleSendTest, DuringFirstEvent) {
};
struct Master : public Reactor {
Master(std::string name, std::promise<int> p) : Reactor(name), p_(std::move(p)) {}
Master(std::string name, std::promise<int> p)
: Reactor(name), p_(std::move(p)) {}
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEvent<MessageInt>([this](const Message &msg, const Subscription &subscription) {
const MessageInt &msgint = dynamic_cast<const MessageInt&>(msg);
if (msgint.x == 101)
FindChannel("main")->Send<MessageInt>(102);
if (msgint.x == 102) {
subscription.Unsubscribe();
CloseChannel("main");
p_.set_value(777);
}
});
stream->OnEvent<MessageInt>(
[this](const Message &msg, const Subscription &subscription) {
const MessageInt &msgint = dynamic_cast<const MessageInt &>(msg);
if (msgint.x == 101) FindChannel("main")->Send<MessageInt>(102);
if (msgint.x == 102) {
subscription.Unsubscribe();
CloseChannel("main");
p_.set_value(777);
}
});
std::shared_ptr<ChannelWriter> channel_writer = FindChannel("main");
channel_writer->Send<MessageInt>(101);
@ -182,7 +185,6 @@ TEST(SimpleSendTest, DuringFirstEvent) {
system.AwaitShutdown();
}
TEST(MultipleSendTest, UnsubscribeService) {
struct MessageInt : public Message {
MessageInt(int xx) : x(xx) {}
@ -197,7 +199,8 @@ TEST(MultipleSendTest, UnsubscribeService) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(55);
channel_writer->Send<MessageInt>(66);
@ -218,24 +221,26 @@ TEST(MultipleSendTest, UnsubscribeService) {
int num_msgs_received = 0;
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const Subscription &subscription) {
ASSERT_TRUE(msgint.x == 55 || msgint.x == 66);
++num_msgs_received;
if (msgint.x == 66) {
subscription.Unsubscribe(); // receive only two of them
}
});
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const Subscription &subscription) {
char c = msgchar.x;
++num_msgs_received;
ASSERT_TRUE(c == 'a' || c == 'b' || c == 'c');
if (num_msgs_received == 5) {
subscription.Unsubscribe();
CloseChannel("main");
}
});
stream->OnEvent<MessageInt>(
[this](const MessageInt &msgint, const Subscription &subscription) {
ASSERT_TRUE(msgint.x == 55 || msgint.x == 66);
++num_msgs_received;
if (msgint.x == 66) {
subscription.Unsubscribe(); // receive only two of them
}
});
stream->OnEvent<MessageChar>(
[this](const MessageChar &msgchar, const Subscription &subscription) {
char c = msgchar.x;
++num_msgs_received;
ASSERT_TRUE(c == 'a' || c == 'b' || c == 'c');
if (num_msgs_received == 5) {
subscription.Unsubscribe();
CloseChannel("main");
}
});
}
};
@ -245,7 +250,6 @@ TEST(MultipleSendTest, UnsubscribeService) {
system.AwaitShutdown();
}
TEST(MultipleSendTest, OnEvent) {
struct MessageInt : public Message {
MessageInt(int xx) : x(xx) {}
@ -260,7 +264,8 @@ TEST(MultipleSendTest, OnEvent) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(101);
@ -278,27 +283,30 @@ TEST(MultipleSendTest, OnEvent) {
int correct_vals = 0;
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
correct_vals = 0;
stream->OnEvent<MessageInt>([this](const MessageInt &msgint, const Subscription&) {
ASSERT_TRUE(msgint.x == 101 || msgint.x == 103);
++correct_vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<MessageInt>(
[this](const MessageInt &msgint, const Subscription &) {
ASSERT_TRUE(msgint.x == 101 || msgint.x == 103);
++correct_vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<MessageChar>([this](const MessageChar &msgchar, const Subscription&) {
ASSERT_TRUE(msgchar.x == 'a' || msgchar.x == 'b');
++correct_vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<MessageChar>(
[this](const MessageChar &msgchar, const Subscription &) {
ASSERT_TRUE(msgchar.x == 'a' || msgchar.x == 'b');
++correct_vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<EndMessage>([this](const EndMessage&, const Subscription&) {
ASSERT_LE(correct_vals, 4);
if (correct_vals == 4) {
CloseChannel("main");
}
});
stream->OnEvent<EndMessage>(
[this](const EndMessage &, const Subscription &) {
ASSERT_LE(correct_vals, 4);
if (correct_vals == 4) {
CloseChannel("main");
}
});
}
};
@ -318,7 +326,8 @@ TEST(MultipleSendTest, Chaining) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageInt>(55);
channel_writer->Send<MessageInt>(66);
@ -331,19 +340,22 @@ TEST(MultipleSendTest, Chaining) {
Worker(std::string name) : Reactor(name) {}
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEventOnce()
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 55);
})
.ChainOnce<MessageInt>([](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 66);
})
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 77);
CloseChannel("main");
});
.ChainOnce<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 55);
})
.ChainOnce<MessageInt>(
[](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 66);
})
.ChainOnce<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 77);
CloseChannel("main");
});
}
};
@ -353,7 +365,6 @@ TEST(MultipleSendTest, Chaining) {
system.AwaitShutdown();
}
TEST(MultipleSendTest, ChainingInRightOrder) {
struct MessageInt : public Message {
MessageInt(int xx) : x(xx) {}
@ -369,7 +380,8 @@ TEST(MultipleSendTest, ChainingInRightOrder) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
channel_writer->Send<MessageChar>('a');
channel_writer->Send<MessageInt>(55);
@ -383,19 +395,22 @@ TEST(MultipleSendTest, ChainingInRightOrder) {
Worker(std::string name) : Reactor(name) {}
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
stream->OnEventOnce()
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 55);
})
.ChainOnce<MessageChar>([](const MessageChar &msg, const Subscription&) {
ASSERT_EQ(msg.x, 'b');
})
.ChainOnce<MessageInt>([this](const MessageInt &msg, const Subscription&) {
ASSERT_EQ(msg.x, 77);
CloseChannel("main");
});
.ChainOnce<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 55);
})
.ChainOnce<MessageChar>(
[](const MessageChar &msg, const Subscription &) {
ASSERT_EQ(msg.x, 'b');
})
.ChainOnce<MessageInt>(
[this](const MessageInt &msg, const Subscription &) {
ASSERT_EQ(msg.x, 77);
CloseChannel("main");
});
}
};
@ -405,7 +420,6 @@ TEST(MultipleSendTest, ChainingInRightOrder) {
system.AwaitShutdown();
}
TEST(MultipleSendTest, ProcessManyMessages) {
const static int num_tests = 100;
@ -418,7 +432,8 @@ TEST(MultipleSendTest, ProcessManyMessages) {
Master(std::string name) : Reactor(name) {}
virtual void Run() {
std::shared_ptr<ChannelWriter> channel_writer;
while (!(channel_writer = System::GetInstance().FindChannel("worker", "main")))
while (!(channel_writer =
System::GetInstance().FindChannel("worker", "main")))
std::this_thread::sleep_for(std::chrono::milliseconds(300));
std::this_thread::sleep_for(std::chrono::milliseconds(rand() % 100));
@ -437,20 +452,22 @@ TEST(MultipleSendTest, ProcessManyMessages) {
int vals = 0;
virtual void Run() {
EventStream* stream = main_.first;
EventStream *stream = main_.first;
vals = 0;
stream->OnEvent<MessageInt>([this](const Message&, const Subscription&) {
++vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<MessageInt>(
[this](const Message &, const Subscription &) {
++vals;
main_.second->Send<EndMessage>();
});
stream->OnEvent<EndMessage>([this](const Message&, const Subscription&) {
ASSERT_LE(vals, num_tests);
if (vals == num_tests) {
CloseChannel("main");
}
});
stream->OnEvent<EndMessage>(
[this](const Message &, const Subscription &) {
ASSERT_LE(vals, num_tests);
if (vals == num_tests) {
CloseChannel("main");
}
});
}
};
@ -460,10 +477,6 @@ TEST(MultipleSendTest, ProcessManyMessages) {
system.AwaitShutdown();
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();

View File

@ -192,31 +192,33 @@ DecodedValue::operator query::TypedValue() const {
std::ostream &operator<<(std::ostream &os, const DecodedVertex &vertex) {
os << "V(";
PrintIterable(os, vertex.labels, ":",
[&](auto &stream, auto label) { stream << label; });
utils::PrintIterable(os, vertex.labels, ":",
[&](auto &stream, auto label) { stream << label; });
os << " {";
PrintIterable(os, vertex.properties, ", ",
[&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
utils::PrintIterable(os, vertex.properties, ", ",
[&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "})";
}
std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge) {
os << "E[" << edge.type;
os << " {";
PrintIterable(os, edge.properties, ", ", [&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
utils::PrintIterable(os, edge.properties, ", ",
[&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}]";
}
std::ostream &operator<<(std::ostream &os, const DecodedUnboundedEdge &edge) {
os << "E[" << edge.type;
os << " {";
PrintIterable(os, edge.properties, ", ", [&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
utils::PrintIterable(os, edge.properties, ", ",
[&](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}]";
}
@ -256,14 +258,14 @@ std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
return os << value.ValueString();
case DecodedValue::Type::List:
os << "[";
PrintIterable(os, value.ValueList());
utils::PrintIterable(os, value.ValueList());
return os << "]";
case DecodedValue::Type::Map:
os << "{";
PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
utils::PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}";
case DecodedValue::Type::Vertex:
return os << value.ValueVertex();

View File

@ -116,10 +116,10 @@ void PrintResults(ResultStreamFaker results) {
// output the summary
std::cout << "Query summary: {";
PrintIterable(std::cout, results.GetSummary(), ", ",
[&](auto &stream, const auto &kv) {
stream << kv.first << ": " << kv.second;
});
utils::PrintIterable(std::cout, results.GetSummary(), ", ",
[&](auto &stream, const auto &kv) {
stream << kv.first << ": " << kv.second;
});
std::cout << "}" << std::endl;
}

View File

@ -170,8 +170,8 @@ class Interpreter {
// When the symbol is aliased or expanded from '*' (inside RETURN or
// WITH), then there is no token position, so use symbol name.
// Otherwise, find the name from stripped query.
header.push_back(FindOr(stripped.named_expressions(),
symbol.token_position(), symbol.name())
header.push_back(utils::FindOr(stripped.named_expressions(),
symbol.token_position(), symbol.name())
.first);
}
stream.Header(header);

View File

@ -302,10 +302,10 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyRange::MakeCursor(
context.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);
};
@ -380,16 +380,15 @@ std::unique_ptr<Cursor> ScanAllByLabelPropertyValue::MakeCursor(
return std::make_unique<ScanAllByLabelPropertyValueCursor>(*this, db);
}
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)
ExpandCommon::ExpandCommon(
Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Direction direction,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
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),
edge_types_(edge_types),
input_(input ? input : std::make_shared<Once>()),
input_symbol_(input_symbol),
existing_node_(existing_node),
@ -539,10 +538,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, Context &context) {
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());
in_edges_.emplace(vertex.in_with_types(&self_.edge_types()));
}
in_edges_it_.emplace(in_edges_->begin());
}
@ -558,10 +555,8 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, Context &context) {
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());
out_edges_.emplace(vertex.out_with_types(&self_.edge_types()));
}
out_edges_it_.emplace(out_edges_->begin());
}
@ -570,16 +565,14 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, Context &context) {
}
}
ExpandVariable::ExpandVariable(Symbol node_symbol, Symbol edge_symbol,
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, edge_type, input,
ExpandVariable::ExpandVariable(
Symbol node_symbol, Symbol edge_symbol, EdgeAtom::Direction direction,
const std::vector<GraphDbTypes::EdgeType> &edge_types, 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, edge_types, input,
input_symbol, existing_node, existing_edge, graph_view),
lower_bound_(lower_bound),
upper_bound_(upper_bound),
@ -601,7 +594,7 @@ namespace {
*/
auto ExpandFromVertex(const VertexAccessor &vertex,
EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type) {
const std::vector<GraphDbTypes::EdgeType> &edge_types) {
// wraps an EdgeAccessor into a pair <accessor, direction>
auto wrapper = [](EdgeAtom::Direction direction, auto &&vertices) {
return iter::imap(
@ -615,27 +608,17 @@ auto ExpandFromVertex(const VertexAccessor &vertex,
std::vector<decltype(wrapper(direction, vertex.in()))> chain_elements;
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 {
auto edges = vertex.in_with_types(&edge_types);
if (edges.begin() != edges.end()) {
chain_elements.emplace_back(
wrapper(EdgeAtom::Direction::IN, vertex.in()));
wrapper(EdgeAtom::Direction::IN, std::move(edges)));
}
}
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 {
auto edges = vertex.out_with_types(&edge_types);
if (edges.begin() != edges.end()) {
chain_elements.emplace_back(
wrapper(EdgeAtom::Direction::OUT, vertex.out()));
wrapper(EdgeAtom::Direction::OUT, std::move(edges)));
}
}
@ -718,7 +701,7 @@ class ExpandVariableCursor : public Cursor {
// the expansion currently being Pulled
std::vector<decltype(ExpandFromVertex(std::declval<VertexAccessor>(),
EdgeAtom::Direction::IN,
self_.edge_type_))>
self_.edge_types_))>
edges_;
// an iterator indicating the possition in the corresponding edges_ element
@ -763,7 +746,7 @@ class ExpandVariableCursor : public Cursor {
if (upper_bound_ > 0) {
SwitchAccessor(vertex, self_.graph_view_);
edges_.emplace_back(
ExpandFromVertex(vertex, self_.direction_, self_.edge_type_));
ExpandFromVertex(vertex, self_.direction_, self_.edge_types_));
edges_it_.emplace_back(edges_.back().begin());
}
@ -933,7 +916,7 @@ class ExpandVariableCursor : public Cursor {
if (upper_bound_ > static_cast<int64_t>(edges_.size())) {
SwitchAccessor(current_vertex, self_.graph_view_);
edges_.emplace_back(ExpandFromVertex(current_vertex, self_.direction_,
self_.edge_type_));
self_.edge_types_));
edges_it_.emplace_back(edges_.back().begin());
}
@ -955,14 +938,14 @@ std::unique_ptr<Cursor> ExpandVariable::MakeCursor(GraphDbAccessor &db) const {
ExpandBreadthFirst::ExpandBreadthFirst(
Symbol node_symbol, Symbol edge_list_symbol, EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type, Expression *max_depth,
Symbol inner_node_symbol, Symbol inner_edge_symbol, Expression *where,
const std::shared_ptr<LogicalOperator> &input, Symbol input_symbol,
bool existing_node, GraphView graph_view)
const std::vector<GraphDbTypes::EdgeType> &edge_types,
Expression *max_depth, Symbol inner_node_symbol, Symbol inner_edge_symbol,
Expression *where, const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, GraphView graph_view)
: node_symbol_(node_symbol),
edge_list_symbol_(edge_list_symbol),
direction_(direction),
edge_type_(edge_type),
edge_types_(edge_types),
max_depth_(max_depth),
inner_node_symbol_(inner_node_symbol),
inner_edge_symbol_(inner_edge_symbol),
@ -1023,22 +1006,12 @@ bool ExpandBreadthFirst::Cursor::Pull(Frame &frame, Context &context) {
// the "where" condition.
auto expand_from_vertex = [this, &expand_pair](VertexAccessor &vertex) {
if (self_.direction_ != EdgeAtom::Direction::IN) {
if (self_.edge_type_) {
for (const EdgeAccessor &edge : vertex.out_with_type(self_.edge_type_))
expand_pair(edge, edge.to());
} else {
for (const EdgeAccessor &edge : vertex.out())
expand_pair(edge, edge.to());
}
for (const EdgeAccessor &edge : vertex.out_with_types(&self_.edge_types_))
expand_pair(edge, edge.to());
}
if (self_.direction_ != EdgeAtom::Direction::OUT) {
if (self_.edge_type_) {
for (const EdgeAccessor &edge : vertex.in_with_type(self_.edge_type_))
expand_pair(edge, edge.from());
} else {
for (const EdgeAccessor &edge : vertex.in())
expand_pair(edge, edge.from());
}
for (const EdgeAccessor &edge : vertex.in_with_types(&self_.edge_types_))
expand_pair(edge, edge.from());
}
};

View File

@ -476,8 +476,9 @@ 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 edge_types GraphDbTypes::EdgeType specifying which edges we
* want to expand. If empty, all edges are valid. If not empty, only edges
* with one of the given types are valid.
* @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.
@ -487,7 +488,7 @@ class ExpandCommon {
*/
ExpandCommon(Symbol node_symbol, Symbol edge_symbol,
EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
const std::shared_ptr<LogicalOperator> &input,
Symbol input_symbol, bool existing_node, bool existing_edge,
GraphView graph_view = GraphView::AS_IS);
@ -496,14 +497,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_; }
const auto &edge_types() const { return edge_types_; }
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;
const std::vector<GraphDbTypes::EdgeType> edge_types_;
// the input op and the symbol under which the op's result
// can be found in the frame
@ -620,8 +621,9 @@ class ExpandVariable : public LogicalOperator, public ExpandCommon {
*/
ExpandVariable(Symbol node_symbol, Symbol edge_symbol,
EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type, bool is_reverse,
Expression *lower_bound, Expression *upper_bound,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
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 = GraphView::AS_IS,
@ -669,7 +671,7 @@ class ExpandBreadthFirst : public LogicalOperator {
public:
ExpandBreadthFirst(Symbol node_symbol, Symbol edge_list_symbol,
EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
Expression *max_depth, Symbol inner_node_symbol,
Symbol inner_edge_symbol, Expression *where,
const std::shared_ptr<LogicalOperator> &input,
@ -711,7 +713,7 @@ class ExpandBreadthFirst : public LogicalOperator {
const Symbol edge_list_symbol_;
const EdgeAtom::Direction direction_;
const GraphDbTypes::EdgeType edge_type_ = nullptr;
const std::vector<GraphDbTypes::EdgeType> edge_types_;
Expression *max_depth_;
// symbols for a single node and edge that are currently getting expanded

View File

@ -428,17 +428,18 @@ class RuleBasedPlanner {
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::unordered_set<GraphDbTypes::Label>())
utils::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::unordered_map<GraphDbTypes::Property,
std::vector<Filters::PropertyFilter>>())
utils::FindOr(
match_ctx.matching.filters.property_filters(), node_symbol,
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).
@ -532,16 +533,12 @@ 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>())
const auto edge_types_set =
utils::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);
}
const std::vector<GraphDbTypes::EdgeType> edge_types(
edge_types_set.begin(), edge_types_set.end());
if (auto *bf_atom = dynamic_cast<BreadthFirstAtom *>(expansion.edge)) {
const auto &traversed_edge_symbol =
symbol_table.at(*bf_atom->traversed_edge_identifier_);
@ -549,22 +546,22 @@ class RuleBasedPlanner {
symbol_table.at(*bf_atom->next_node_identifier_);
// Inline BFS edge filtering together with its filter expression.
auto *filter_expr = impl::BoolJoin<AndOperator>(
storage,
impl::ExtractMultiExpandFilter(bound_symbols, node_symbol,
all_filters, storage),
storage, impl::ExtractMultiExpandFilter(
bound_symbols, node_symbol, all_filters, storage),
bf_atom->filter_expression_);
last_op = new ExpandBreadthFirst(
node_symbol, edge_symbol, expansion.direction, edge_type,
bf_atom->max_depth_, next_node_symbol, traversed_edge_symbol,
filter_expr, std::shared_ptr<LogicalOperator>(last_op),
node1_symbol, existing_node, match_context.graph_view);
node_symbol, edge_symbol, expansion.direction,
std::move(edge_types), bf_atom->max_depth_, next_node_symbol,
traversed_edge_symbol, filter_expr,
std::shared_ptr<LogicalOperator>(last_op), node1_symbol,
existing_node, match_context.graph_view);
} else if (expansion.edge->has_range_) {
auto *filter_expr = impl::ExtractMultiExpandFilter(
bound_symbols, node_symbol, all_filters, storage);
last_op = new ExpandVariable(
node_symbol, edge_symbol, expansion.direction, edge_type,
expansion.is_flipped, expansion.edge->lower_bound_,
expansion.edge->upper_bound_,
node_symbol, edge_symbol, expansion.direction,
std::move(edge_types), expansion.is_flipped,
expansion.edge->lower_bound_, expansion.edge->upper_bound_,
std::shared_ptr<LogicalOperator>(last_op), node1_symbol,
existing_node, existing_edge, match_context.graph_view,
filter_expr);
@ -585,10 +582,11 @@ class RuleBasedPlanner {
existing_node = true;
}
}
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);
last_op = new Expand(node_symbol, edge_symbol, expansion.direction,
std::move(edge_types),
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

View File

@ -203,14 +203,14 @@ std::ostream &operator<<(std::ostream &os, const TypedValue &value) {
return os << value.Value<std::string>();
case TypedValue::Type::List:
os << "[";
PrintIterable(os, value.Value<std::vector<TypedValue>>());
utils::PrintIterable(os, value.Value<std::vector<TypedValue>>());
return os << "]";
case TypedValue::Type::Map:
os << "{";
PrintIterable(os, value.Value<std::map<std::string, TypedValue>>(), ", ",
[](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
utils::PrintIterable(os, value.Value<std::map<std::string, TypedValue>>(),
", ", [](auto &stream, const auto &pair) {
stream << pair.first << ": " << pair.second;
});
return os << "}";
case TypedValue::Type::Vertex:
return os << value.Value<VertexAccessor>();

View File

@ -31,7 +31,8 @@ bool EdgeAccessor::is_cycle() const {
std::ostream &operator<<(std::ostream &os, const EdgeAccessor &ea) {
os << "E[" << ea.db_accessor().EdgeTypeName(ea.EdgeType());
os << " {";
PrintIterable(os, ea.Properties(), ", ", [&](auto &stream, const auto &pair) {
utils::PrintIterable(os, ea.Properties(), ", ", [&](auto &stream,
const auto &pair) {
stream << ea.db_accessor().PropertyName(pair.first) << ": " << pair.second;
});
return os << "}]";

View File

@ -5,6 +5,7 @@
#include "database/graph_db_datatypes.hpp"
#include "mvcc/version_list.hpp"
#include "utils/algorithm.hpp"
// forward declare Vertex and Edge because they need this data structure
class Edge;
@ -26,7 +27,7 @@ class Edges {
};
/** Custom iterator that takes care of skipping edges when the destination
* vertex or edge type are known. */
* vertex or edge types are known. */
class Iterator {
public:
/** Ctor that just sets the position. Used for normal iteration (that does
@ -38,7 +39,7 @@ class Edges {
explicit Iterator(std::vector<Element>::const_iterator iterator)
: position_(iterator) {}
/** Ctor used for creating the beginning iterator with known destionation
/** Ctor used for creating the beginning iterator with known destination
* vertex.
*
* @param iterator - Iterator in the underlying storage.
@ -51,16 +52,17 @@ class Edges {
update_position();
}
/** Ctor used for creating the beginning iterator with known edge type.
/** Ctor used for creating the beginning iterator with known edge types.
*
* @param iterator - Iterator in the underlying storage.
* @param end - End iterator in the underlying storage.
* @param edge_type - The edge type that must be matched.
* @param edge_types - The edge types at least one of which must be matched.
* If nullptr all edges are valid.
*/
Iterator(std::vector<Element>::const_iterator position,
std::vector<Element>::const_iterator end,
GraphDbTypes::EdgeType edge_type)
: position_(position), end_(end), edge_type_(edge_type) {
const std::vector<GraphDbTypes::EdgeType> *edge_types)
: position_(position), end_(end), edge_types_(edge_types) {
update_position();
}
@ -86,7 +88,8 @@ class Edges {
// Optional predicates. If set they define which edges are skipped by the
// iterator. Only one can be not-null in the current implementation.
vertex_ptr_t vertex_{nullptr};
GraphDbTypes::EdgeType edge_type_{nullptr};
// For edge types we use a vector pointer because it's optional.
const std::vector<GraphDbTypes::EdgeType> *edge_types_ = nullptr;
/** Helper function that skips edges that don't satisfy the predicate
* present in this iterator. */
@ -95,11 +98,10 @@ class Edges {
position_ = std::find_if(
position_, end_,
[v = this->vertex_](const Element &e) { return e.vertex == v; });
else if (edge_type_)
position_ = std::find_if(position_, end_,
[et = this->edge_type_](const Element &e) {
return e.edge_type == et;
});
else if (edge_types_)
position_ = std::find_if(position_, end_, [this](const Element &e) {
return utils::Contains(*edge_types_, e.edge_type);
});
}
};
@ -144,11 +146,15 @@ class Edges {
/*
* Creates a beginning iterator that will skip edges whose edge type is not
* equal to the given. Relies on the fact that edge types are immutable during
* the whole edge lifetime.
* among the given. If none are given, or the pointer is null, then all edges
* are valid. Relies on the fact that edge types are immutable during the
* whole edge lifetime.
*/
auto begin(GraphDbTypes::EdgeType edge_type) const {
return Iterator(storage_.begin(), storage_.end(), edge_type);
auto begin(const std::vector<GraphDbTypes::EdgeType> *edge_types) const {
if (edge_types && !edge_types->empty())
return Iterator(storage_.begin(), storage_.end(), edge_types);
else
return Iterator(storage_.begin());
}
private:

View File

@ -43,11 +43,12 @@ const std::vector<GraphDbTypes::Label> &VertexAccessor::labels() const {
std::ostream &operator<<(std::ostream &os, const VertexAccessor &va) {
os << "V(";
PrintIterable(os, va.labels(), ":", [&](auto &stream, auto label) {
utils::PrintIterable(os, va.labels(), ":", [&](auto &stream, auto label) {
stream << va.db_accessor().LabelName(label);
});
os << " {";
PrintIterable(os, va.Properties(), ", ", [&](auto &stream, const auto &pair) {
utils::PrintIterable(os, va.Properties(), ", ", [&](auto &stream,
const auto &pair) {
stream << va.db_accessor().PropertyName(pair.first) << ": " << pair.second;
});
return os << "})";

View File

@ -82,12 +82,14 @@ class VertexAccessor : public RecordAccessor<Vertex> {
}
/**
* Returns EdgeAccessors for all incoming edges whose type is equal to the
* given.
* Returns EdgeAccessors for all incoming edges whose type is one of the
* given. If the given collection of types is nullptr or empty, all edge types
* are valid.
*/
auto in_with_type(GraphDbTypes::EdgeType edge_type) const {
auto in_with_types(
const std::vector<GraphDbTypes::EdgeType> *edge_types) const {
return MakeAccessorIterator<EdgeAccessor>(
current().in_.begin(edge_type), current().in_.end(), db_accessor());
current().in_.begin(edge_types), current().in_.end(), db_accessor());
}
/**
@ -108,12 +110,14 @@ class VertexAccessor : public RecordAccessor<Vertex> {
}
/**
* Returns EdgeAccessors for all outgoing edges whose type is equal to the
* given.
* Returns EdgeAccessors for all outgoing edges whose type is one of the
* given. If the given collection of types is nullptr or empty, all edge types
* are valid.
*/
auto out_with_type(GraphDbTypes::EdgeType edge_type) const {
auto out_with_types(
const std::vector<GraphDbTypes::EdgeType> *edge_types) const {
return MakeAccessorIterator<EdgeAccessor>(
current().out_.begin(edge_type), current().out_.end(), db_accessor());
current().out_.begin(edge_types), current().out_.end(), db_accessor());
}
};

View File

@ -81,7 +81,7 @@ class Snapshot {
friend std::ostream &operator<<(std::ostream &stream,
const Snapshot &snapshot) {
stream << "Snapshot(";
PrintIterable(stream, snapshot.transaction_ids_);
utils::PrintIterable(stream, snapshot.transaction_ids_);
stream << ")";
return stream;
}

View File

@ -6,6 +6,8 @@
#include "utils/exceptions.hpp"
namespace utils {
/**
* Outputs a collection of items to the given stream, separating them with the
* given delimiter.
@ -17,8 +19,9 @@
* streams the item to the stream.
*/
template <typename TStream, typename TIterable, typename TStreamer>
void PrintIterable(TStream &stream, const TIterable &iterable,
const std::string &delim = ", ", TStreamer streamer = {}) {
inline void PrintIterable(TStream &stream, const TIterable &iterable,
const std::string &delim = ", ",
TStreamer streamer = {}) {
bool first = true;
for (const auto &item : iterable) {
if (first)
@ -38,8 +41,8 @@ void PrintIterable(TStream &stream, const TIterable &iterable,
* @param delim Delimiter that is put between items.
*/
template <typename TStream, typename TIterable>
void PrintIterable(TStream &stream, const TIterable &iterable,
const std::string &delim = ", ") {
inline void PrintIterable(TStream &stream, const TIterable &iterable,
const std::string &delim = ", ") {
PrintIterable(stream, iterable, delim,
[](auto &stream, const auto &item) { stream << item; });
}
@ -54,8 +57,8 @@ void PrintIterable(TStream &stream, const TIterable &iterable,
* map.
*/
template <class TMap, class TKey, class TVal>
std::pair<TVal, bool> FindOr(const TMap &map, const TKey &key,
TVal &&or_value) {
inline 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};
return {std::forward<TVal>(or_value), false};
@ -69,7 +72,7 @@ std::pair<TVal, bool> FindOr(const TMap &map, const TKey &key,
* @exception BasicException is thrown if the `iterable` is empty.
*/
template <class TIterable>
auto First(TIterable &&iterable) {
inline auto First(TIterable &&iterable) {
if (iterable.begin() != iterable.end()) return *iterable.begin();
throw utils::BasicException("Empty iterable");
}
@ -82,7 +85,22 @@ auto First(TIterable &&iterable) {
* @return The first element of the `iterable` or the `empty_value`.
*/
template <class TVal, class TIterable>
TVal First(TIterable &&iterable, TVal &&empty_value) {
inline TVal First(TIterable &&iterable, TVal &&empty_value) {
if (iterable.begin() != iterable.end()) return *iterable.begin();
return empty_value;
}
/**
* Returns `true` if the given iterable contains the given element.
*
* @param iterable An iterable collection of values.
* @param element The sought element.
* @return `true` if element is contained in iterable.
* @tparam TIiterable type of iterable.
* @tparam TElement type of element.
*/
template <typename TIterable, typename TElement>
inline bool Contains(const TIterable &iterable, const TElement &element) {
return std::find(iterable.begin(), iterable.end(), element) != iterable.end();
}
} // namespace utils

View File

@ -31,20 +31,20 @@ void PrintJsonDecodedValue(std::ostream &os,
break;
case DecodedValue::Type::List:
os << "[";
PrintIterable(os, value.ValueList(), ", ",
[](auto &stream, const auto &item) {
PrintJsonDecodedValue(stream, item);
});
utils::PrintIterable(os, value.ValueList(), ", ",
[](auto &stream, const auto &item) {
PrintJsonDecodedValue(stream, item);
});
os << "]";
break;
case DecodedValue::Type::Map:
os << "{";
PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
PrintJsonDecodedValue(stream, {pair.first});
stream << ": ";
PrintJsonDecodedValue(stream, pair.second);
});
utils::PrintIterable(os, value.ValueMap(), ", ",
[](auto &stream, const auto &pair) {
PrintJsonDecodedValue(stream, {pair.first});
stream << ": ";
PrintJsonDecodedValue(stream, pair.second);
});
os << "}";
break;
default:

View File

@ -102,16 +102,16 @@ class Session {
auto CreateVertex(const DecodedVertex &vertex) {
std::stringstream os;
os << "CREATE (n :";
PrintIterable(os, vertex.labels, ":");
utils::PrintIterable(os, vertex.labels, ":");
os << " {";
PrintIterable(os, vertex.properties, ", ",
[&](auto &stream, const auto &pair) {
if (pair.second.type() == DecodedValue::Type::String) {
stream << pair.first << ": \"" << pair.second << "\"";
} else {
stream << pair.first << ": " << pair.second;
}
});
utils::PrintIterable(
os, vertex.properties, ", ", [&](auto &stream, const auto &pair) {
if (pair.second.type() == DecodedValue::Type::String) {
stream << pair.first << ": \"" << pair.second << "\"";
} else {
stream << pair.first << ": " << pair.second;
}
});
os << "})";
return Execute(os.str(), {}, "CREATE (n :labels... {...})");
}
@ -129,14 +129,14 @@ class Session {
os << "-";
}
os << "[:" << edge.type << " {";
PrintIterable(os, edge.properties, ", ",
[&](auto &stream, const auto &pair) {
if (pair.second.type() == DecodedValue::Type::String) {
stream << pair.first << ": \"" << pair.second << "\"";
} else {
stream << pair.first << ": " << pair.second;
}
});
utils::PrintIterable(
os, edge.properties, ", ", [&](auto &stream, const auto &pair) {
if (pair.second.type() == DecodedValue::Type::String) {
stream << pair.first << ": \"" << pair.second << "\"";
} else {
stream << pair.first << ": " << pair.second;
}
});
os << "}]";
if (edge.from == from.id) {
os << "->";
@ -418,7 +418,8 @@ int main(int argc, char **argv) {
out << "{\"num_executed_queries\": " << executed_queries << ", "
<< "\"elapsed_time\": " << timer.Elapsed().count()
<< ", \"queries\": [";
PrintIterable(out, aggregated_stats, ", ", [](auto &stream, const auto &x) {
utils::PrintIterable(out, aggregated_stats, ", ", [](auto &stream,
const auto &x) {
stream << "{\"query\": " << nlohmann::json(x.first) << ", \"stats\": ";
PrintJsonDecodedValue(stream, DecodedValue(x.second));
stream << "}";

View File

@ -34,7 +34,7 @@ void PrintJsonMetadata(
std::ostream &os,
const std::vector<std::map<std::string, DecodedValue>> &metadata) {
os << "[";
PrintIterable(os, metadata, ", ", [](auto &stream, const auto &item) {
utils::PrintIterable(os, metadata, ", ", [](auto &stream, const auto &item) {
PrintJsonDecodedValue(stream, item);
});
os << "]";

View File

@ -420,8 +420,9 @@ class PlanPrinter : public query::plan::HierarchicalLogicalOperatorVisitor {
bool PreVisit(query::plan::Produce &op) override {
WithPrintLn([&](auto &out) {
out << "* Produce {";
PrintIterable(out, op.named_expressions(), ", ",
[](auto &out, const auto &nexpr) { out << nexpr->name_; });
utils::PrintIterable(
out, op.named_expressions(), ", ",
[](auto &out, const auto &nexpr) { out << nexpr->name_; });
out << "}";
});
return true;

View File

@ -163,22 +163,24 @@ TEST_F(QueryCostEstimator, ScanAllByLabelPropertyRangeConstExpr) {
}
TEST_F(QueryCostEstimator, Expand) {
MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr,
last_op_, NextSymbol(), false, false);
MakeOp<Expand>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN,
std::vector<GraphDbTypes::EdgeType>{}, last_op_, NextSymbol(),
false, false);
EXPECT_COST(CardParam::kExpand * CostParam::kExpand);
}
TEST_F(QueryCostEstimator, ExpandVariable) {
MakeOp<ExpandVariable>(NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN,
nullptr, false, nullptr, nullptr, last_op_,
NextSymbol(), false, false);
std::vector<GraphDbTypes::EdgeType>{}, false, nullptr,
nullptr, last_op_, NextSymbol(), false, false);
EXPECT_COST(CardParam::kExpandVariable * CostParam::kExpandVariable);
}
TEST_F(QueryCostEstimator, ExpandBreadthFirst) {
MakeOp<ExpandBreadthFirst>(
NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN, nullptr, Literal(3),
NextSymbol(), NextSymbol(), Literal(true), last_op_, NextSymbol(), false);
NextSymbol(), NextSymbol(), EdgeAtom::Direction::IN,
std::vector<GraphDbTypes::EdgeType>{}, Literal(3), NextSymbol(),
NextSymbol(), Literal(true), last_op_, NextSymbol(), false);
EXPECT_COST(CardParam::kExpandBreadthFirst * CostParam::kExpandBreadthFirst);
}
@ -204,12 +206,11 @@ TEST_F(QueryCostEstimator, ExpandUniquenessFilter) {
}
TEST_F(QueryCostEstimator, UnwindLiteral) {
TEST_OP(
MakeOp<query::plan::Unwind>(
last_op_,
storage_.Create<ListLiteral>(std::vector<Expression *>(7, nullptr)),
NextSymbol()),
CostParam::kUnwind, 7);
TEST_OP(MakeOp<query::plan::Unwind>(
last_op_, storage_.Create<ListLiteral>(
std::vector<Expression *>(7, nullptr)),
NextSymbol()),
CostParam::kUnwind, 7);
}
TEST_F(QueryCostEstimator, UnwindNoLiteral) {

View File

@ -48,9 +48,8 @@ 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, nullptr, false, "m", false);
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::BOTH, {}, false, "m", false);
auto one = LITERAL(1);
auto n_p = PROPERTY_LOOKUP("n", prop);

View File

@ -178,7 +178,7 @@ ExpandTuple MakeExpand(AstTreeStorage &storage, SymbolTable &symbol_table,
std::shared_ptr<LogicalOperator> input,
Symbol input_symbol, const std::string &edge_identifier,
EdgeAtom::Direction direction,
const GraphDbTypes::EdgeType &edge_type,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
bool existing_edge, const std::string &node_identifier,
bool existing_node,
GraphView graph_view = GraphView::AS_IS) {
@ -190,7 +190,7 @@ 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, edge_type,
auto op = std::make_shared<Expand>(node_sym, edge_sym, direction, edge_types,
input, input_symbol, existing_node,
existing_edge, graph_view);

View File

@ -302,7 +302,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, 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>(
@ -354,9 +354,8 @@ 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, nullptr, false, "m", false);
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::BOTH, {}, false, "m", false);
// getter expressions for deletion
auto n_get = storage.Create<Identifier>("n");
@ -479,7 +478,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, false, "m", false);
// set prop1 to 42 on n and r
auto prop1 = dba->Property("prop1");
@ -530,7 +529,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, false, "m", false);
auto op = update ? plan::SetProperties::Op::UPDATE
: plan::SetProperties::Op::REPLACE;
@ -633,7 +632,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, false, "m", false);
auto n_p = PROPERTY_LOOKUP("n", prop1);
symbol_table[*n_p->expression_] = n.sym_;
@ -711,7 +710,7 @@ TEST(QueryPlan, NodeFilterSet) {
scan_all.node_->properties_[prop] = LITERAL(42);
auto expand =
MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
EdgeAtom::Direction::BOTH, {}, false, "m", false);
auto *filter_expr =
EQ(storage.Create<PropertyLookup>(scan_all.node_->identifier_, prop),
LITERAL(42));
@ -751,7 +750,7 @@ TEST(QueryPlan, FilterRemove) {
scan_all.node_->properties_[prop] = LITERAL(42);
auto expand =
MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, false, "m", false);
EdgeAtom::Direction::BOTH, {}, false, "m", false);
auto filter_prop = PROPERTY_LOOKUP("n", prop);
symbol_table[*filter_prop->expression_] = scan_all.sym_;
auto filter =
@ -813,9 +812,8 @@ 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, nullptr, false, "m", false);
auto r_m = MakeExpand(storage, symbol_table, std::make_shared<Once>(), n.sym_,
"r", EdgeAtom::Direction::BOTH, {}, 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));

View File

@ -278,7 +278,7 @@ TEST_F(ExpandFixture, 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,
nullptr, false, "m", false, graph_view);
{}, false, "m", false, graph_view);
// make a named expression and a produce
auto output = NEXPR("m", IDENT("m"));
@ -315,7 +315,7 @@ TEST_F(ExpandFixture, Expand) {
TEST_F(ExpandFixture, ExpandPath) {
auto n = MakeScanAll(storage, symbol_table, "n");
auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::OUT, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, false, "m", false);
Symbol path_sym = symbol_table.CreateSymbol("path", true);
auto path = std::make_shared<ConstructNamedPath>(
r_m.op_, path_sym,
@ -409,7 +409,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,
const std::vector<GraphDbTypes::EdgeType> &edge_types,
std::experimental::optional<size_t> lower,
std::experimental::optional<size_t> upper, Symbol edge_sym,
bool existing_edge, const std::string &node_to,
@ -430,11 +430,11 @@ 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, edge_type, is_reverse, convert(lower),
n_to_sym, edge_sym, direction, edge_types, 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, edge_type,
return std::make_shared<Expand>(n_to_sym, edge_sym, direction, edge_types,
filter_op, n_from.sym_, false,
existing_edge, graph_view);
}
@ -494,7 +494,7 @@ TEST_F(QueryPlanExpandVariable, OneVariableExpansion) {
bool reverse) {
auto e = Edge("r", direction);
return GetEdgeListSizes(
AddMatch<ExpandVariable>(nullptr, "n", layer, direction, nullptr, lower,
AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower,
upper, e, false, "m", GraphView::AS_IS,
reverse),
e);
@ -558,20 +558,20 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
if (single_expansion_before) {
symbols.push_back(Edge("r0", direction));
last_op = AddMatch<Expand>(last_op, "n0", layer, direction, nullptr,
lower, upper, symbols.back(), false, "m0");
last_op = AddMatch<Expand>(last_op, "n0", layer, direction, {}, 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, nullptr,
lower, upper, var_length_sym, false, "m1");
AddMatch<ExpandVariable>(last_op, "n1", layer, direction, {}, 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, nullptr,
lower, upper, symbols.back(), false, "m2");
last_op = AddMatch<Expand>(last_op, "n2", layer, direction, {}, lower,
upper, symbols.back(), false, "m2");
}
if (add_uniqueness_check) {
@ -600,12 +600,11 @@ 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, nullptr,
lower, upper, e1, false, "m1");
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {},
lower, upper, e1, false, "m1");
auto e2 = Edge("r2", direction);
auto last_op = AddMatch<ExpandVariable>(
first, "n2", layer, direction, nullptr, lower, upper, e2, false, "m2");
auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction, {},
lower, upper, e2, false, "m2");
if (add_uniqueness_check) {
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(
last_op, e2, std::vector<Symbol>{e1});
@ -626,12 +625,11 @@ 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, nullptr,
lower, upper, e1, false, "m1");
auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {},
lower, upper, e1, false, "m1");
auto e2 = same_edge_symbol ? e1 : Edge("r2", direction);
auto second =
AddMatch<ExpandVariable>(first, "n2", layer, direction, nullptr, lower,
AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower,
upper, e2, same_edge_symbol, "m2");
return GetEdgeListSizes(second, e2);
};
@ -658,11 +656,13 @@ TEST_F(QueryPlanExpandVariable, ExistingEdges) {
}
TEST_F(QueryPlanExpandVariable, GraphState) {
auto test_expand = [&](GraphView graph_view, const auto &edge_type) {
auto test_expand = [&](
GraphView graph_view,
const std::vector<GraphDbTypes::EdgeType> &edge_types) {
auto e = Edge("r", EdgeAtom::Direction::OUT);
return GetEdgeListSizes(
AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT,
edge_type, 2, 2, e, false, "m", graph_view),
edge_types, 2, 2, e, false, "m", graph_view),
e);
};
@ -676,23 +676,23 @@ TEST_F(QueryPlanExpandVariable, GraphState) {
ASSERT_EQ(CountIterable(dba->Vertices(false)), 6);
ASSERT_EQ(CountIterable(dba->Vertices(true)), 8);
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, edge_type), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(GraphView::NEW, new_edge_type), (map_int{}));
EXPECT_EQ(test_expand(GraphView::OLD, {}), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(GraphView::OLD, {new_edge_type}), (map_int{}));
EXPECT_EQ(test_expand(GraphView::NEW, {}), (map_int{{2, 12}}));
EXPECT_EQ(test_expand(GraphView::NEW, {edge_type}), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(GraphView::NEW, {new_edge_type}), (map_int{}));
dba->AdvanceCommand();
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, edge_type), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(graph_view, new_edge_type), (map_int{}));
EXPECT_EQ(test_expand(graph_view, {}), (map_int{{2, 12}}));
EXPECT_EQ(test_expand(graph_view, {edge_type}), (map_int{{2, 8}}));
EXPECT_EQ(test_expand(graph_view, {new_edge_type}), (map_int{}));
}
}
TEST_F(QueryPlanExpandVariable, NamedPath) {
auto e = Edge("r", EdgeAtom::Direction::OUT);
auto expand = AddMatch<ExpandVariable>(
nullptr, "n", 0, EdgeAtom::Direction::OUT, nullptr, 2, 2, e, false, "m");
nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 2, 2, e, false, "m");
auto find_symbol = [this](const std::string &name) {
for (const auto &pos_sym : symbol_table.table())
if (pos_sym.second.name() == name) return pos_sym.second;
@ -803,9 +803,10 @@ class QueryPlanExpandBreadthFirst : public testing::Test {
: symbol_table.CreateSymbol("node", true);
auto edge_list_sym = symbol_table.CreateSymbol("edgelist_", true);
last_op = std::make_shared<ExpandBreadthFirst>(
node_sym, edge_list_sym, direction, nullptr, LITERAL(max_depth),
inner_node, inner_edge, where, last_op, n.sym_,
existing_node_input != nullptr, graph_view);
node_sym, edge_list_sym, direction,
std::vector<GraphDbTypes::EdgeType>{}, LITERAL(max_depth), inner_node,
inner_edge, where, last_op, n.sym_, existing_node_input != nullptr,
graph_view);
Frame frame(symbol_table.max_position());
auto cursor = last_op->MakeCursor(*dba);
@ -974,7 +975,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, 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_});
@ -1049,7 +1050,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, false, "m", false);
// RETURN m
auto m_ne = NEXPR("m", IDENT("m"));
symbol_table[*m_ne->expression_] = r_m.node_sym_;
@ -1096,9 +1097,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,
nullptr, m.op_, m.sym_, true, false);
auto expand = std::make_shared<plan::Expand>(
with_n_sym, edge_sym, edge_direction,
std::vector<GraphDbTypes::EdgeType>{}, m.op_, m.sym_, true, false);
// RETURN m
auto m_ne = NEXPR("m", IDENT("m"));
symbol_table[*m_ne->expression_] = m.sym_;
@ -1129,7 +1130,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, nullptr, false, "m", false);
EdgeAtom::Direction::BOTH, {}, 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_});
@ -1147,9 +1148,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,
nullptr, a.op_, a.sym_, false, true);
auto expand = std::make_shared<plan::Expand>(
node_sym, with_r_sym, edge_direction,
std::vector<GraphDbTypes::EdgeType>{}, a.op_, a.sym_, false, true);
// RETURN a
auto a_ne = NEXPR("a", IDENT("a"));
symbol_table[*a_ne->expression_] = a.sym_;
@ -1177,13 +1178,14 @@ 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, nullptr, false, "n",
with_existing);
auto r_n =
MakeExpand(storage, symbol_table, n.op_, n.sym_, "r",
EdgeAtom::Direction::OUT, {}, false, "n", with_existing);
if (with_existing)
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);
r_n.op_ =
std::make_shared<Expand>(n.sym_, r_n.edge_sym_, r_n.edge_->direction_,
std::vector<GraphDbTypes::EdgeType>{}, n.op_,
n.sym_, with_existing, false);
// make a named expression and a produce
auto output = NEXPR("n", IDENT("n"));
@ -1221,16 +1223,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, nullptr, false, "j", false);
auto r_k = MakeExpand(storage, symbol_table, r_j.op_, r_j.node_sym_, "r",
EdgeAtom::Direction::BOTH, nullptr, with_existing,
"k", false);
auto r_j = MakeExpand(storage, symbol_table, i.op_, i.sym_, "r",
EdgeAtom::Direction::BOTH, {}, 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);
if (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);
r_k.node_sym_, r_j.edge_sym_, r_k.edge_->direction_,
std::vector<GraphDbTypes::EdgeType>{}, r_j.op_, r_j.node_sym_, false,
with_existing);
// make a named expression and a produce
auto output = NEXPR("r", IDENT("r"));
@ -1263,7 +1265,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, nullptr, false, "_", false);
EdgeAtom::Direction::BOTH, {}, false, "_", false);
EXPECT_EQ(1, PullAll(r_.op_, *dba, symbol_table));
}
@ -1311,7 +1313,7 @@ TEST(QueryPlan, EdgeFilter) {
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);
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 =
@ -1356,7 +1358,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, nullptr, false, "m", false);
EdgeAtom::Direction::OUT, {}, 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);
@ -1423,16 +1425,15 @@ 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, nullptr, false, "n2", false);
auto r1_n2 = MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1",
EdgeAtom::Direction::OUT, {}, 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, nullptr, false, "n3", false);
EdgeAtom::Direction::OUT, {}, false, "n3", false);
last_op = r2_n3.op_;
if (edge_uniqueness)
last_op = std::make_shared<ExpandUniquenessFilter<EdgeAccessor>>(

View File

@ -19,12 +19,12 @@ namespace std {
// Overloads for printing resulting rows from a query.
std::ostream &operator<<(std::ostream &stream,
const std::vector<TypedValue> &row) {
PrintIterable(stream, row);
utils::PrintIterable(stream, row);
return stream;
}
std::ostream &operator<<(std::ostream &stream,
const std::vector<std::vector<TypedValue>> &rows) {
PrintIterable(stream, rows, "\n");
utils::PrintIterable(stream, rows, "\n");
return stream;
}

View File

@ -289,22 +289,28 @@ TEST(RecordAccessor, VertexEdgeConnectionsWithEdgeType) {
auto v2 = dba->InsertVertex();
auto a = dba->EdgeType("a");
auto b = dba->EdgeType("b");
auto c = dba->EdgeType("c");
auto ea = dba->InsertEdge(v1, v2, a);
auto eb_1 = dba->InsertEdge(v2, v1, b);
auto eb_2 = dba->InsertEdge(v2, v1, b);
auto ec = dba->InsertEdge(v1, v2, c);
dba->AdvanceCommand();
TEST_EDGE_ITERABLE(v1.in(), {eb_1, eb_2});
TEST_EDGE_ITERABLE(v2.in(), {ea});
TEST_EDGE_ITERABLE(v2.in(), {ea, ec});
TEST_EDGE_ITERABLE(v1.in_with_type(a));
TEST_EDGE_ITERABLE(v1.in_with_type(b), {eb_1, eb_2});
TEST_EDGE_ITERABLE(v1.out_with_type(a), {ea});
TEST_EDGE_ITERABLE(v1.out_with_type(b));
TEST_EDGE_ITERABLE(v2.in_with_type(a), {ea});
TEST_EDGE_ITERABLE(v2.in_with_type(b));
TEST_EDGE_ITERABLE(v2.out_with_type(a));
TEST_EDGE_ITERABLE(v2.out_with_type(b), {eb_1, eb_2});
std::vector<GraphDbTypes::EdgeType> edges_a{a};
std::vector<GraphDbTypes::EdgeType> edges_b{b};
std::vector<GraphDbTypes::EdgeType> edges_ac{a, c};
TEST_EDGE_ITERABLE(v1.in_with_types(&edges_a));
TEST_EDGE_ITERABLE(v1.in_with_types(&edges_b), {eb_1, eb_2});
TEST_EDGE_ITERABLE(v1.out_with_types(&edges_a), {ea});
TEST_EDGE_ITERABLE(v1.out_with_types(&edges_b));
TEST_EDGE_ITERABLE(v1.out_with_types(&edges_ac), {ea, ec});
TEST_EDGE_ITERABLE(v2.in_with_types(&edges_a), {ea});
TEST_EDGE_ITERABLE(v2.in_with_types(&edges_b));
TEST_EDGE_ITERABLE(v2.out_with_types(&edges_a));
TEST_EDGE_ITERABLE(v2.out_with_types(&edges_b), {eb_1, eb_2});
}
#undef TEST_EDGE_ITERABLE