diff --git a/src/communication/bolt/v1/decoder/decoded_value.cpp b/src/communication/bolt/v1/decoder/decoded_value.cpp
index 170e14e4d..12a0e298e 100644
--- a/src/communication/bolt/v1/decoder/decoded_value.cpp
+++ b/src/communication/bolt/v1/decoder/decoded_value.cpp
@@ -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");
 }
diff --git a/src/communication/bolt/v1/decoder/decoded_value.hpp b/src/communication/bolt/v1/decoder/decoded_value.hpp
index b456286ba..3edef6b19 100644
--- a/src/communication/bolt/v1/decoder/decoded_value.hpp
+++ b/src/communication/bolt/v1/decoder/decoded_value.hpp
@@ -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);
 }
diff --git a/src/communication/bolt/v1/decoder/decoder.hpp b/src/communication/bolt/v1/decoder/decoder.hpp
index db4dca3e9..6170c95a2 100644
--- a/src/communication/bolt/v1/decoder/decoder.hpp
+++ b/src/communication/bolt/v1/decoder/decoder.hpp
@@ -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;
+  }
 };
 }
diff --git a/src/communication/bolt/v1/encoder/base_encoder.hpp b/src/communication/bolt/v1/encoder/base_encoder.hpp
index dee9663fd..081163083 100644
--- a/src/communication/bolt/v1/encoder/base_encoder.hpp
+++ b/src/communication/bolt/v1/encoder/base_encoder.hpp
@@ -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;
     }
   }
diff --git a/src/query/path.hpp b/src/query/path.hpp
index ca925e310..48214eaf6 100644
--- a/src/query/path.hpp
+++ b/src/query/path.hpp
@@ -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_; }
diff --git a/src/storage/edge_accessor.cpp b/src/storage/edge_accessor.cpp
index 227b06de2..86cf33a49 100644
--- a/src/storage/edge_accessor.cpp
+++ b/src/storage/edge_accessor.cpp
@@ -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==(&current().from_);
+}
+
 VertexAccessor EdgeAccessor::to() const {
   return VertexAccessor(current().to_, db_accessor());
 }
+
+bool EdgeAccessor::to_is(const VertexAccessor &v) const {
+  return v.operator==(&current().to_);
+}
+
 bool EdgeAccessor::is_cycle() const {
   return &current().to_ == &current().from_;
 }
diff --git a/src/storage/edge_accessor.hpp b/src/storage/edge_accessor.hpp
index 948c52633..3580cf412 100644
--- a/src/storage/edge_accessor.hpp
+++ b/src/storage/edge_accessor.hpp
@@ -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;
diff --git a/src/storage/record_accessor.hpp b/src/storage/record_accessor.hpp
index 8d1105dbd..15857919c 100644
--- a/src/storage/record_accessor.hpp
+++ b/src/storage/record_accessor.hpp
@@ -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.
    *