diff --git a/include/_mgp.hpp b/include/_mgp.hpp index 17d4bc1e0..cc1daebf7 100644 --- a/include/_mgp.hpp +++ b/include/_mgp.hpp @@ -367,6 +367,10 @@ inline mgp_map_item *map_items_iterator_next(mgp_map_items_iterator *it) { inline mgp_vertex_id vertex_get_id(mgp_vertex *v) { return MgInvoke<mgp_vertex_id>(mgp_vertex_get_id, v); } +inline size_t vertex_get_in_degree(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_get_in_degree, v); } + +inline size_t vertex_get_out_degree(mgp_vertex *v) { return MgInvoke<size_t>(mgp_vertex_get_out_degree, v); } + inline mgp_vertex *vertex_copy(mgp_vertex *v, mgp_memory *memory) { return MgInvoke<mgp_vertex *>(mgp_vertex_copy, v, memory); } diff --git a/include/mg_procedure.h b/include/mg_procedure.h index 3ade1dfa6..4c771b4d2 100644 --- a/include/mg_procedure.h +++ b/include/mg_procedure.h @@ -647,6 +647,12 @@ struct mgp_vertex_id { /// Get the ID of given vertex. enum mgp_error mgp_vertex_get_id(struct mgp_vertex *v, struct mgp_vertex_id *result); +/// Get the in degree of given vertex. +enum mgp_error mgp_vertex_get_in_degree(struct mgp_vertex *v, size_t *result); + +/// Get the out degree of given vertex. +enum mgp_error mgp_vertex_get_out_degree(struct mgp_vertex *v, size_t *result); + /// Result is non-zero if the vertex can be modified. /// The mutability of the vertex is the same as the graph which it is part of. If a vertex is immutable, then edges /// cannot be created or deleted, properties and labels cannot be set or removed and all of the returned edges will be diff --git a/include/mgp.hpp b/include/mgp.hpp index 43436fd61..2b1f0318d 100644 --- a/include/mgp.hpp +++ b/include/mgp.hpp @@ -752,6 +752,12 @@ class Node { /// @brief returns the string representation const std::string ToString() const; + /// @brief returns the in degree of a node + inline size_t InDegree() const; + + /// @brief returns the out degree of a node + inline size_t OutDegree() const; + private: mgp_vertex *ptr_; }; @@ -2790,6 +2796,10 @@ inline const std::string Node::ToString() const { return "(id: " + std::to_string(Id().AsInt()) + labels + ", properties: {" + properties + "})"; } +inline size_t Node::InDegree() const { return mgp::vertex_get_in_degree(ptr_); } + +inline size_t Node::OutDegree() const { return mgp::vertex_get_out_degree(ptr_); } + // Relationship: inline Relationship::Relationship(mgp_edge *ptr) : ptr_(mgp::MemHandlerCallback(edge_copy, ptr)) {} diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp index cec29feed..aa40c6a2d 100644 --- a/src/query/db_accessor.hpp +++ b/src/query/db_accessor.hpp @@ -242,6 +242,10 @@ class SubgraphVertexAccessor final { storage::Gid Gid() const noexcept { return impl_.Gid(); } + storage::Result<size_t> InDegree(storage::View view) const { return impl_.InDegree(view); } + + storage::Result<size_t> OutDegree(storage::View view) const { return impl_.OutDegree(view); } + storage::Result<storage::PropertyValue> SetProperty(storage::PropertyId key, const storage::PropertyValue &value) { return impl_.SetProperty(key, value); } diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp index 4c845eec5..5e3a147e3 100644 --- a/src/query/procedure/mg_procedure_impl.cpp +++ b/src/query/procedure/mg_procedure_impl.cpp @@ -1605,6 +1605,48 @@ mgp_error mgp_vertex_get_id(mgp_vertex *v, mgp_vertex_id *result) { result); } +mgp_error mgp_vertex_get_in_degree(struct mgp_vertex *v, size_t *result) { + return WrapExceptions( + [v]() -> size_t { + auto maybe_in_degree = std::visit([v](const auto &impl) { return impl.InDegree(v->graph->view); }, v->impl); + if (maybe_in_degree.HasError()) { + switch (maybe_in_degree.GetError()) { + case memgraph::storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the degree of a deleted vertex!"}; + case memgraph::storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules shouldn't have access to nonexistent objects when getting vertex degree!"); + case memgraph::storage::Error::PROPERTIES_DISABLED: + case memgraph::storage::Error::VERTEX_HAS_EDGES: + case memgraph::storage::Error::SERIALIZATION_ERROR: + LOG_FATAL("Unexpected error when getting vertex degree."); + } + } + return *maybe_in_degree; + }, + result); +} + +mgp_error mgp_vertex_get_out_degree(struct mgp_vertex *v, size_t *result) { + return WrapExceptions( + [v]() -> size_t { + auto maybe_out_degree = std::visit([v](const auto &impl) { return impl.OutDegree(v->graph->view); }, v->impl); + if (maybe_out_degree.HasError()) { + switch (maybe_out_degree.GetError()) { + case memgraph::storage::Error::DELETED_OBJECT: + throw DeletedObjectException{"Cannot get the degree of a deleted vertex!"}; + case memgraph::storage::Error::NONEXISTENT_OBJECT: + LOG_FATAL("Query modules shouldn't have access to nonexistent objects when getting vertex degree!"); + case memgraph::storage::Error::PROPERTIES_DISABLED: + case memgraph::storage::Error::VERTEX_HAS_EDGES: + case memgraph::storage::Error::SERIALIZATION_ERROR: + LOG_FATAL("Unexpected error when getting vertex degree."); + } + } + return *maybe_out_degree; + }, + result); +} + mgp_error mgp_vertex_underlying_graph_is_mutable(mgp_vertex *v, int *result) { return mgp_graph_is_mutable(v->graph, result); } diff --git a/tests/unit/cpp_api.cpp b/tests/unit/cpp_api.cpp index 57411f723..0acf2d18c 100644 --- a/tests/unit/cpp_api.cpp +++ b/tests/unit/cpp_api.cpp @@ -689,3 +689,25 @@ TYPED_TEST(CppApiTestFixture, TestValueToString) { "(id: 2, :Label1:Label2, properties: {})-[type: Loves, id: 0, properties: {key: property}]->(id: 3, properties: " "{key: node_property, key2: node_property2})-[type: Loves2, id: 1, properties: {}]->(id: 4, properties: {})"); } + +TYPED_TEST(CppApiTestFixture, TestInAndOutDegrees) { + mgp_graph raw_graph = this->CreateGraph(memgraph::storage::View::NEW); + auto graph = mgp::Graph(&raw_graph); + auto node_1 = graph.CreateNode(); + auto node_2 = graph.CreateNode(); + auto node_3 = graph.CreateNode(); + auto relationship = graph.CreateRelationship(node_1, node_2, "Relationship1"); + auto relationship2 = graph.CreateRelationship(node_1, node_2, "Relationship2"); + auto relationship3 = graph.CreateRelationship(node_1, node_2, "Relationship3"); + auto relationship4 = graph.CreateRelationship(node_1, node_2, "Relationship4"); + auto relationship5 = graph.CreateRelationship(node_1, node_3, "Relationship5"); + auto relationship6 = graph.CreateRelationship(node_1, node_3, "Relationship6"); + + ASSERT_EQ(node_1.OutDegree(), 6); + ASSERT_EQ(node_2.InDegree(), 4); + ASSERT_EQ(node_3.InDegree(), 2); + + ASSERT_EQ(node_1.InDegree(), 0); + ASSERT_EQ(node_2.OutDegree(), 0); + ASSERT_EQ(node_3.OutDegree(), 0); +}