From 04ceb8d4b17f26c3e3d29d25f711a1760bfb7721 Mon Sep 17 00:00:00 2001 From: Matej Ferencevic <matej.ferencevic@memgraph.io> Date: Sat, 5 Sep 2020 13:05:30 +0200 Subject: [PATCH] Implement `valueType` openCypher function Reviewers: buda Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D2818 --- .../interpret/awesome_memgraph_functions.cpp | 40 ++++++++++++++++++- tests/unit/query_expression_evaluator.cpp | 32 +++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index b84c78e61..f4d525565 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -74,6 +74,7 @@ struct Integer {}; struct PositiveInteger {}; struct NonZeroInteger {}; struct NonNegativeInteger {}; +struct Double {}; struct Number {}; struct List {}; struct String {}; @@ -96,6 +97,8 @@ bool ArgIsType(const TypedValue &arg) { return arg.IsInt() && arg.ValueInt() != 0; } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { return arg.IsInt() && arg.ValueInt() >= 0; + } else if constexpr (std::is_same_v<ArgType, Double>) { + return arg.IsDouble(); } else if constexpr (std::is_same_v<ArgType, Number>) { return arg.IsNumeric(); } else if constexpr (std::is_same_v<ArgType, List>) { @@ -120,6 +123,8 @@ bool ArgIsType(const TypedValue &arg) { template <class ArgType> constexpr const char *ArgTypeName() { + // The type names returned should be standardized openCypher type names. + // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf if constexpr (std::is_same_v<ArgType, Null>) { return "null"; } else if constexpr (std::is_same_v<ArgType, Bool>) { @@ -132,6 +137,8 @@ constexpr const char *ArgTypeName() { return "non-zero integer"; } else if constexpr (std::is_same_v<ArgType, NonNegativeInteger>) { return "non-negative integer"; + } else if constexpr (std::is_same_v<ArgType, Double>) { + return "float"; } else if constexpr (std::is_same_v<ArgType, Number>) { return "number"; } else if constexpr (std::is_same_v<ArgType, List>) { @@ -143,7 +150,7 @@ constexpr const char *ArgTypeName() { } else if constexpr (std::is_same_v<ArgType, Vertex>) { return "node"; } else if constexpr (std::is_same_v<ArgType, Edge>) { - return "edge"; + return "relationship"; } else if constexpr (std::is_same_v<ArgType, Path>) { return "path"; } else if constexpr (std::is_same_v<ArgType, void>) { @@ -548,6 +555,36 @@ TypedValue Type(const TypedValue *args, int64_t nargs, ctx.memory); } +TypedValue ValueType(const TypedValue *args, int64_t nargs, + const FunctionContext &ctx) { + FType<Or<Null, Bool, Integer, Double, String, List, Map, Vertex, Edge, Path>>( + "type", args, nargs); + // The type names returned should be standardized openCypher type names. + // https://github.com/opencypher/openCypher/blob/master/docs/openCypher9.pdf + switch (args[0].type()) { + case TypedValue::Type::Null: + return TypedValue("NULL", ctx.memory); + case TypedValue::Type::Bool: + return TypedValue("BOOLEAN", ctx.memory); + case TypedValue::Type::Int: + return TypedValue("INTEGER", ctx.memory); + case TypedValue::Type::Double: + return TypedValue("FLOAT", ctx.memory); + case TypedValue::Type::String: + return TypedValue("STRING", ctx.memory); + case TypedValue::Type::List: + return TypedValue("LIST", ctx.memory); + case TypedValue::Type::Map: + return TypedValue("MAP", ctx.memory); + case TypedValue::Type::Vertex: + return TypedValue("NODE", ctx.memory); + case TypedValue::Type::Edge: + return TypedValue("RELATIONSHIP", ctx.memory); + case TypedValue::Type::Path: + return TypedValue("PATH", ctx.memory); + } +} + // TODO: How is Keys different from Properties function? TypedValue Keys(const TypedValue *args, int64_t nargs, const FunctionContext &ctx) { @@ -1088,6 +1125,7 @@ NameToFunction(const std::string &function_name) { if (function_name == "TOFLOAT") return ToFloat; if (function_name == "TOINTEGER") return ToInteger; if (function_name == "TYPE") return Type; + if (function_name == "VALUETYPE") return ValueType; // List functions if (function_name == "KEYS") return Keys; diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp index 29e291412..bb8d1739b 100644 --- a/tests/unit/query_expression_evaluator.cpp +++ b/tests/unit/query_expression_evaluator.cpp @@ -1418,6 +1418,38 @@ TEST_F(FunctionTest, Type) { ASSERT_THROW(EvaluateFunction("TYPE", 2), QueryRuntimeException); } +TEST_F(FunctionTest, ValueType) { + ASSERT_THROW(EvaluateFunction("VALUETYPE"), QueryRuntimeException); + ASSERT_THROW(EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), + QueryRuntimeException); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), + "BOOLEAN"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), + "INTEGER"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), + "FLOAT"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), + "STRING"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(std::vector<TypedValue>{ + TypedValue(1), TypedValue(2)})) + .ValueString(), + "LIST"); + ASSERT_EQ(EvaluateFunction("VALUETYPE", + TypedValue(std::map<std::string, TypedValue>{ + {"test", TypedValue(1)}})) + .ValueString(), + "MAP"); + auto v1 = dba.InsertVertex(); + auto v2 = dba.InsertVertex(); + ASSERT_EQ(EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE"); + auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1")); + ASSERT_TRUE(e.HasValue()); + ASSERT_EQ(EvaluateFunction("VALUETYPE", *e).ValueString(), "RELATIONSHIP"); + Path p(v1, *e, v2); + ASSERT_EQ(EvaluateFunction("VALUETYPE", p).ValueString(), "PATH"); +} + TEST_F(FunctionTest, Labels) { ASSERT_THROW(EvaluateFunction("LABELS"), QueryRuntimeException); ASSERT_TRUE(EvaluateFunction("LABELS", TypedValue()).IsNull());