memgraph/src/communication/bolt/v1/value.hpp
2023-11-22 13:05:02 +00:00

292 lines
9.6 KiB
C++

// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "utils/cast.hpp"
#include "utils/exceptions.hpp"
#include "utils/temporal.hpp"
namespace memgraph::communication::bolt {
/** Forward declaration of Value class. */
class Value;
/** Wraps int64_t to prevent dangerous implicit conversions. */
class Id {
public:
Id() = default;
/** Construct Id from uint64_t */
static Id FromUint(uint64_t id) { return Id(utils::MemcpyCast<int64_t>(id)); }
/** Construct Id from int64_t */
static Id FromInt(int64_t id) { return Id(id); }
int64_t AsInt() const { return id_; }
uint64_t AsUint() const { return utils::MemcpyCast<uint64_t>(id_); }
private:
explicit Id(int64_t id) : id_(id) {}
int64_t id_;
};
inline bool operator==(const Id &id1, const Id &id2) { return id1.AsInt() == id2.AsInt(); }
inline bool operator!=(const Id &id1, const Id &id2) { return !(id1 == id2); }
/**
* Structure used when reading a Vertex with the decoder.
* The decoder writes data into this structure.
*/
struct Vertex {
Id id;
std::vector<std::string> labels;
std::map<std::string, Value> properties;
std::string element_id;
};
/**
* Structure used when reading an Edge with the decoder.
* The decoder writes data into this structure.
*/
struct Edge {
Id id;
Id from;
Id to;
std::string type;
std::map<std::string, Value> properties;
std::string element_id;
std::string from_element_id;
std::string to_element_id;
};
/**
* Structure used when reading an UnboundEdge with the decoder.
* The decoder writes data into this structure.
*/
struct UnboundedEdge {
Id id;
std::string type;
std::map<std::string, Value> properties;
std::string element_id;
};
/**
* Structure used when reading a Path with the decoder.
* The decoder writes data into this structure.
*/
struct Path {
Path() = default;
Path(const std::vector<Vertex> &vertices, const std::vector<Edge> &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, e.element_id};
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<Vertex> vertices;
/** Unique edges in the path. */
std::vector<UnboundedEdge> edges;
/**
* Indices that map path positions to vertices/edges.
* Positive indices for left-to-right directionality and negative for
* right-to-left.
*/
std::vector<int64_t> indices;
};
/** Value represents supported values in the Bolt protocol. */
class Value {
public:
/** Default constructor, makes Null */
Value() : type_(Type::Null) {}
/** Types that can be stored in a Value. */
enum class Type : unsigned {
Null,
Bool,
Int,
Double,
String,
List,
Map,
Vertex,
Edge,
UnboundedEdge,
Path,
Date,
LocalTime,
LocalDateTime,
Duration
};
// constructors for primitive types
Value(bool value) : type_(Type::Bool) { bool_v = value; }
Value(int value) : type_(Type::Int) { int_v = value; }
Value(int64_t value) : type_(Type::Int) { int_v = value; }
Value(double value) : type_(Type::Double) { double_v = value; }
// constructors for non-primitive types
Value(const std::string &value) : type_(Type::String) { new (&string_v) std::string(value); }
Value(const char *value) : Value(std::string(value)) {}
Value(const std::vector<Value> &value) : type_(Type::List) { new (&list_v) std::vector<Value>(value); }
Value(const std::map<std::string, Value> &value) : type_(Type::Map) {
new (&map_v) std::map<std::string, Value>(value);
}
Value(const Vertex &value) : type_(Type::Vertex) { new (&vertex_v) Vertex(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 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) {
new (&local_date_time_v) utils::LocalDateTime(date_time);
}
Value(const utils::Duration &dur) : type_(Type::Duration) { new (&duration_v) utils::Duration(dur); }
// move constructors for non-primitive values
Value(std::string &&value) noexcept : type_(Type::String) { new (&string_v) std::string(std::move(value)); }
Value(std::vector<Value> &&value) noexcept : type_(Type::List) { new (&list_v) std::vector<Value>(std::move(value)); }
Value(std::map<std::string, Value> &&value) noexcept : type_(Type::Map) {
new (&map_v) std::map<std::string, Value>(std::move(value));
}
Value(Vertex &&value) noexcept : type_(Type::Vertex) { new (&vertex_v) Vertex(std::move(value)); }
Value(Edge &&value) noexcept : type_(Type::Edge) { new (&edge_v) Edge(std::move(value)); }
Value(UnboundedEdge &&value) noexcept : type_(Type::UnboundedEdge) {
new (&unbounded_edge_v) UnboundedEdge(std::move(value));
}
Value(Path &&value) noexcept : type_(Type::Path) { new (&path_v) Path(std::move(value)); }
Value &operator=(const Value &other);
Value &operator=(Value &&other) noexcept;
Value(const Value &other);
Value(Value &&other) noexcept;
~Value();
Type type() const { return type_; }
#define DECL_GETTER_BY_VALUE(type, value_type) \
value_type &Value##type(); \
value_type Value##type() const;
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<Value>)
using map_t = std::map<std::string, Value>;
DECL_GETTER_BY_REFERENCE(Map, map_t)
DECL_GETTER_BY_REFERENCE(Vertex, Vertex)
DECL_GETTER_BY_REFERENCE(Edge, Edge)
DECL_GETTER_BY_REFERENCE(UnboundedEdge, UnboundedEdge)
DECL_GETTER_BY_REFERENCE(Path, Path)
DECL_GETTER_BY_REFERENCE(Date, utils::Date)
DECL_GETTER_BY_REFERENCE(LocalTime, utils::LocalTime)
DECL_GETTER_BY_REFERENCE(LocalDateTime, utils::LocalDateTime)
DECL_GETTER_BY_REFERENCE(Duration, utils::Duration)
#undef DECL_GETTER_BY_REFERNCE
#define TYPE_CHECKER(type) \
bool Is##type() const { return type_ == Type::type; }
TYPE_CHECKER(Bool)
TYPE_CHECKER(Int)
TYPE_CHECKER(Double)
TYPE_CHECKER(String)
TYPE_CHECKER(List)
TYPE_CHECKER(Map)
TYPE_CHECKER(Vertex)
TYPE_CHECKER(Edge)
TYPE_CHECKER(UnboundedEdge)
TYPE_CHECKER(Path)
TYPE_CHECKER(Date)
TYPE_CHECKER(LocalTime)
TYPE_CHECKER(LocalDateTime)
TYPE_CHECKER(Duration)
#undef TYPE_CHECKER
friend std::ostream &operator<<(std::ostream &os, const Value &value);
private:
Type type_;
// storage for the value of the property
union {
bool bool_v;
int64_t int_v;
double double_v;
std::string string_v;
std::vector<Value> list_v;
std::map<std::string, Value> map_v;
Vertex vertex_v;
Edge edge_v;
UnboundedEdge unbounded_edge_v;
Path path_v;
utils::Date date_v;
utils::LocalTime local_time_v;
utils::LocalDateTime local_date_time_v;
utils::Duration duration_v;
};
};
/**
* An exception raised by the Value system.
*/
class ValueException : public utils::BasicException {
public:
using utils::BasicException::BasicException;
ValueException() : BasicException("Incompatible template param and type!") {}
SPECIALIZE_GET_EXCEPTION_NAME(ValueException)
};
/**
* Output operators.
*/
std::ostream &operator<<(std::ostream &os, const Vertex &vertex);
std::ostream &operator<<(std::ostream &os, const Edge &edge);
std::ostream &operator<<(std::ostream &os, const UnboundedEdge &edge);
std::ostream &operator<<(std::ostream &os, const Path &path);
std::ostream &operator<<(std::ostream &os, const Value &value);
std::ostream &operator<<(std::ostream &os, const Value::Type type);
} // namespace memgraph::communication::bolt