From c55183bd974d91fe938bc069183ee89db89c5146 Mon Sep 17 00:00:00 2001
From: antoniofilipovic <filipovicantonio1998@gmail.com>
Date: Fri, 19 Aug 2022 12:58:37 +0200
Subject: [PATCH] fix edges bug and paths bug and add tests

---
 src/query/db_accessor.cpp                     |   3 -
 src/query/db_accessor.hpp                     |   1 -
 src/query/procedure/mg_procedure_impl.cpp     |  74 +++++++----
 src/query/procedure/mg_procedure_impl.hpp     |  80 ++++++-----
 tests/e2e/write_procedures/CMakeLists.txt     |   1 +
 tests/e2e/write_procedures/procedures/read.py |  36 ++++-
 tests/e2e/write_procedures/read_subgraph.py   | 124 ++++++++++++++++++
 7 files changed, 254 insertions(+), 65 deletions(-)
 create mode 100644 tests/e2e/write_procedures/read_subgraph.py

diff --git a/src/query/db_accessor.cpp b/src/query/db_accessor.cpp
index 5268b3cd2..8f591306d 100644
--- a/src/query/db_accessor.cpp
+++ b/src/query/db_accessor.cpp
@@ -111,9 +111,6 @@ std::optional<VertexAccessor> SubgraphDbAccessor::FindVertex(storage::Gid gid, s
 query::Graph *SubgraphDbAccessor::getGraph() { return graph_; }
 
 VertexAccessor SubgraphVertexAccessor::GetVertexAccessor() { return impl_; }
-static SubgraphVertexAccessor MakeSubgraphVertexAccessor(query::VertexAccessor impl, query::Graph *graph_) {
-  return SubgraphVertexAccessor(impl, graph_);
-}
 
 auto SubgraphVertexAccessor::OutEdges(storage::View view) -> decltype(impl_.OutEdges(view)) const {
   // todo antoniofilipovic add filtering here
diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp
index abb8619e7..d134d7e90 100644
--- a/src/query/db_accessor.hpp
+++ b/src/query/db_accessor.hpp
@@ -193,7 +193,6 @@ class SubgraphVertexAccessor final {
   query::VertexAccessor impl_;
   query::Graph *graph_;
 
-  static SubgraphVertexAccessor MakeSubgraphVertexAccessor(query::VertexAccessor impl, query::Graph *graph_);
   explicit SubgraphVertexAccessor(query::VertexAccessor impl, query::Graph *graph_) : impl_(impl), graph_(graph_) {}
 
   bool operator==(const SubgraphVertexAccessor &v) const noexcept {
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index 6a0038760..2787c3c1f 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -476,8 +476,18 @@ mgp_value::mgp_value(const memgraph::query::TypedValue &tv, mgp_graph *graph, me
     }
     case MGP_VALUE_TYPE_EDGE: {
       memgraph::utils::Allocator<mgp_edge> allocator(m);
-      // todo(antoniofilipovic) fix
-      // edge_v = allocator.new_object<mgp_edge>(tv.ValueEdge(), graph);
+
+      edge_v = std::visit(
+          memgraph::utils::Overloaded{
+              [&tv, graph, &allocator](memgraph::query::DbAccessor *db_impl) {
+                return allocator.new_object<mgp_edge>(tv.ValueEdge(), graph);
+              },
+              [&tv, graph, &allocator](memgraph::query::SubgraphDbAccessor *db_impl) {
+                return allocator.new_object<mgp_edge>(
+                    tv.ValueEdge(), memgraph::query::SubgraphVertexAccessor(tv.ValueEdge().From(), db_impl->getGraph()),
+                    memgraph::query::SubgraphVertexAccessor(tv.ValueEdge().To(), db_impl->getGraph()), graph);
+              }},
+          graph->impl);
       break;
     }
     case MGP_VALUE_TYPE_PATH: {
@@ -487,13 +497,28 @@ mgp_value::mgp_value(const memgraph::query::TypedValue &tv, mgp_graph *graph, me
       // released.
       mgp_path tmp_path(m);
       tmp_path.vertices.reserve(tv.ValuePath().vertices().size());
+      // todo(antoniofilipovic) check if this works correctly -> test valuepath created as parameter
       for (const auto &v : tv.ValuePath().vertices()) {
-        tmp_path.vertices.emplace_back(v, graph);
+        // tmp_path.vertices.emplace_back(v, graph);
+        std::visit(
+            memgraph::utils::Overloaded{
+                [&v, graph, &tmp_path](memgraph::query::DbAccessor *impl) { tmp_path.vertices.emplace_back(v, graph); },
+                [&v, graph, &tmp_path](memgraph::query::SubgraphDbAccessor *impl) {
+                  tmp_path.vertices.emplace_back(memgraph::query::SubgraphVertexAccessor(v, impl->getGraph()), graph);
+                }},
+            graph->impl);
       }
       tmp_path.edges.reserve(tv.ValuePath().edges().size());
       for (const auto &e : tv.ValuePath().edges()) {
-        // todo(antoniofilipovic) fix this
-        // tmp_path.edges.emplace_back(e, graph);
+        std::visit(
+            memgraph::utils::Overloaded{
+                [&e, graph, &tmp_path](memgraph::query::DbAccessor *db_impl) { tmp_path.edges.emplace_back(e, graph); },
+                [&e, graph, &tmp_path](memgraph::query::SubgraphDbAccessor *db_impl) {
+                  tmp_path.edges.emplace_back(e, memgraph::query::SubgraphVertexAccessor(e.From(), db_impl->getGraph()),
+                                              memgraph::query::SubgraphVertexAccessor(e.To(), db_impl->getGraph()),
+                                              graph);
+                }},
+            graph->impl);
       }
       memgraph::utils::Allocator<mgp_path> allocator(m);
       path_v = allocator.new_object<mgp_path>(std::move(tmp_path));
@@ -818,19 +843,15 @@ mgp_value::mgp_value(mgp_value &&other, memgraph::utils::MemoryResource *m) : ty
 mgp_value::~mgp_value() noexcept { DeleteValueMember(this); }
 
 mgp_edge *mgp_edge::Copy(const mgp_edge &edge, mgp_memory &memory) {
-  // return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.from.graph);
-
   return std::visit(
       memgraph::utils::Overloaded{
           [&](memgraph::query::DbAccessor *db_impl) {
             return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.from.graph);
-            // return NewRawMgpObject<mgp_edge>(&memory, edge.impl, edge.impl.From(), edge.impl.To(), edge.to.graph,
-            // memory.impl);
           },
           [&](memgraph::query::SubgraphDbAccessor *db_impl) {
-            const auto &from_v = memgraph::query::SubgraphVertexAccessor(edge.impl.From(), db_impl->getGraph());
-            const auto &to_v = memgraph::query::SubgraphVertexAccessor(edge.impl.To(), db_impl->getGraph());
-            return NewRawMgpObject<mgp_edge>(&memory, edge.impl, from_v, to_v, edge.to.graph, memory.impl);
+            return NewRawMgpObject<mgp_edge>(
+                &memory, edge.impl, memgraph::query::SubgraphVertexAccessor(edge.impl.From(), db_impl->getGraph()),
+                memgraph::query::SubgraphVertexAccessor(edge.impl.To(), db_impl->getGraph()), edge.to.graph);
           }},
       edge.to.graph->impl);
 }
@@ -2443,23 +2464,18 @@ mgp_error mgp_graph_create_edge(mgp_graph *graph, mgp_vertex *from, mgp_vertex *
         if (ctx->trigger_context_collector) {
           ctx->trigger_context_collector->RegisterCreatedObject(*edge);
         }
-        // check what does this method call
-        // todo(antoniofilipovic)
-        // return NewRawMgpObject<mgp_edge>(memory, edge.GetValue(), from->graph);
-        return std::visit(memgraph::utils::Overloaded{
-                              [memory, edge, from](memgraph::query::DbAccessor *db_impl) {
-                                return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), from->graph);
-                              },
-                              [memory, edge, from](memgraph::query::SubgraphDbAccessor *db_impl) {
-                                const auto &v_from =
-                                    memgraph::query::SubgraphVertexAccessor::MakeSubgraphVertexAccessor(
-                                        edge.GetValue().From(), db_impl->getGraph());
-                                const auto &v_to = memgraph::query::SubgraphVertexAccessor::MakeSubgraphVertexAccessor(
-                                    edge.GetValue().To(), db_impl->getGraph());
-                                return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), v_from, v_to,
-                                                                 from->graph, memory->impl);
-                              }},
-                          graph->impl);
+        return std::visit(
+            memgraph::utils::Overloaded{
+                [memory, edge, from](memgraph::query::DbAccessor *db_impl) {
+                  return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), from->graph);
+                },
+                [memory, edge, from](memgraph::query::SubgraphDbAccessor *db_impl) {
+                  const auto &v_from =
+                      memgraph::query::SubgraphVertexAccessor(edge.GetValue().From(), db_impl->getGraph());
+                  const auto &v_to = memgraph::query::SubgraphVertexAccessor(edge.GetValue().To(), db_impl->getGraph());
+                  return NewRawMgpObject<mgp_edge>(memory->impl, edge.GetValue(), v_from, v_to, from->graph);
+                }},
+            graph->impl);
       },
       result);
 }
diff --git a/src/query/procedure/mg_procedure_impl.hpp b/src/query/procedure/mg_procedure_impl.hpp
index f26ed98b4..7dea54605 100644
--- a/src/query/procedure/mg_procedure_impl.hpp
+++ b/src/query/procedure/mg_procedure_impl.hpp
@@ -448,10 +448,30 @@ struct mgp_vertex {
       : memory(memory), impl(v), graph(graph) {}
 
   mgp_vertex(const mgp_vertex &other, memgraph::utils::MemoryResource *memory) noexcept
-      : memory(memory), impl(other.impl), graph(other.graph) {}
+      : memory(memory), impl(other.impl), graph(other.graph) {
+    std::visit(memgraph::utils::Overloaded{
+                   [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
+                   [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
+               other.impl);
+
+    std::visit(memgraph::utils::Overloaded{
+                   [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
+                   [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
+               this->impl);
+  }
 
   mgp_vertex(mgp_vertex &&other, memgraph::utils::MemoryResource *memory) noexcept
-      : memory(memory), impl(other.impl), graph(other.graph) {}
+      : memory(memory), impl(other.impl), graph(other.graph) {
+    std::visit(memgraph::utils::Overloaded{
+                   [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
+                   [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
+               other.impl);
+
+    std::visit(memgraph::utils::Overloaded{
+                   [](memgraph::query::VertexAccessor) { std::cout << "VertexAccessor" << std::endl; },
+                   [](memgraph::query::SubgraphVertexAccessor) { std::cout << "SubgraphVertexAccessor" << std::endl; }},
+               this->impl);
+  }
 
   mgp_vertex(mgp_vertex &&other) noexcept : memory(other.memory), impl(other.impl), graph(other.graph) {}
 
@@ -497,6 +517,34 @@ struct mgp_vertex {
   mgp_graph *graph;
 };
 
+struct mgp_graph {
+  std::variant<memgraph::query::DbAccessor *, memgraph::query::SubgraphDbAccessor *> impl;
+  memgraph::storage::View view;
+  // TODO: Merge `mgp_graph` and `mgp_memory` into a single `mgp_context`. The
+  // `ctx` field is out of place here.
+  memgraph::query::ExecutionContext *ctx;
+
+  // memgraph:::query::Graph *subraph;
+
+  static mgp_graph WritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view,
+                                 memgraph::query::ExecutionContext &ctx) {
+    return mgp_graph{&acc, view, &ctx};
+  }
+
+  static mgp_graph NonWritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view) {
+    return mgp_graph{&acc, view, nullptr};
+  }
+
+  static mgp_graph WritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view,
+                                 memgraph::query::ExecutionContext &ctx) {
+    return mgp_graph{&acc, view, &ctx};
+  }
+
+  static mgp_graph NonWritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view) {
+    return mgp_graph{&acc, view, nullptr};
+  }
+};
+
 struct mgp_edge {
   /// Allocator type so that STL containers are aware that we need one.
   /// We don't actually need this, but it simplifies the C API, because we store
@@ -609,34 +657,6 @@ struct mgp_func_result {
   std::optional<memgraph::utils::pmr::string> error_msg;
 };
 
-struct mgp_graph {
-  std::variant<memgraph::query::DbAccessor *, memgraph::query::SubgraphDbAccessor *> impl;
-  memgraph::storage::View view;
-  // TODO: Merge `mgp_graph` and `mgp_memory` into a single `mgp_context`. The
-  // `ctx` field is out of place here.
-  memgraph::query::ExecutionContext *ctx;
-
-  // memgraph:::query::Graph *subraph;
-
-  static mgp_graph WritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view,
-                                 memgraph::query::ExecutionContext &ctx) {
-    return mgp_graph{&acc, view, &ctx};
-  }
-
-  static mgp_graph NonWritableGraph(memgraph::query::DbAccessor &acc, memgraph::storage::View view) {
-    return mgp_graph{&acc, view, nullptr};
-  }
-
-  static mgp_graph WritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view,
-                                 memgraph::query::ExecutionContext &ctx) {
-    return mgp_graph{&acc, view, &ctx};
-  }
-
-  static mgp_graph NonWritableGraph(memgraph::query::SubgraphDbAccessor &acc, memgraph::storage::View view) {
-    return mgp_graph{&acc, view, nullptr};
-  }
-};
-
 // Prevents user to use ExecutionContext in writable callables
 struct mgp_func_context {
   memgraph::query::DbAccessor *impl;
diff --git a/tests/e2e/write_procedures/CMakeLists.txt b/tests/e2e/write_procedures/CMakeLists.txt
index 72b640271..27a9a73e2 100644
--- a/tests/e2e/write_procedures/CMakeLists.txt
+++ b/tests/e2e/write_procedures/CMakeLists.txt
@@ -5,5 +5,6 @@ endfunction()
 copy_write_procedures_e2e_python_files(common.py)
 copy_write_procedures_e2e_python_files(conftest.py)
 copy_write_procedures_e2e_python_files(simple_write.py)
+copy_write_procedures_e2e_python_files(read_subgraph.py)
 
 add_subdirectory(procedures)
diff --git a/tests/e2e/write_procedures/procedures/read.py b/tests/e2e/write_procedures/procedures/read.py
index 3f791a37a..5f503721d 100644
--- a/tests/e2e/write_procedures/procedures/read.py
+++ b/tests/e2e/write_procedures/procedures/read.py
@@ -13,11 +13,43 @@ import mgp
 
 
 @mgp.read_proc
-def underlying_graph_is_mutable(ctx: mgp.ProcCtx,
-                                object: mgp.Any) -> mgp.Record(mutable=bool):
+def underlying_graph_is_mutable(ctx: mgp.ProcCtx, object: mgp.Any) -> mgp.Record(mutable=bool):
     return mgp.Record(mutable=object.underlying_graph_is_mutable())
 
 
 @mgp.read_proc
 def graph_is_mutable(ctx: mgp.ProcCtx) -> mgp.Record(mutable=bool):
     return mgp.Record(mutable=ctx.graph.is_mutable())
+
+
+@mgp.read_proc
+def subgraph_get_vertices(ctx: mgp.ProcCtx) -> mgp.Record(node=mgp.Vertex):
+    return [mgp.Record(node=vertex) for vertex in ctx.graph.vertices]
+
+
+@mgp.read_proc
+def subgraph_get_out_edges(ctx: mgp.ProcCtx, vertex: mgp.Vertex) -> mgp.Record(edge=mgp.Edge):
+    return [mgp.Record(edge=edge) for edge in vertex.out_edges]
+
+
+@mgp.read_proc
+def subgraph_get_in_edges(ctx: mgp.ProcCtx, vertex: mgp.Vertex) -> mgp.Record(edge=mgp.Edge):
+    return [mgp.Record(edge=edge) for edge in vertex.in_edges]
+
+
+@mgp.read_proc
+def subgraph_get_2_hop_edges(ctx: mgp.ProcCtx, vertex: mgp.Vertex) -> mgp.Record(edge=mgp.Edge):
+    out_edges = vertex.out_edges
+    records = []
+    for edge in out_edges:
+        vertex = edge.to_vertex
+        properties = vertex.properties
+        print(properties)
+        records.extend([mgp.Record(edge=edge) for edge in edge.to_vertex.out_edges])
+    return records
+
+
+@mgp.read_proc
+def subgraph_get_out_edges_vertex_id(ctx: mgp.ProcCtx, vertex: mgp.Vertex) -> mgp.Record(edge=mgp.Edge):
+    vertex = ctx.graph.get_vertex_by_id(vertex.id)
+    return [mgp.Record(edge=edge) for edge in vertex.out_edges]
diff --git a/tests/e2e/write_procedures/read_subgraph.py b/tests/e2e/write_procedures/read_subgraph.py
new file mode 100644
index 000000000..cd1f22939
--- /dev/null
+++ b/tests/e2e/write_procedures/read_subgraph.py
@@ -0,0 +1,124 @@
+# Copyright 2022 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.
+
+import typing
+import mgclient
+import sys
+import pytest
+from common import execute_and_fetch_all, has_n_result_row
+
+
+def create_subgraph(cursor):
+    execute_and_fetch_all(cursor, "CREATE (n:Person {id: 1});")
+    execute_and_fetch_all(cursor, "CREATE (n:Person {id: 2});")
+    execute_and_fetch_all(cursor, "CREATE (n:Person {id: 3});")
+    execute_and_fetch_all(cursor, "CREATE (n:Person {id: 4});")
+    execute_and_fetch_all(cursor, "CREATE (n:Team {id: 5});")
+    execute_and_fetch_all(cursor, "CREATE (n:Team {id: 6});")
+    execute_and_fetch_all(cursor, "MATCH (p:Person {id: 1}) MATCH (t:Team {id:5}) CREATE (p)-[:SUPPORTS]->(t);")
+    execute_and_fetch_all(cursor, "MATCH (p:Person {id: 1}) MATCH (t:Team {id:6}) CREATE (p)-[:SUPPORTS]->(t);")
+    execute_and_fetch_all(cursor, "MATCH (p:Person {id: 2}) MATCH (t:Team {id:6}) CREATE (p)-[:SUPPORTS]->(t);")
+    execute_and_fetch_all(cursor, "MATCH (p1:Person {id: 1}) MATCH (p2:Person {id:2}) CREATE (p1)-[:KNOWS]->(p2);")
+    execute_and_fetch_all(cursor, "MATCH (t1:Team {id: 5}) MATCH (t2:Team {id:6}) CREATE (t1)-[:IS_RIVAL_TO]->(t2);")
+    execute_and_fetch_all(cursor, "MATCH (p1:Person {id: 3}) MATCH (p2:Person {id:4}) CREATE (p1)-[:KNOWS]->(p2);")
+
+
+def test_get_vertices(connection):
+    cursor = connection.cursor()
+    execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
+    create_subgraph(cursor)
+
+    assert has_n_result_row(cursor, "MATCH (n) RETURN n", 6)
+    results = execute_and_fetch_all(
+        cursor,
+        f"MATCH p=(n:Person)-[:SUPPORTS]->(m:Team) WITH project(p) AS graph CALL read.subgraph_get_vertices(graph) YIELD node RETURN node;",
+    )
+    assert len(results) == 4
+
+    execute_and_fetch_all(
+        cursor,
+        f"MATCH (n) DETACH DELETE n;",
+    )
+
+
+def test_get_out_edges(connection):
+    cursor = connection.cursor()
+    execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
+    create_subgraph(cursor)
+
+    assert has_n_result_row(cursor, "MATCH (n) RETURN n", 6)
+    results = execute_and_fetch_all(
+        cursor,
+        f"MATCH p=(n:Person)-[:SUPPORTS]->(m:Team) WITH project(p) AS graph MATCH (n1:Person {{id:1}}) CALL read.subgraph_get_out_edges(graph, n1) YIELD edge RETURN edge;",
+    )
+    assert len(results) == 2
+    execute_and_fetch_all(
+        cursor,
+        f"MATCH (n) DETACH DELETE n;",
+    )
+
+
+def test_get_in_edges(connection):
+    cursor = connection.cursor()
+    execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
+    create_subgraph(cursor)
+
+    assert has_n_result_row(cursor, "MATCH (n) RETURN n", 6)
+    results = execute_and_fetch_all(
+        cursor,
+        f"MATCH p=(n:Person)-[:SUPPORTS]->(m:Team) WITH project(p) AS graph MATCH (t1:Team {{id:6}}) CALL read.subgraph_get_in_edges(graph, t1) YIELD edge RETURN edge;",
+    )
+
+    assert len(results) == 2
+
+    execute_and_fetch_all(
+        cursor,
+        f"MATCH (n) DETACH DELETE n;",
+    )
+
+
+def test_get_2_hop_edges(connection):
+    cursor = connection.cursor()
+    execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
+    create_subgraph(cursor)
+
+    assert has_n_result_row(cursor, "MATCH (n) RETURN n", 6)
+    results = execute_and_fetch_all(
+        cursor,
+        f"MATCH p=(n:Person)-[:SUPPORTS]->(m:Team) WITH project(p) AS graph MATCH (n1:Person {{id:1}}) CALL read.subgraph_get_2_hop_edges(graph, n1) YIELD edge RETURN edge;",
+    )
+    assert len(results) == 0
+    execute_and_fetch_all(
+        cursor,
+        f"MATCH (n) DETACH DELETE n;",
+    )
+
+
+def test_get_out_edges_vertex_id(connection):
+    cursor = connection.cursor()
+    execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n;")
+    create_subgraph(cursor=cursor)
+
+    assert has_n_result_row(cursor, "MATCH (n) RETURN n", 6)
+    results = execute_and_fetch_all(
+        cursor,
+        f"MATCH p=(n:Person)-[:SUPPORTS]->(m:Team) WITH project(p) AS graph MATCH (n1:Person {{id:1}}) CALL read.subgraph_get_out_edges_vertex_id(graph, n1) YIELD edge RETURN edge;",
+    )
+    assert len(results) == 2
+
+    execute_and_fetch_all(
+        cursor,
+        f"MATCH (n) DETACH DELETE n;",
+    )
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))