fix edges bug and paths bug and add tests

This commit is contained in:
antoniofilipovic 2022-08-19 12:58:37 +02:00
parent 5de88ea24b
commit c55183bd97
7 changed files with 254 additions and 65 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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]

View File

@ -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"]))