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:
parent
f91aa7b8fe
commit
a9381df09e
.clang-format
experimental/distributed/tests
src
communication/bolt/v1/decoder
query
storage
transactions
utils
tests
macro_benchmark/clients
manual
unit
@ -5,4 +5,5 @@ Standard: "C++11"
|
||||
UseTab: Never
|
||||
DerivePointerAlignment: false
|
||||
PointerAlignment: Right
|
||||
ColumnLimit : 80
|
||||
...
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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>();
|
||||
|
@ -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 << "}]";
|
||||
|
@ -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:
|
||||
|
@ -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 << "})";
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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 << "}";
|
||||
|
@ -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 << "]";
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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>>(
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user