Bolt - path enconding and decoding
Summary: Added path encoding and decoding in our Bolt layer. Returning paths to the Neo client seems to work fine. Not sure how to test that the decoded works fine (our client?). Tests are not written, that suite is complicated. Leaving that to mferencevic. Also reduced DecodedValue a bit with macros. Less code, less error prone. Reviewers: buda, florijan Reviewed By: florijan Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D810
This commit is contained in:
parent
41a027a721
commit
383175d85f
@ -2,117 +2,38 @@
|
||||
|
||||
namespace communication::bolt {
|
||||
|
||||
bool DecodedValue::ValueBool() const {
|
||||
if (type_ != Type::Bool) {
|
||||
throw DecodedValueException();
|
||||
#define DEF_GETTER_BY_VAL(type, value_type, field) \
|
||||
value_type DecodedValue::Value##type() const { \
|
||||
if (type_ != Type::type) throw DecodedValueException(); \
|
||||
return field; \
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
int64_t DecodedValue::ValueInt() const {
|
||||
if (type_ != Type::Int) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
DEF_GETTER_BY_VAL(Bool, bool, bool_v)
|
||||
DEF_GETTER_BY_VAL(Int, int64_t, int_v)
|
||||
DEF_GETTER_BY_VAL(Double, double, double_v)
|
||||
|
||||
double DecodedValue::ValueDouble() const {
|
||||
if (type_ != Type::Double) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return double_v;
|
||||
}
|
||||
#undef DEF_GETTER_BY_VAL
|
||||
|
||||
const std::string &DecodedValue::ValueString() const {
|
||||
if (type_ != Type::String) {
|
||||
throw DecodedValueException();
|
||||
#define DEF_GETTER_BY_REF(type, value_type, field) \
|
||||
value_type &DecodedValue::Value##type() { \
|
||||
if (type_ != Type::type) throw DecodedValueException(); \
|
||||
return field; \
|
||||
} \
|
||||
const value_type &DecodedValue::Value##type() const { \
|
||||
if (type_ != Type::type) throw DecodedValueException(); \
|
||||
return field; \
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
const std::vector<DecodedValue> &DecodedValue::ValueList() const {
|
||||
if (type_ != Type::List) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
DEF_GETTER_BY_REF(String, std::string, string_v)
|
||||
DEF_GETTER_BY_REF(List, std::vector<DecodedValue>, list_v)
|
||||
using map_t = std::map<std::string, DecodedValue>;
|
||||
DEF_GETTER_BY_REF(Map, map_t, map_v)
|
||||
DEF_GETTER_BY_REF(Vertex, DecodedVertex, vertex_v)
|
||||
DEF_GETTER_BY_REF(Edge, DecodedEdge, edge_v)
|
||||
DEF_GETTER_BY_REF(UnboundedEdge, DecodedUnboundedEdge, unbounded_edge_v)
|
||||
DEF_GETTER_BY_REF(Path, DecodedPath, path_v)
|
||||
|
||||
const std::map<std::string, DecodedValue> &DecodedValue::ValueMap() const {
|
||||
if (type_ != Type::Map) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return map_v;
|
||||
}
|
||||
|
||||
const DecodedVertex &DecodedValue::ValueVertex() const {
|
||||
if (type_ != Type::Vertex) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return vertex_v;
|
||||
}
|
||||
|
||||
const DecodedEdge &DecodedValue::ValueEdge() const {
|
||||
if (type_ != Type::Edge) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return edge_v;
|
||||
}
|
||||
|
||||
bool &DecodedValue::ValueBool() {
|
||||
if (type_ != Type::Bool) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return bool_v;
|
||||
}
|
||||
|
||||
int64_t &DecodedValue::ValueInt() {
|
||||
if (type_ != Type::Int) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return int_v;
|
||||
}
|
||||
|
||||
double &DecodedValue::ValueDouble() {
|
||||
if (type_ != Type::Double) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return double_v;
|
||||
}
|
||||
|
||||
std::string &DecodedValue::ValueString() {
|
||||
if (type_ != Type::String) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return string_v;
|
||||
}
|
||||
|
||||
std::vector<DecodedValue> &DecodedValue::ValueList() {
|
||||
if (type_ != Type::List) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return list_v;
|
||||
}
|
||||
|
||||
std::map<std::string, DecodedValue> &DecodedValue::ValueMap() {
|
||||
if (type_ != Type::Map) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return map_v;
|
||||
}
|
||||
|
||||
DecodedVertex &DecodedValue::ValueVertex() {
|
||||
if (type_ != Type::Vertex) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return vertex_v;
|
||||
}
|
||||
|
||||
DecodedEdge &DecodedValue::ValueEdge() {
|
||||
if (type_ != Type::Edge) {
|
||||
throw DecodedValueException();
|
||||
}
|
||||
return edge_v;
|
||||
}
|
||||
#undef DEF_GETTER_BY_REF
|
||||
|
||||
DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
|
||||
switch (other.type_) {
|
||||
@ -142,6 +63,12 @@ DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
|
||||
case Type::Edge:
|
||||
new (&edge_v) DecodedEdge(other.edge_v);
|
||||
return;
|
||||
case Type::UnboundedEdge:
|
||||
new (&unbounded_edge_v) DecodedUnboundedEdge(other.unbounded_edge_v);
|
||||
return;
|
||||
case Type::Path:
|
||||
new (&path_v) DecodedPath(other.path_v);
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported DecodedValue::Type");
|
||||
}
|
||||
@ -179,6 +106,12 @@ DecodedValue &DecodedValue::operator=(const DecodedValue &other) {
|
||||
case Type::Edge:
|
||||
new (&edge_v) DecodedEdge(other.edge_v);
|
||||
return *this;
|
||||
case Type::UnboundedEdge:
|
||||
new (&unbounded_edge_v) DecodedUnboundedEdge(other.unbounded_edge_v);
|
||||
return *this;
|
||||
case Type::Path:
|
||||
new (&path_v) DecodedPath(other.path_v);
|
||||
return *this;
|
||||
}
|
||||
permanent_fail("Unsupported DecodedValue::Type");
|
||||
}
|
||||
@ -216,6 +149,12 @@ DecodedValue::~DecodedValue() {
|
||||
case Type::Edge:
|
||||
edge_v.~DecodedEdge();
|
||||
return;
|
||||
case Type::UnboundedEdge:
|
||||
unbounded_edge_v.~DecodedUnboundedEdge();
|
||||
return;
|
||||
case Type::Path:
|
||||
path_v.~DecodedPath();
|
||||
return;
|
||||
}
|
||||
permanent_fail("Unsupported DecodedValue::Type");
|
||||
}
|
||||
@ -238,7 +177,10 @@ DecodedValue::operator query::TypedValue() const {
|
||||
case Type::Map:
|
||||
return query::TypedValue(
|
||||
std::map<std::string, query::TypedValue>(map_v.begin(), map_v.end()));
|
||||
default:
|
||||
case Type::Vertex:
|
||||
case Type::Edge:
|
||||
case Type::UnboundedEdge:
|
||||
case Type::Path:
|
||||
throw DecodedValueException(
|
||||
"Unsupported conversion from DecodedValue to TypedValue");
|
||||
}
|
||||
@ -265,6 +207,37 @@ std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge) {
|
||||
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;
|
||||
});
|
||||
return os << "}]";
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedPath &path) {
|
||||
os << path.vertices[0];
|
||||
debug_assert(path.indices.size() % 2 == 0,
|
||||
"Must have even number of indices");
|
||||
for (auto it = path.indices.begin(); it != path.indices.end();) {
|
||||
auto edge_ind = *it++;
|
||||
auto vertex_ind = *it++;
|
||||
bool arrow_to_right = true;
|
||||
if (edge_ind < 0) {
|
||||
arrow_to_right = false;
|
||||
edge_ind = -edge_ind;
|
||||
}
|
||||
|
||||
if (!arrow_to_right) os << "<";
|
||||
os << "-" << path.edges[edge_ind - 1] << "-";
|
||||
if (arrow_to_right) os << ">";
|
||||
os << path.vertices[vertex_ind];
|
||||
}
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
|
||||
switch (value.type_) {
|
||||
case DecodedValue::Type::Null:
|
||||
@ -292,6 +265,38 @@ std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
|
||||
return os << value.ValueVertex();
|
||||
case DecodedValue::Type::Edge:
|
||||
return os << value.ValueEdge();
|
||||
case DecodedValue::Type::UnboundedEdge:
|
||||
return os << value.ValueUnboundedEdge();
|
||||
case DecodedValue::Type::Path:
|
||||
return os << value.ValuePath();
|
||||
}
|
||||
permanent_fail("Unsupported DecodedValue::Type");
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedValue::Type type) {
|
||||
switch (type) {
|
||||
case DecodedValue::Type::Null:
|
||||
return os << "null";
|
||||
case DecodedValue::Type::Bool:
|
||||
return os << "bool";
|
||||
case DecodedValue::Type::Int:
|
||||
return os << "int";
|
||||
case DecodedValue::Type::Double:
|
||||
return os << "double";
|
||||
case DecodedValue::Type::String:
|
||||
return os << "string";
|
||||
case DecodedValue::Type::List:
|
||||
return os << "list";
|
||||
case DecodedValue::Type::Map:
|
||||
return os << "map";
|
||||
case DecodedValue::Type::Vertex:
|
||||
return os << "vertex";
|
||||
case DecodedValue::Type::Edge:
|
||||
return os << "edge";
|
||||
case DecodedValue::Type::UnboundedEdge:
|
||||
return os << "unbounded_edge";
|
||||
case DecodedValue::Type::Path:
|
||||
return os << "path";
|
||||
}
|
||||
permanent_fail("Unsupported DecodedValue::Type");
|
||||
}
|
||||
|
@ -37,6 +37,26 @@ struct DecodedEdge {
|
||||
std::map<std::string, DecodedValue> properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure used when reading an UnboundEdge with the decoder.
|
||||
* The decoder writes data into this structure.
|
||||
*/
|
||||
struct DecodedUnboundedEdge {
|
||||
int64_t id;
|
||||
std::string type;
|
||||
std::map<std::string, DecodedValue> properties;
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure used when reading a Path with the decoder.
|
||||
* The decoder writes data into this structure.
|
||||
*/
|
||||
struct DecodedPath {
|
||||
std::vector<DecodedVertex> vertices;
|
||||
std::vector<DecodedUnboundedEdge> edges;
|
||||
std::vector<int64_t> indices;
|
||||
};
|
||||
|
||||
/**
|
||||
* DecodedValue provides an encapsulation arround TypedValue, DecodedVertex
|
||||
* and DecodedEdge. This is necessary because TypedValue stores vertices and
|
||||
@ -60,7 +80,9 @@ class DecodedValue {
|
||||
List,
|
||||
Map,
|
||||
Vertex,
|
||||
Edge
|
||||
Edge,
|
||||
UnboundedEdge,
|
||||
Path
|
||||
};
|
||||
|
||||
// constructors for primitive types
|
||||
@ -86,6 +108,12 @@ class DecodedValue {
|
||||
DecodedValue(const DecodedEdge &value) : type_(Type::Edge) {
|
||||
new (&edge_v) DecodedEdge(value);
|
||||
}
|
||||
DecodedValue(const DecodedUnboundedEdge &value) : type_(Type::UnboundedEdge) {
|
||||
new (&unbounded_edge_v) DecodedUnboundedEdge(value);
|
||||
}
|
||||
DecodedValue(const DecodedPath &value) : type_(Type::Path) {
|
||||
new (&path_v) DecodedPath(value);
|
||||
}
|
||||
|
||||
DecodedValue &operator=(const DecodedValue &other);
|
||||
DecodedValue(const DecodedValue &other);
|
||||
@ -93,25 +121,29 @@ class DecodedValue {
|
||||
|
||||
Type type() const { return type_; }
|
||||
|
||||
// constant getters
|
||||
bool ValueBool() const;
|
||||
int64_t ValueInt() const;
|
||||
double ValueDouble() const;
|
||||
const std::string &ValueString() const;
|
||||
const std::vector<DecodedValue> &ValueList() const;
|
||||
const std::map<std::string, DecodedValue> &ValueMap() const;
|
||||
const DecodedVertex &ValueVertex() const;
|
||||
const DecodedEdge &ValueEdge() const;
|
||||
#define DECL_GETTER_BY_VALUE(type, value_type) \
|
||||
value_type Value##type() const;
|
||||
|
||||
// getters
|
||||
bool &ValueBool();
|
||||
int64_t &ValueInt();
|
||||
double &ValueDouble();
|
||||
std::string &ValueString();
|
||||
std::vector<DecodedValue> &ValueList();
|
||||
std::map<std::string, DecodedValue> &ValueMap();
|
||||
DecodedVertex &ValueVertex();
|
||||
DecodedEdge &ValueEdge();
|
||||
DECL_GETTER_BY_VALUE(Bool, bool)
|
||||
DECL_GETTER_BY_VALUE(Int, int64_t)
|
||||
DECL_GETTER_BY_VALUE(Double, double)
|
||||
|
||||
#undef DECL_GETTER_BY_VALUE
|
||||
|
||||
#define DECL_GETTER_BY_REFERENCE(type, value_type) \
|
||||
value_type &Value##type(); \
|
||||
const value_type &Value##type() const;
|
||||
|
||||
DECL_GETTER_BY_REFERENCE(String, std::string)
|
||||
DECL_GETTER_BY_REFERENCE(List, std::vector<DecodedValue>)
|
||||
using map_t = std::map<std::string, DecodedValue>;
|
||||
DECL_GETTER_BY_REFERENCE(Map, map_t)
|
||||
DECL_GETTER_BY_REFERENCE(Vertex, DecodedVertex)
|
||||
DECL_GETTER_BY_REFERENCE(Edge, DecodedEdge)
|
||||
DECL_GETTER_BY_REFERENCE(UnboundedEdge, DecodedUnboundedEdge)
|
||||
DECL_GETTER_BY_REFERENCE(Path, DecodedPath)
|
||||
|
||||
#undef DECL_GETTER_BY_REFERNCE
|
||||
|
||||
// conversion function to TypedValue
|
||||
operator query::TypedValue() const;
|
||||
@ -131,6 +163,8 @@ class DecodedValue {
|
||||
std::map<std::string, DecodedValue> map_v;
|
||||
DecodedVertex vertex_v;
|
||||
DecodedEdge edge_v;
|
||||
DecodedUnboundedEdge unbounded_edge_v;
|
||||
DecodedPath path_v;
|
||||
};
|
||||
};
|
||||
|
||||
@ -149,5 +183,8 @@ class DecodedValueException : public utils::BasicException {
|
||||
*/
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedVertex &vertex);
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge);
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedUnboundedEdge &edge);
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedPath &path);
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedValue &value);
|
||||
std::ostream &operator<<(std::ostream &os, const DecodedValue::Type type);
|
||||
}
|
||||
|
@ -75,8 +75,29 @@ class Decoder {
|
||||
case Marker::Map32:
|
||||
return ReadMap(marker, data);
|
||||
|
||||
case Marker::TinyStruct3:
|
||||
return ReadVertex(marker, data);
|
||||
case Marker::TinyStruct3: {
|
||||
// For tiny struct 3 we will also read the Signature to switch between
|
||||
// vertex, unbounded_edge and path. Note that in those functions we
|
||||
// won't perform an additional signature read.
|
||||
uint8_t signature;
|
||||
if (!buffer_.Read(&signature, 1)) {
|
||||
DLOG(WARNING) << "[ReadVertex] Missing marker and/or signature data!";
|
||||
return false;
|
||||
}
|
||||
switch (static_cast<Signature>(signature)) {
|
||||
case Signature::Node:
|
||||
return ReadVertex(data);
|
||||
case Signature::UnboundRelationship:
|
||||
return ReadUnboundedEdge(data);
|
||||
case Signature::Path:
|
||||
return ReadPath(data);
|
||||
default:
|
||||
DLOG(WARNING) << "[ReadValue] Expected [node | unbounded_ege | "
|
||||
"path] signature, received "
|
||||
<< signature;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case Marker::TinyStruct5:
|
||||
return ReadEdge(marker, data);
|
||||
@ -348,29 +369,12 @@ class Decoder {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadVertex(const Marker &marker, DecodedValue *data) {
|
||||
uint8_t value;
|
||||
bool ReadVertex(DecodedValue *data) {
|
||||
DecodedValue dv;
|
||||
DecodedVertex vertex;
|
||||
|
||||
DLOG(INFO) << "[ReadVertex] Start";
|
||||
|
||||
if (!buffer_.Read(&value, 1)) {
|
||||
DLOG(WARNING) << "[ReadVertex] Missing marker and/or signature data!";
|
||||
return false;
|
||||
}
|
||||
|
||||
// check header
|
||||
if (marker != Marker::TinyStruct3) {
|
||||
DLOG(WARNING) << "[ReadVertex] Received invalid marker "
|
||||
<< (uint64_t)underlying_cast(marker);
|
||||
return false;
|
||||
}
|
||||
if (value != underlying_cast(Signature::Node)) {
|
||||
DLOG(WARNING) << "[ReadVertex] Received invalid signature " << value;
|
||||
return false;
|
||||
}
|
||||
|
||||
// read ID
|
||||
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
||||
DLOG(WARNING) << "[ReadVertex] Couldn't read ID!";
|
||||
@ -432,7 +436,7 @@ class Decoder {
|
||||
|
||||
// read ID
|
||||
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
||||
DLOG(WARNING) << "[ReadEdge] couldn't read ID!";
|
||||
DLOG(WARNING) << "[ReadEdge] Couldn't read ID!";
|
||||
return false;
|
||||
}
|
||||
edge.id = dv.ValueInt();
|
||||
@ -471,5 +475,91 @@ class Decoder {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadUnboundedEdge(DecodedValue *data) {
|
||||
DecodedValue dv;
|
||||
DecodedUnboundedEdge edge;
|
||||
|
||||
DLOG(INFO) << "[ReadUnboundedEdge] Start";
|
||||
|
||||
// read ID
|
||||
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
||||
DLOG(WARNING) << "[ReadUnboundedEdge] Couldn't read ID!";
|
||||
return false;
|
||||
}
|
||||
edge.id = dv.ValueInt();
|
||||
|
||||
// read type
|
||||
if (!ReadValue(&dv, DecodedValue::Type::String)) {
|
||||
DLOG(WARNING) << "[ReadUnboundedEdge] Couldn't read type!";
|
||||
return false;
|
||||
}
|
||||
edge.type = dv.ValueString();
|
||||
|
||||
// read properties
|
||||
if (!ReadValue(&dv, DecodedValue::Type::Map)) {
|
||||
DLOG(WARNING) << "[ReadUnboundedEdge] Couldn't read properties!";
|
||||
return false;
|
||||
}
|
||||
edge.properties = dv.ValueMap();
|
||||
|
||||
*data = DecodedValue(edge);
|
||||
|
||||
DLOG(INFO) << "[ReadUnboundedEdge] Success";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadPath(DecodedValue *data) {
|
||||
DecodedValue dv;
|
||||
DecodedPath path;
|
||||
|
||||
DLOG(INFO) << "[ReadPath] Start";
|
||||
|
||||
// vertices
|
||||
if (!ReadValue(&dv, DecodedValue::Type::List)) {
|
||||
DLOG(WARNING) << "[ReadPath] Couldn't read vertices!";
|
||||
return false;
|
||||
}
|
||||
for (const auto &vertex : dv.ValueList()) {
|
||||
if (vertex.type() != DecodedValue::Type::Vertex) {
|
||||
DLOG(WARNING) << "[ReadPath] Received a '" << vertex.type() << "' element in the vertices list!";
|
||||
return false;
|
||||
}
|
||||
path.vertices.emplace_back(vertex.ValueVertex());
|
||||
}
|
||||
|
||||
// edges
|
||||
if (!ReadValue(&dv, DecodedValue::Type::List)) {
|
||||
DLOG(WARNING) << "[ReadPath] Couldn't read edges!";
|
||||
return false;
|
||||
}
|
||||
for (const auto &edge : dv.ValueList()) {
|
||||
if (edge.type() != DecodedValue::Type::UnboundedEdge) {
|
||||
DLOG(WARNING) << "[ReadPath] Received a '" << edge.type() << "' element in the edges list!";
|
||||
return false;
|
||||
}
|
||||
path.edges.emplace_back(edge.ValueUnboundedEdge());
|
||||
}
|
||||
|
||||
// indices
|
||||
if (!ReadValue(&dv, DecodedValue::Type::List)) {
|
||||
DLOG(WARNING) << "[ReadPath] Couldn't read indices!";
|
||||
return false;
|
||||
}
|
||||
for (const auto &index : dv.ValueList()) {
|
||||
if (index.type() != DecodedValue::Type::Int) {
|
||||
DLOG(WARNING) << "[ReadPath] Received a '" << index.type() << "' element in the indices list (expected an int)!";
|
||||
return false;
|
||||
}
|
||||
path.indices.emplace_back(index.ValueInt());
|
||||
}
|
||||
|
||||
*data = DecodedValue(path);
|
||||
|
||||
DLOG(INFO) << "[ReadPath] Success";
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -127,13 +127,16 @@ class BaseEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
void WriteEdge(const EdgeAccessor &edge) {
|
||||
WriteRAW(underlying_cast(Marker::TinyStruct) + 5);
|
||||
WriteRAW(underlying_cast(Signature::Relationship));
|
||||
void WriteEdge(const EdgeAccessor &edge, bool unbound = false) {
|
||||
WriteRAW(underlying_cast(Marker::TinyStruct) + (unbound ? 3 : 5));
|
||||
WriteRAW(underlying_cast(unbound ? Signature::UnboundRelationship
|
||||
: Signature::Relationship));
|
||||
|
||||
WriteUInt(edge.temporary_id());
|
||||
WriteUInt(edge.from().temporary_id());
|
||||
WriteUInt(edge.to().temporary_id());
|
||||
if (!unbound) {
|
||||
WriteUInt(edge.from().temporary_id());
|
||||
WriteUInt(edge.to().temporary_id());
|
||||
}
|
||||
|
||||
// write type
|
||||
WriteString(edge.db_accessor().EdgeTypeName(edge.EdgeType()));
|
||||
@ -147,8 +150,47 @@ class BaseEncoder {
|
||||
}
|
||||
}
|
||||
|
||||
void WritePath() {
|
||||
// TODO: this isn't implemented in the backend!
|
||||
void WritePath(const query::Path &path) {
|
||||
// Prepare the data structures to be written.
|
||||
//
|
||||
// Unique vertices in the path.
|
||||
std::vector<VertexAccessor> vertices;
|
||||
// Unique edges in the path.
|
||||
std::vector<EdgeAccessor> edges;
|
||||
// Indices that map path positions to vertices/edges elements. Positive
|
||||
// indices for left-to-right directionality and negative for right-to-left.
|
||||
std::vector<int> indices;
|
||||
|
||||
// Helper function. Looks for the given element in the collection. If found
|
||||
// it puts it's 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 = [&indices](auto &collection, const auto &element,
|
||||
int multiplier, int offset) {
|
||||
auto found = std::find(collection.begin(), collection.end(), element);
|
||||
indices.emplace_back(
|
||||
multiplier * (std::distance(collection.begin(), found) + offset));
|
||||
if (found == collection.end()) collection.emplace_back(element);
|
||||
};
|
||||
|
||||
vertices.emplace_back(path.vertices()[0]);
|
||||
for (uint i = 0; i < path.size(); i++) {
|
||||
const auto &e = path.edges()[i];
|
||||
const auto &v = path.vertices()[i + 1];
|
||||
add_element(edges, e, e.to_is(v) ? 1 : -1, 1);
|
||||
add_element(vertices, v, 1, 0);
|
||||
}
|
||||
|
||||
// Write data.
|
||||
WriteRAW(underlying_cast(Marker::TinyStruct) + 3);
|
||||
WriteRAW(underlying_cast(Signature::Path));
|
||||
WriteTypeSize(vertices.size(), MarkerList);
|
||||
for (auto &v : vertices) WriteVertex(v);
|
||||
WriteTypeSize(edges.size(), MarkerList);
|
||||
for (auto &e : edges) WriteEdge(e, true);
|
||||
WriteTypeSize(indices.size(), MarkerList);
|
||||
for (auto &i : indices) WriteInt(i);
|
||||
}
|
||||
|
||||
void WriteTypedValue(const query::TypedValue &value) {
|
||||
@ -181,8 +223,7 @@ class BaseEncoder {
|
||||
WriteEdge(value.Value<EdgeAccessor>());
|
||||
break;
|
||||
case query::TypedValue::Type::Path:
|
||||
// TODO: this is not implemeted yet!
|
||||
WritePath();
|
||||
WritePath(value.ValuePath());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,9 @@ class Path {
|
||||
Expand(others...);
|
||||
}
|
||||
|
||||
/** Returns the number of expansions (edges) in this path. */
|
||||
auto size() const { return edges_.size(); }
|
||||
|
||||
auto &vertices() { return vertices_; }
|
||||
auto &edges() { return edges_; }
|
||||
const auto &vertices() const { return vertices_; }
|
||||
|
@ -12,9 +12,18 @@ VertexAccessor EdgeAccessor::from() const {
|
||||
return VertexAccessor(current().from_, db_accessor());
|
||||
}
|
||||
|
||||
bool EdgeAccessor::from_is(const VertexAccessor &v) const {
|
||||
return v.operator==(¤t().from_);
|
||||
}
|
||||
|
||||
VertexAccessor EdgeAccessor::to() const {
|
||||
return VertexAccessor(current().to_, db_accessor());
|
||||
}
|
||||
|
||||
bool EdgeAccessor::to_is(const VertexAccessor &v) const {
|
||||
return v.operator==(¤t().to_);
|
||||
}
|
||||
|
||||
bool EdgeAccessor::is_cycle() const {
|
||||
return ¤t().to_ == ¤t().from_;
|
||||
}
|
||||
|
@ -32,11 +32,19 @@ class EdgeAccessor : public RecordAccessor<Edge> {
|
||||
*/
|
||||
VertexAccessor from() const;
|
||||
|
||||
/** Checks if the given vertex is the source of this edge, without
|
||||
* creating an additional accessor to perform the check. */
|
||||
bool from_is(const VertexAccessor &v) const;
|
||||
|
||||
/**
|
||||
* Returns an accessor to the destination Vertex of this edge.
|
||||
*/
|
||||
VertexAccessor to() const;
|
||||
|
||||
/** Checks ig the given vertex is the destination of this edge, without
|
||||
* creating an additional accessor to perform the check. */
|
||||
bool to_is(const VertexAccessor &v) const;
|
||||
|
||||
/** Returns true if this edge is a cycle (start and end node are
|
||||
* the same. */
|
||||
bool is_cycle() const;
|
||||
|
@ -100,6 +100,13 @@ class RecordAccessor : public TotalOrdering<RecordAccessor<TRecord>> {
|
||||
return vlist_ == other.vlist_;
|
||||
}
|
||||
|
||||
/** Enables equality check against a version list pointer. This makes it
|
||||
* possible to check if an accessor and a vlist ptr represent the same graph
|
||||
* element without creating an accessor (not very cheap). */
|
||||
bool operator==(const mvcc::VersionList<TRecord> *other_vlist) const {
|
||||
return vlist_ == other_vlist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a GraphDB accessor of this record accessor.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user