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 {
|
namespace communication::bolt {
|
||||||
|
|
||||||
bool DecodedValue::ValueBool() const {
|
#define DEF_GETTER_BY_VAL(type, value_type, field) \
|
||||||
if (type_ != Type::Bool) {
|
value_type DecodedValue::Value##type() const { \
|
||||||
throw DecodedValueException();
|
if (type_ != Type::type) throw DecodedValueException(); \
|
||||||
|
return field; \
|
||||||
}
|
}
|
||||||
return bool_v;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t DecodedValue::ValueInt() const {
|
DEF_GETTER_BY_VAL(Bool, bool, bool_v)
|
||||||
if (type_ != Type::Int) {
|
DEF_GETTER_BY_VAL(Int, int64_t, int_v)
|
||||||
throw DecodedValueException();
|
DEF_GETTER_BY_VAL(Double, double, double_v)
|
||||||
}
|
|
||||||
return int_v;
|
|
||||||
}
|
|
||||||
|
|
||||||
double DecodedValue::ValueDouble() const {
|
#undef DEF_GETTER_BY_VAL
|
||||||
if (type_ != Type::Double) {
|
|
||||||
throw DecodedValueException();
|
|
||||||
}
|
|
||||||
return double_v;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &DecodedValue::ValueString() const {
|
#define DEF_GETTER_BY_REF(type, value_type, field) \
|
||||||
if (type_ != Type::String) {
|
value_type &DecodedValue::Value##type() { \
|
||||||
throw DecodedValueException();
|
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 {
|
DEF_GETTER_BY_REF(String, std::string, string_v)
|
||||||
if (type_ != Type::List) {
|
DEF_GETTER_BY_REF(List, std::vector<DecodedValue>, list_v)
|
||||||
throw DecodedValueException();
|
using map_t = std::map<std::string, DecodedValue>;
|
||||||
}
|
DEF_GETTER_BY_REF(Map, map_t, map_v)
|
||||||
return list_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 {
|
#undef DEF_GETTER_BY_REF
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
|
DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
|
||||||
switch (other.type_) {
|
switch (other.type_) {
|
||||||
@ -142,6 +63,12 @@ DecodedValue::DecodedValue(const DecodedValue &other) : type_(other.type_) {
|
|||||||
case Type::Edge:
|
case Type::Edge:
|
||||||
new (&edge_v) DecodedEdge(other.edge_v);
|
new (&edge_v) DecodedEdge(other.edge_v);
|
||||||
return;
|
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");
|
permanent_fail("Unsupported DecodedValue::Type");
|
||||||
}
|
}
|
||||||
@ -179,6 +106,12 @@ DecodedValue &DecodedValue::operator=(const DecodedValue &other) {
|
|||||||
case Type::Edge:
|
case Type::Edge:
|
||||||
new (&edge_v) DecodedEdge(other.edge_v);
|
new (&edge_v) DecodedEdge(other.edge_v);
|
||||||
return *this;
|
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");
|
permanent_fail("Unsupported DecodedValue::Type");
|
||||||
}
|
}
|
||||||
@ -216,6 +149,12 @@ DecodedValue::~DecodedValue() {
|
|||||||
case Type::Edge:
|
case Type::Edge:
|
||||||
edge_v.~DecodedEdge();
|
edge_v.~DecodedEdge();
|
||||||
return;
|
return;
|
||||||
|
case Type::UnboundedEdge:
|
||||||
|
unbounded_edge_v.~DecodedUnboundedEdge();
|
||||||
|
return;
|
||||||
|
case Type::Path:
|
||||||
|
path_v.~DecodedPath();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
permanent_fail("Unsupported DecodedValue::Type");
|
permanent_fail("Unsupported DecodedValue::Type");
|
||||||
}
|
}
|
||||||
@ -238,7 +177,10 @@ DecodedValue::operator query::TypedValue() const {
|
|||||||
case Type::Map:
|
case Type::Map:
|
||||||
return query::TypedValue(
|
return query::TypedValue(
|
||||||
std::map<std::string, query::TypedValue>(map_v.begin(), map_v.end()));
|
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(
|
throw DecodedValueException(
|
||||||
"Unsupported conversion from DecodedValue to TypedValue");
|
"Unsupported conversion from DecodedValue to TypedValue");
|
||||||
}
|
}
|
||||||
@ -265,6 +207,37 @@ std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge) {
|
|||||||
return os << "}]";
|
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) {
|
std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
|
||||||
switch (value.type_) {
|
switch (value.type_) {
|
||||||
case DecodedValue::Type::Null:
|
case DecodedValue::Type::Null:
|
||||||
@ -292,6 +265,38 @@ std::ostream &operator<<(std::ostream &os, const DecodedValue &value) {
|
|||||||
return os << value.ValueVertex();
|
return os << value.ValueVertex();
|
||||||
case DecodedValue::Type::Edge:
|
case DecodedValue::Type::Edge:
|
||||||
return os << value.ValueEdge();
|
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");
|
permanent_fail("Unsupported DecodedValue::Type");
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,26 @@ struct DecodedEdge {
|
|||||||
std::map<std::string, DecodedValue> properties;
|
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
|
* DecodedValue provides an encapsulation arround TypedValue, DecodedVertex
|
||||||
* and DecodedEdge. This is necessary because TypedValue stores vertices and
|
* and DecodedEdge. This is necessary because TypedValue stores vertices and
|
||||||
@ -60,7 +80,9 @@ class DecodedValue {
|
|||||||
List,
|
List,
|
||||||
Map,
|
Map,
|
||||||
Vertex,
|
Vertex,
|
||||||
Edge
|
Edge,
|
||||||
|
UnboundedEdge,
|
||||||
|
Path
|
||||||
};
|
};
|
||||||
|
|
||||||
// constructors for primitive types
|
// constructors for primitive types
|
||||||
@ -86,6 +108,12 @@ class DecodedValue {
|
|||||||
DecodedValue(const DecodedEdge &value) : type_(Type::Edge) {
|
DecodedValue(const DecodedEdge &value) : type_(Type::Edge) {
|
||||||
new (&edge_v) DecodedEdge(value);
|
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 &operator=(const DecodedValue &other);
|
||||||
DecodedValue(const DecodedValue &other);
|
DecodedValue(const DecodedValue &other);
|
||||||
@ -93,25 +121,29 @@ class DecodedValue {
|
|||||||
|
|
||||||
Type type() const { return type_; }
|
Type type() const { return type_; }
|
||||||
|
|
||||||
// constant getters
|
#define DECL_GETTER_BY_VALUE(type, value_type) \
|
||||||
bool ValueBool() const;
|
value_type Value##type() 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;
|
|
||||||
|
|
||||||
// getters
|
DECL_GETTER_BY_VALUE(Bool, bool)
|
||||||
bool &ValueBool();
|
DECL_GETTER_BY_VALUE(Int, int64_t)
|
||||||
int64_t &ValueInt();
|
DECL_GETTER_BY_VALUE(Double, double)
|
||||||
double &ValueDouble();
|
|
||||||
std::string &ValueString();
|
#undef DECL_GETTER_BY_VALUE
|
||||||
std::vector<DecodedValue> &ValueList();
|
|
||||||
std::map<std::string, DecodedValue> &ValueMap();
|
#define DECL_GETTER_BY_REFERENCE(type, value_type) \
|
||||||
DecodedVertex &ValueVertex();
|
value_type &Value##type(); \
|
||||||
DecodedEdge &ValueEdge();
|
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
|
// conversion function to TypedValue
|
||||||
operator query::TypedValue() const;
|
operator query::TypedValue() const;
|
||||||
@ -131,6 +163,8 @@ class DecodedValue {
|
|||||||
std::map<std::string, DecodedValue> map_v;
|
std::map<std::string, DecodedValue> map_v;
|
||||||
DecodedVertex vertex_v;
|
DecodedVertex vertex_v;
|
||||||
DecodedEdge edge_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 DecodedVertex &vertex);
|
||||||
std::ostream &operator<<(std::ostream &os, const DecodedEdge &edge);
|
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 &value);
|
||||||
|
std::ostream &operator<<(std::ostream &os, const DecodedValue::Type type);
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,29 @@ class Decoder {
|
|||||||
case Marker::Map32:
|
case Marker::Map32:
|
||||||
return ReadMap(marker, data);
|
return ReadMap(marker, data);
|
||||||
|
|
||||||
case Marker::TinyStruct3:
|
case Marker::TinyStruct3: {
|
||||||
return ReadVertex(marker, data);
|
// 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:
|
case Marker::TinyStruct5:
|
||||||
return ReadEdge(marker, data);
|
return ReadEdge(marker, data);
|
||||||
@ -348,29 +369,12 @@ class Decoder {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadVertex(const Marker &marker, DecodedValue *data) {
|
bool ReadVertex(DecodedValue *data) {
|
||||||
uint8_t value;
|
|
||||||
DecodedValue dv;
|
DecodedValue dv;
|
||||||
DecodedVertex vertex;
|
DecodedVertex vertex;
|
||||||
|
|
||||||
DLOG(INFO) << "[ReadVertex] Start";
|
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
|
// read ID
|
||||||
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
||||||
DLOG(WARNING) << "[ReadVertex] Couldn't read ID!";
|
DLOG(WARNING) << "[ReadVertex] Couldn't read ID!";
|
||||||
@ -432,7 +436,7 @@ class Decoder {
|
|||||||
|
|
||||||
// read ID
|
// read ID
|
||||||
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
if (!ReadValue(&dv, DecodedValue::Type::Int)) {
|
||||||
DLOG(WARNING) << "[ReadEdge] couldn't read ID!";
|
DLOG(WARNING) << "[ReadEdge] Couldn't read ID!";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
edge.id = dv.ValueInt();
|
edge.id = dv.ValueInt();
|
||||||
@ -471,5 +475,91 @@ class Decoder {
|
|||||||
|
|
||||||
return true;
|
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) {
|
void WriteEdge(const EdgeAccessor &edge, bool unbound = false) {
|
||||||
WriteRAW(underlying_cast(Marker::TinyStruct) + 5);
|
WriteRAW(underlying_cast(Marker::TinyStruct) + (unbound ? 3 : 5));
|
||||||
WriteRAW(underlying_cast(Signature::Relationship));
|
WriteRAW(underlying_cast(unbound ? Signature::UnboundRelationship
|
||||||
|
: Signature::Relationship));
|
||||||
|
|
||||||
WriteUInt(edge.temporary_id());
|
WriteUInt(edge.temporary_id());
|
||||||
WriteUInt(edge.from().temporary_id());
|
if (!unbound) {
|
||||||
WriteUInt(edge.to().temporary_id());
|
WriteUInt(edge.from().temporary_id());
|
||||||
|
WriteUInt(edge.to().temporary_id());
|
||||||
|
}
|
||||||
|
|
||||||
// write type
|
// write type
|
||||||
WriteString(edge.db_accessor().EdgeTypeName(edge.EdgeType()));
|
WriteString(edge.db_accessor().EdgeTypeName(edge.EdgeType()));
|
||||||
@ -147,8 +150,47 @@ class BaseEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WritePath() {
|
void WritePath(const query::Path &path) {
|
||||||
// TODO: this isn't implemented in the backend!
|
// 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) {
|
void WriteTypedValue(const query::TypedValue &value) {
|
||||||
@ -181,8 +223,7 @@ class BaseEncoder {
|
|||||||
WriteEdge(value.Value<EdgeAccessor>());
|
WriteEdge(value.Value<EdgeAccessor>());
|
||||||
break;
|
break;
|
||||||
case query::TypedValue::Type::Path:
|
case query::TypedValue::Type::Path:
|
||||||
// TODO: this is not implemeted yet!
|
WritePath(value.ValuePath());
|
||||||
WritePath();
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,9 @@ class Path {
|
|||||||
Expand(others...);
|
Expand(others...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the number of expansions (edges) in this path. */
|
||||||
|
auto size() const { return edges_.size(); }
|
||||||
|
|
||||||
auto &vertices() { return vertices_; }
|
auto &vertices() { return vertices_; }
|
||||||
auto &edges() { return edges_; }
|
auto &edges() { return edges_; }
|
||||||
const auto &vertices() const { return vertices_; }
|
const auto &vertices() const { return vertices_; }
|
||||||
|
@ -12,9 +12,18 @@ VertexAccessor EdgeAccessor::from() const {
|
|||||||
return VertexAccessor(current().from_, db_accessor());
|
return VertexAccessor(current().from_, db_accessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EdgeAccessor::from_is(const VertexAccessor &v) const {
|
||||||
|
return v.operator==(¤t().from_);
|
||||||
|
}
|
||||||
|
|
||||||
VertexAccessor EdgeAccessor::to() const {
|
VertexAccessor EdgeAccessor::to() const {
|
||||||
return VertexAccessor(current().to_, db_accessor());
|
return VertexAccessor(current().to_, db_accessor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool EdgeAccessor::to_is(const VertexAccessor &v) const {
|
||||||
|
return v.operator==(¤t().to_);
|
||||||
|
}
|
||||||
|
|
||||||
bool EdgeAccessor::is_cycle() const {
|
bool EdgeAccessor::is_cycle() const {
|
||||||
return ¤t().to_ == ¤t().from_;
|
return ¤t().to_ == ¤t().from_;
|
||||||
}
|
}
|
||||||
|
@ -32,11 +32,19 @@ class EdgeAccessor : public RecordAccessor<Edge> {
|
|||||||
*/
|
*/
|
||||||
VertexAccessor from() const;
|
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.
|
* Returns an accessor to the destination Vertex of this edge.
|
||||||
*/
|
*/
|
||||||
VertexAccessor to() const;
|
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
|
/** Returns true if this edge is a cycle (start and end node are
|
||||||
* the same. */
|
* the same. */
|
||||||
bool is_cycle() const;
|
bool is_cycle() const;
|
||||||
|
@ -100,6 +100,13 @@ class RecordAccessor : public TotalOrdering<RecordAccessor<TRecord>> {
|
|||||||
return vlist_ == other.vlist_;
|
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.
|
* Returns a GraphDB accessor of this record accessor.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user