From 47c1cd6e3d0dff4e63fc2136baf3649bc105690c Mon Sep 17 00:00:00 2001 From: florijan <florijan@memgraph.io> Date: Wed, 9 Aug 2017 11:48:36 +0200 Subject: [PATCH] Degree(Vertex) function added Summary: - added only one function for getting the total (in + out) vertex degree, it's required for the Ravelin use-case - specific `degree_in` and `degree_out` functions can be added as necessary - also fixed random_graph_generator bug (needed it for testing) Reviewers: buda, mislav.bradac Reviewed By: buda, mislav.bradac Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D652 --- CHANGELOG.md | 1 + docs/user_technical/open-cypher.md | 1 + .../interpret/awesome_memgraph_functions.cpp | 17 +++++++++++++++++ src/utils/random_graph_generator.hpp | 1 + .../memgraph_V1/features/functions.feature | 11 +++++++++++ tests/unit/query_expression_evaluator.cpp | 18 ++++++++++++++++++ 6 files changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd9139834..efed40cc1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ * Support for `all` function in openCypher. * User specified transaction execution timeout. * Support for query parameters (except for parameters in place of property maps). +* `degree` function added. ## v0.6.0 diff --git a/docs/user_technical/open-cypher.md b/docs/user_technical/open-cypher.md index 705064b7a..13c7db593 100644 --- a/docs/user_technical/open-cypher.md +++ b/docs/user_technical/open-cypher.md @@ -404,6 +404,7 @@ functions. `coalesce` | Returns the first non null argument. `startNode` | Returns the starting node of an edge. `endNode` | Returns the destination node of an edge. + `degree` | Returns the number of edges (both incoming and outgoing) of a node. `head` | Returns the first element of a list. `last` | Returns the last element of a list. `properties` | Returns the properties of a node or an edge. diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index 49ea09c5d..6992afb24 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -153,6 +153,22 @@ TypedValue StartNode(const std::vector<TypedValue> &args, GraphDbAccessor &) { } } +TypedValue Degree(const std::vector<TypedValue> &args, GraphDbAccessor &) { + if (args.size() != 1U) { + throw QueryRuntimeException("degree requires one argument"); + } + switch (args[0].type()) { + case TypedValue::Type::Null: + return TypedValue::Null; + case TypedValue::Type::Vertex: { + auto &vertex = args[0].Value<VertexAccessor>(); + return static_cast<int64_t>(vertex.out_degree() + vertex.in_degree()); + } + default: + throw QueryRuntimeException("degree called with incompatible type"); + } +} + TypedValue ToBoolean(const std::vector<TypedValue> &args, GraphDbAccessor &) { if (args.size() != 1U) { throw QueryRuntimeException("toBoolean requires one argument"); @@ -492,6 +508,7 @@ NameToFunction(const std::string &function_name) { if (function_name == "PROPERTIES") return Properties; if (function_name == "SIZE") return Size; if (function_name == "STARTNODE") return StartNode; + if (function_name == "DEGREE") return Degree; if (function_name == "TOBOOLEAN") return ToBoolean; if (function_name == "TOFLOAT") return ToFloat; if (function_name == "TOINTEGER") return ToInteger; diff --git a/src/utils/random_graph_generator.hpp b/src/utils/random_graph_generator.hpp index 7042edead..9e8fe83a9 100644 --- a/src/utils/random_graph_generator.hpp +++ b/src/utils/random_graph_generator.hpp @@ -151,6 +151,7 @@ class RandomGraphGenerator { auto property = dba->property(prop_name); for (VertexAccessor va : dba->vertices(false)) if (predicate(va)) va.PropsSet(property, value_generator()); + dba->commit(); } private: diff --git a/tests/qa/tck_engine/tests/memgraph_V1/features/functions.feature b/tests/qa/tck_engine/tests/memgraph_V1/features/functions.feature index a71cb7829..5399c4ce6 100644 --- a/tests/qa/tck_engine/tests/memgraph_V1/features/functions.feature +++ b/tests/qa/tck_engine/tests/memgraph_V1/features/functions.feature @@ -487,6 +487,17 @@ Feature: Functions | n | a | b | | null | 3 | 2 | + Scenario: Degree test: + When executing query: + """ + CREATE (a)-[:Type]->(b)<-[:Type]-(c) + RETURN DEGREE(a) AS da, DEGREE(b) AS db, DEGREE(null) AS dn + """ + Then the result should be: + | da | db | dn | + | 1 | 2 | null | + + Scenario: Last test: When executing query: """ diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index 5a137d902..801bebff0 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -768,6 +768,24 @@ TEST(ExpressionEvaluator, FunctionStartNode) { ASSERT_THROW(EvaluateFunction("STARTNODE", {2}), QueryRuntimeException); } +TEST(ExpressionEvaluator, FunctionDegree) { + ASSERT_THROW(EvaluateFunction("DEGREE", {}), QueryRuntimeException); + ASSERT_EQ(EvaluateFunction("DEGREE", {TypedValue::Null}).type(), + TypedValue::Type::Null); + Dbms dbms; + auto dba = dbms.active(); + auto v1 = dba->insert_vertex(); + auto v2 = dba->insert_vertex(); + auto v3 = dba->insert_vertex(); + auto e12 = dba->insert_edge(v1, v2, dba->edge_type("t")); + dba->insert_edge(v3, v2, dba->edge_type("t")); + ASSERT_EQ(EvaluateFunction("DEGREE", {v1}).Value<int64_t>(), 1); + ASSERT_EQ(EvaluateFunction("DEGREE", {v2}).Value<int64_t>(), 2); + ASSERT_EQ(EvaluateFunction("DEGREE", {v3}).Value<int64_t>(), 1); + ASSERT_THROW(EvaluateFunction("DEGREE", {2}), QueryRuntimeException); + ASSERT_THROW(EvaluateFunction("DEGREE", {e12}), QueryRuntimeException); +} + TEST(ExpressionEvaluator, FunctionToBoolean) { ASSERT_THROW(EvaluateFunction("TOBOOLEAN", {}), QueryRuntimeException); ASSERT_EQ(EvaluateFunction("TOBOOLEAN", {TypedValue::Null}).type(),