diff --git a/src/communication/bolt/v1/encoder/base_encoder.hpp b/src/communication/bolt/v1/encoder/base_encoder.hpp index 064cd0f95..27ab7930d 100644 --- a/src/communication/bolt/v1/encoder/base_encoder.hpp +++ b/src/communication/bolt/v1/encoder/base_encoder.hpp @@ -181,6 +181,17 @@ class BaseEncoder { for (auto &i : path.indices) WriteInt(i); } + void WriteGraph(const Graph &graph) { + WriteRAW(utils::UnderlyingCast(Marker::TinyStruct) + 3); + WriteRAW(utils::UnderlyingCast(Signature::Path)); // todo fico, fix + WriteTypeSize(graph.vertices.size(), MarkerList); + for (auto &v : graph.vertices) WriteVertex(v); + WriteTypeSize(graph.edges.size(), MarkerList); + for (auto &e : graph.edges) WriteEdge(e); + WriteTypeSize(graph.indices.size(), MarkerList); + for (auto &i : graph.indices) WriteInt(i); + } + void WriteDate(const utils::Date &date) { WriteRAW(utils::UnderlyingCast(Marker::TinyStruct1)); WriteRAW(utils::UnderlyingCast(Signature::Date)); @@ -259,6 +270,9 @@ class BaseEncoder { case Value::Type::Duration: WriteDuration(value.ValueDuration()); break; + case Value::Type::Graph: + WriteGraph(value.ValueGraph()); + break; } } diff --git a/src/communication/bolt/v1/value.cpp b/src/communication/bolt/v1/value.cpp index a370f78d5..d64cf70fb 100644 --- a/src/communication/bolt/v1/value.cpp +++ b/src/communication/bolt/v1/value.cpp @@ -54,6 +54,7 @@ DEF_GETTER_BY_REF(Date, utils::Date, date_v) DEF_GETTER_BY_REF(LocalTime, utils::LocalTime, local_time_v) DEF_GETTER_BY_REF(LocalDateTime, utils::LocalDateTime, local_date_time_v) DEF_GETTER_BY_REF(Duration, utils::Duration, duration_v) +DEF_GETTER_BY_REF(Graph, Graph, graph_v) #undef DEF_GETTER_BY_REF @@ -103,6 +104,8 @@ Value::Value(const Value &other) : type_(other.type_) { case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); return; + case Type::Graph: + throw ValueException(); } } @@ -157,6 +160,9 @@ Value &Value::operator=(const Value &other) { case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); return *this; + case Type::Graph: + new (&graph_v) Graph(other.graph_v); + return *this; } } return *this; @@ -208,6 +214,9 @@ Value::Value(Value &&other) noexcept : type_(other.type_) { case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); break; + case Type::Graph: + new (&graph_v) Graph(std::move(other.graph_v)); + break; } // reset the type of other @@ -266,6 +275,9 @@ Value &Value::operator=(Value &&other) noexcept { case Type::Duration: new (&duration_v) utils::Duration(other.duration_v); break; + case Type::Graph: + new (&graph_v) Graph(std::move(other.graph_v)); + break; } // reset the type of other @@ -324,6 +336,9 @@ Value::~Value() { case Type::Duration: duration_v.~Duration(); return; + case Type::Graph: + graph_v.~Graph(); + return; } } @@ -424,6 +439,8 @@ std::ostream &operator<<(std::ostream &os, const Value &value) { return os << value.ValueLocalDateTime(); case Value::Type::Duration: return os << value.ValueDuration(); + case Value::Type::Graph: + throw ValueException("Not supported for Graph"); } } @@ -459,6 +476,8 @@ std::ostream &operator<<(std::ostream &os, const Value::Type type) { return os << "local_date_time"; case Value::Type::Duration: return os << "duration"; + case Value::Type::Graph: + throw ValueException("error"); } } } // namespace memgraph::communication::bolt diff --git a/src/communication/bolt/v1/value.hpp b/src/communication/bolt/v1/value.hpp index 442e8060d..ebee13d4d 100644 --- a/src/communication/bolt/v1/value.hpp +++ b/src/communication/bolt/v1/value.hpp @@ -125,6 +125,50 @@ struct Path { std::vector indices; }; +/** + * Structure used when reading a Graph with the decoder. + * The decoder writes data into this structure. + */ +struct Graph { + Graph() {} + + Graph(const std::vector &vertices, const std::vector &edges) { + // Helper function. Looks for the given element in the collection. If found, + // puts its index into `indices`. Otherwise emplaces the given element + // into the collection and puts that index into `indices`. A multiplier is + // added to switch between positive and negative indices (that define edge + // direction). + auto add_element = [this](auto &collection, const auto &element, int multiplier, int offset) { + auto found = + std::find_if(collection.begin(), collection.end(), [&](const auto &e) { return e.id == element.id; }); + indices.emplace_back(multiplier * (std::distance(collection.begin(), found) + offset)); + if (found == collection.end()) collection.push_back(element); + }; + + this->vertices.reserve(vertices.size()); + this->edges.reserve(edges.size()); + this->vertices.emplace_back(vertices[0]); + for (uint i = 0; i < edges.size(); i++) { + const auto &e = edges[i]; + const auto &v = vertices[i + 1]; + UnboundedEdge unbounded_edge{e.id, e.type, e.properties}; + add_element(this->edges, unbounded_edge, e.to == v.id ? 1 : -1, 1); + add_element(this->vertices, v, 1, 0); + } + } + + /** Unique vertices in the path. */ + std::vector vertices; + /** Unique edges in the path. */ + std::vector edges; + /** + * Indices that map path positions to vertices/edges. + * Positive indices for left-to-right directionality and negative for + * right-to-left. + */ + std::vector indices; +}; + /** Value represents supported values in the Bolt protocol. */ class Value { public: @@ -147,7 +191,8 @@ class Value { Date, LocalTime, LocalDateTime, - Duration + Duration, + Graph }; // constructors for primitive types @@ -167,6 +212,8 @@ class Value { Value(const Edge &value) : type_(Type::Edge) { new (&edge_v) Edge(value); } Value(const UnboundedEdge &value) : type_(Type::UnboundedEdge) { new (&unbounded_edge_v) UnboundedEdge(value); } Value(const Path &value) : type_(Type::Path) { new (&path_v) Path(value); } + Value(const Graph &value) : type_(Type::Graph) { new (&graph_v) Graph(value); } + Value(const utils::Date &date) : type_(Type::Date) { new (&date_v) utils::Date(date); } Value(const utils::LocalTime &time) : type_(Type::LocalTime) { new (&local_time_v) utils::LocalTime(time); } Value(const utils::LocalDateTime &date_time) : type_(Type::LocalDateTime) { @@ -185,6 +232,7 @@ class Value { new (&unbounded_edge_v) UnboundedEdge(std::move(value)); } Value(Path &&value) noexcept : type_(Type::Path) { new (&path_v) Path(std::move(value)); } + Value(Graph &&value) noexcept : type_(Type::Graph) { new (&graph_v) Graph(std::move(value)); } Value &operator=(const Value &other); Value &operator=(Value &&other) noexcept; @@ -220,6 +268,7 @@ class Value { DECL_GETTER_BY_REFERENCE(LocalTime, utils::LocalTime) DECL_GETTER_BY_REFERENCE(LocalDateTime, utils::LocalDateTime) DECL_GETTER_BY_REFERENCE(Duration, utils::Duration) + DECL_GETTER_BY_REFERENCE(Graph, Graph) #undef DECL_GETTER_BY_REFERNCE #define TYPE_CHECKER(type) \ @@ -239,6 +288,7 @@ class Value { TYPE_CHECKER(LocalTime) TYPE_CHECKER(LocalDateTime) TYPE_CHECKER(Duration) + TYPE_CHECKER(Graph) #undef TYPE_CHECKER friend std::ostream &operator<<(std::ostream &os, const Value &value); @@ -262,6 +312,7 @@ class Value { utils::LocalTime local_time_v; utils::LocalDateTime local_date_time_v; utils::Duration duration_v; + Graph graph_v; }; }; /** diff --git a/src/glue/communication.cpp b/src/glue/communication.cpp index a91c4b16a..f357d8b4c 100644 --- a/src/glue/communication.cpp +++ b/src/glue/communication.cpp @@ -60,6 +60,8 @@ query::TypedValue ToTypedValue(const Value &value) { return query::TypedValue(value.ValueLocalDateTime()); case Value::Type::Duration: return query::TypedValue(value.ValueDuration()); + case Value::Type::Graph: + throw communication::bolt::ValueException("Unsupported conversion from Value to TypedValue"); } } @@ -128,7 +130,9 @@ storage::Result ToBoltValue(const query::TypedValue &value, const storage case query::TypedValue::Type::Duration: return Value(value.ValueDuration()); case query::TypedValue::Type::Graph: - throw communication::bolt::ValueException("Unsupported conversion from TypedValue to Value for Graph"); + auto maybe_graph = ToBoltGraph(value.ValueGraph(), db, view); + if (maybe_graph.HasError()) return maybe_graph.GetError(); + return Value(std::move(*maybe_graph)); } } @@ -185,6 +189,25 @@ storage::Result ToBoltPath(const query::Path &path, c return communication::bolt::Path(vertices, edges); } +storage::Result ToBoltGraph(const query::Graph &graph, const storage::Storage &db, + storage::View view) { + std::vector vertices; + vertices.reserve(graph.vertices().size()); + for (const auto &v : graph.vertices()) { + auto maybe_vertex = ToBoltVertex(v, db, view); + if (maybe_vertex.HasError()) return maybe_vertex.GetError(); + vertices.emplace_back(std::move(*maybe_vertex)); + } + std::vector edges; + edges.reserve(graph.edges().size()); + for (const auto &e : graph.edges()) { + auto maybe_edge = ToBoltEdge(e, db, view); + if (maybe_edge.HasError()) return maybe_edge.GetError(); + edges.emplace_back(std::move(*maybe_edge)); + } + return communication::bolt::Graph(vertices, edges); +} + storage::PropertyValue ToPropertyValue(const Value &value) { switch (value.type()) { case Value::Type::Null: @@ -225,6 +248,8 @@ storage::PropertyValue ToPropertyValue(const Value &value) { case Value::Type::Duration: return storage::PropertyValue( storage::TemporalData(storage::TemporalType::Duration, value.ValueDuration().microseconds)); + case Value::Type::Graph: + throw communication::bolt::ValueException("Unsupported conversion from Value to PropertyValue"); } } diff --git a/src/glue/communication.hpp b/src/glue/communication.hpp index 6e5fd32c5..9b1ec670b 100644 --- a/src/glue/communication.hpp +++ b/src/glue/communication.hpp @@ -51,6 +51,14 @@ storage::Result ToBoltEdge(const storage::EdgeAccesso storage::Result ToBoltPath(const query::Path &path, const storage::Storage &db, storage::View view); +/// @param query::Graph for converting to communication::bolt::Graph. +/// @param storage::Storage for ToBoltVertex and ToBoltEdge. +/// @param storage::View for ToBoltVertex and ToBoltEdge. +/// +/// @throw std::bad_alloc +storage::Result ToBoltGraph(const query::Graph &graph, const storage::Storage &db, + storage::View view); + /// @param query::TypedValue for converting to communication::bolt::Value. /// @param storage::Storage for ToBoltVertex and ToBoltEdge. /// @param storage::View for ToBoltVertex and ToBoltEdge.