From 9243bba7a0430508b01e3c53d311642a030c1a6b Mon Sep 17 00:00:00 2001 From: Sophie-Xie <84560950+Sophie-Xie@users.noreply.github.com> Date: Mon, 18 Oct 2021 10:23:46 +0800 Subject: [PATCH] Cherry pick v2.6.0 1015 (#3107) * Expression test modify (#3041) * modify expression test * modify expression test 2 * complete coding * fix bug * modify expression test case * clang-format * fix bug:initialization-order-fiasco * add some obj * add test_path_function Co-authored-by: cpw <13495049+CPWstatic@users.noreply.github.com> * add hash & hash (#3051) * fix dangling edge in path (#3008) * fix dangling edge * add test case * fix ci error * Fix graph/meta/storage version in show hosts (#3054) * Fix graph version bug * Fix storage version * Print cpack config * Decrease ubuntu compile parallelism * fix bug #3048 (#3069) Co-authored-by: haifei.zhao <32253291+zhaohaifei@users.noreply.github.com> Co-authored-by: cpw <13495049+CPWstatic@users.noreply.github.com> Co-authored-by: jimingquan Co-authored-by: Yee <2520865+yixinglu@users.noreply.github.com> --- .github/workflows/pull_request.yml | 2 +- cmake/CPackage.cmake | 9 +- src/clients/meta/MetaClient.cpp | 4 +- src/common/datatypes/Map.cpp | 11 + src/common/datatypes/Map.h | 8 + src/common/datatypes/Set.cpp | 11 + src/common/datatypes/Set.h | 9 +- src/common/datatypes/Value.cpp | 4 +- src/common/expression/test/CMakeLists.txt | 88 ++++ .../test/FunctionCallExpressionTest.cpp | 8 +- .../expression/test/LogicalExpressionTest.cpp | 473 ++++++++++++++++-- .../test/RelationalExpressionTest.cpp | 81 ++- src/common/expression/test/TestBase.h | 259 +++------- .../executor/query/DataCollectExecutor.cpp | 19 +- src/graph/service/GraphService.cpp | 7 +- .../processors/parts/ListHostsProcessor.cpp | 7 +- src/version/Version.cpp.in | 8 +- .../tck/features/go/GoYieldVertexEdge.feature | 20 +- tests/tck/features/match/Base.feature | 15 + .../tck/features/path/AllPath.IntVid.feature | 59 ++- tests/tck/features/path/AllPath.feature | 59 ++- 21 files changed, 844 insertions(+), 317 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 93edc0147b0..6f76ccba573 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -97,7 +97,7 @@ jobs: -DENABLE_COVERAGE=on \ -B build echo "::set-output name=j::10" - echo "::set-output name=t::10" + echo "::set-output name=t::7" ;; esac ;; diff --git a/cmake/CPackage.cmake b/cmake/CPackage.cmake index b26eda90e5a..36a9acbc149 100644 --- a/cmake/CPackage.cmake +++ b/cmake/CPackage.cmake @@ -129,10 +129,11 @@ macro(package to_one name home_page scripts_dir) set(HOST_SYSTEM_VER "Unknown") endif() - message(STATUS "HOST_SYSTEM_NAME is ${HOST_SYSTEM_NAME}") - message(STATUS "HOST_SYSTEM_VER is ${HOST_SYSTEM_VER}") - message(STATUS "CPACK_GENERATOR is ${CPACK_GENERATOR}") - message(STATUS "CMAKE_HOST_SYSTEM_PROCESSOR is ${CMAKE_HOST_SYSTEM_PROCESSOR}") + print_config(NEBULA_BUILD_VERSION) + print_config(HOST_SYSTEM_NAME) + print_config(HOST_SYSTEM_VER) + print_config(CPACK_GENERATOR) + print_config(CMAKE_HOST_SYSTEM_PROCESSOR) set(CPACK_PACKAGE_FILE_NAME ${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}.${HOST_SYSTEM_VER}.${CMAKE_HOST_SYSTEM_PROCESSOR}) if (${to_one}) diff --git a/src/clients/meta/MetaClient.cpp b/src/clients/meta/MetaClient.cpp index a3e46fc9c0d..d4766213d32 100644 --- a/src/clients/meta/MetaClient.cpp +++ b/src/clients/meta/MetaClient.cpp @@ -2335,9 +2335,7 @@ folly::Future> MetaClient::heartbeat() { req.set_host(options_.localHost_); req.set_role(options_.role_); req.set_git_info_sha(options_.gitInfoSHA_); -#if defined(NEBULA_BUILD_VERSION) - req.set_version(versionString(false)); -#endif + req.set_version(getOriginVersion()); if (options_.role_ == cpp2::HostRole::STORAGE) { if (options_.clusterId_.load() == 0) { options_.clusterId_ = FileBasedClusterIdMan::getClusterIdFromFile(FLAGS_cluster_id_path); diff --git a/src/common/datatypes/Map.cpp b/src/common/datatypes/Map.cpp index 7ab73d5c9b7..5ac176ae86a 100644 --- a/src/common/datatypes/Map.cpp +++ b/src/common/datatypes/Map.cpp @@ -46,3 +46,14 @@ folly::dynamic Map::getMetaData() const { } } // namespace nebula + +namespace std { +std::size_t hash::operator()(const nebula::Map& m) const noexcept { + size_t seed = 0; + for (auto& v : m.kvs) { + seed ^= hash()(v.first) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; +} + +} // namespace std diff --git a/src/common/datatypes/Map.h b/src/common/datatypes/Map.h index 333e5d0cb0d..9e7db20c1b3 100644 --- a/src/common/datatypes/Map.h +++ b/src/common/datatypes/Map.h @@ -70,4 +70,12 @@ struct Map { inline std::ostream& operator<<(std::ostream& os, const Map& m) { return os << m.toString(); } } // namespace nebula + +namespace std { +template <> +struct hash { + std::size_t operator()(const nebula::Map& m) const noexcept; +}; + +} // namespace std #endif // COMMON_DATATYPES_MAP_H_ diff --git a/src/common/datatypes/Set.cpp b/src/common/datatypes/Set.cpp index 2e0e90a2f5a..5a130ace3e5 100644 --- a/src/common/datatypes/Set.cpp +++ b/src/common/datatypes/Set.cpp @@ -43,3 +43,14 @@ folly::dynamic Set::getMetaData() const { } } // namespace nebula + +namespace std { +std::size_t hash::operator()(const nebula::Set& s) const noexcept { + size_t seed = 0; + for (auto& v : s.values) { + seed ^= hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + return seed; +} + +} // namespace std diff --git a/src/common/datatypes/Set.h b/src/common/datatypes/Set.h index dabc33e0d7e..425097c04f2 100644 --- a/src/common/datatypes/Set.h +++ b/src/common/datatypes/Set.h @@ -54,6 +54,13 @@ struct Set { }; inline std::ostream& operator<<(std::ostream& os, const Set& s) { return os << s.toString(); } - } // namespace nebula + +namespace std { +template <> +struct hash { + std::size_t operator()(const nebula::Set& s) const noexcept; +}; + +} // namespace std #endif // COMMON_DATATYPES_SET_H_ diff --git a/src/common/datatypes/Value.cpp b/src/common/datatypes/Value.cpp index e4294a551ab..e6f82a2d517 100644 --- a/src/common/datatypes/Value.cpp +++ b/src/common/datatypes/Value.cpp @@ -69,10 +69,10 @@ std::size_t hash::operator()(const nebula::Value& v) const noexce return hash()(v.getGeography()); } case nebula::Value::Type::MAP: { - LOG(FATAL) << "Hash for MAP has not been implemented"; + return hash()(v.getMap()); } case nebula::Value::Type::SET: { - LOG(FATAL) << "Hash for SET has not been implemented"; + return hash()(v.getSet()); } case nebula::Value::Type::DATASET: { LOG(FATAL) << "Hash for DATASET has not been implemented"; diff --git a/src/common/expression/test/CMakeLists.txt b/src/common/expression/test/CMakeLists.txt index 20ba9d99f2a..577ce3f2f79 100644 --- a/src/common/expression/test/CMakeLists.txt +++ b/src/common/expression/test/CMakeLists.txt @@ -3,6 +3,50 @@ # This source code is licensed under Apache 2.0 License, # attached with Common Clause Condition 1.0, found in the LICENSES directory. +set(expression_test_common_libs + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ + $ +) + nebula_add_library( expr_ctx_mock_obj OBJECT ExpressionContextMock.cpp @@ -24,9 +68,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_executable( @@ -45,10 +91,12 @@ nebula_add_executable( $ $ $ + ${expression_test_common_libs} LIBRARIES follybenchmark boost_regex ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_executable( @@ -110,9 +158,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -129,9 +179,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -148,9 +200,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -167,9 +221,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -186,9 +242,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -205,9 +263,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -224,9 +284,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -243,9 +305,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -262,9 +326,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -281,9 +347,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -300,9 +368,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -319,9 +389,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -338,9 +410,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -357,9 +431,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -376,9 +452,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -395,9 +473,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -414,9 +494,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -433,9 +515,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -452,9 +536,11 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) nebula_add_test( @@ -471,7 +557,9 @@ nebula_add_test( $ $ $ + ${expression_test_common_libs} LIBRARIES gtest ${THRIFT_LIBRARIES} + ${PROXYGEN_LIBRARIES} ) diff --git a/src/common/expression/test/FunctionCallExpressionTest.cpp b/src/common/expression/test/FunctionCallExpressionTest.cpp index 2f10c8ed350..63f6216f445 100644 --- a/src/common/expression/test/FunctionCallExpressionTest.cpp +++ b/src/common/expression/test/FunctionCallExpressionTest.cpp @@ -60,7 +60,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { path.src.vid = "1"; STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, true); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, true); } { // hasSameEdgeInPath @@ -70,7 +70,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); STEP("2", "edge", 0, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, true); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, true); } { // hasSameEdgeInPath @@ -80,7 +80,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, 1); STEP("2", "edge", 0, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, false); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, false); } { // hasSameEdgeInPath @@ -90,7 +90,7 @@ TEST_F(FunctionCallExpressionTest, FunctionCallTest) { STEP("2", "edge", 0, 1); STEP("1", "edge", 0, -1); STEP("2", "edge", 1, 1); - TEST_FUNCTION(hasSameEdgeInPath, {path}, false); + TEST_PATH_FUNCTION(hasSameEdgeInPath, {path}, false); } // Check function { diff --git a/src/common/expression/test/LogicalExpressionTest.cpp b/src/common/expression/test/LogicalExpressionTest.cpp index dd900642e20..4d87f99cbc7 100644 --- a/src/common/expression/test/LogicalExpressionTest.cpp +++ b/src/common/expression/test/LogicalExpressionTest.cpp @@ -94,21 +94,63 @@ TEST_F(LogicalExpressionTest, LogicalCalculation) { TEST_EXPR(true AND 2 / 0, Value::kNullDivByZero); TEST_EXPR(false AND 2 / 0, false); TEST_EXPR(2 / 0 AND 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty AND null AND 2 / 0 AND empty, Value::kNullDivByZero); + { + // empty AND null AND 2 / 0 AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(2)); + auto *operand5 = ConstantExpression::make(&pool, Value(0)); + auto *operand6 = ArithmeticExpression::makeDivision(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeAnd(&pool, operand3, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } TEST_EXPR(2 / 0 OR true, Value::kNullDivByZero); TEST_EXPR(2 / 0 OR false, Value::kNullDivByZero); TEST_EXPR(true OR 2 / 0, true); TEST_EXPR(false OR 2 / 0, Value::kNullDivByZero); TEST_EXPR(2 / 0 OR 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty OR null OR 2 / 0 OR empty, Value::kNullDivByZero); + { + // empty OR null OR 2 / 0 OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(2)); + auto *operand5 = ConstantExpression::make(&pool, Value(0)); + auto *operand6 = ArithmeticExpression::makeDivision(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeOr(&pool, operand3, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } TEST_EXPR(2 / 0 XOR true, Value::kNullDivByZero); TEST_EXPR(2 / 0 XOR false, Value::kNullDivByZero); TEST_EXPR(true XOR 2 / 0, Value::kNullDivByZero); TEST_EXPR(false XOR 2 / 0, Value::kNullDivByZero); TEST_EXPR(2 / 0 XOR 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty XOR 2 / 0 XOR null XOR empty, Value::kNullDivByZero); + { + // empty XOR 2 / 0 XOR null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(2)); + auto *operand3 = ConstantExpression::make(&pool, Value(0)); + auto *operand4 = ArithmeticExpression::makeDivision(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeXor(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } // test normal null TEST_EXPR(null AND true, Value::kNullValue); @@ -116,62 +158,411 @@ TEST_F(LogicalExpressionTest, LogicalCalculation) { TEST_EXPR(true AND null, Value::kNullValue); TEST_EXPR(false AND null, false); TEST_EXPR(null AND null, Value::kNullValue); - TEST_EXPR(empty AND null AND empty, Value::kNullValue); + { + // empty AND null AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } TEST_EXPR(null OR true, true); TEST_EXPR(null OR false, Value::kNullValue); TEST_EXPR(true OR null, true); TEST_EXPR(false OR null, Value::kNullValue); TEST_EXPR(null OR null, Value::kNullValue); - TEST_EXPR(empty OR null OR empty, Value::kNullValue); + { + // empty OR null OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } TEST_EXPR(null XOR true, Value::kNullValue); TEST_EXPR(null XOR false, Value::kNullValue); TEST_EXPR(true XOR null, Value::kNullValue); TEST_EXPR(false XOR null, Value::kNullValue); TEST_EXPR(null XOR null, Value::kNullValue); - TEST_EXPR(empty XOR null XOR empty, Value::kNullValue); + { + // empty XOR null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand3 = LogicalExpression::makeXor(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } // test empty - TEST_EXPR(empty, Value::kEmpty); - TEST_EXPR(empty AND true, Value::kEmpty); - TEST_EXPR(empty AND false, false); - TEST_EXPR(true AND empty, Value::kEmpty); - TEST_EXPR(false AND empty, false); - TEST_EXPR(empty AND empty, Value::kEmpty); - TEST_EXPR(empty AND null, Value::kNullValue); - TEST_EXPR(null AND empty, Value::kNullValue); - TEST_EXPR(empty AND true AND empty, Value::kEmpty); - - TEST_EXPR(empty OR true, true); - TEST_EXPR(empty OR false, Value::kEmpty); - TEST_EXPR(true OR empty, true); - TEST_EXPR(false OR empty, Value::kEmpty); - TEST_EXPR(empty OR empty, Value::kEmpty); - TEST_EXPR(empty OR null, Value::kNullValue); - TEST_EXPR(null OR empty, Value::kNullValue); - TEST_EXPR(empty OR false OR empty, Value::kEmpty); - - TEST_EXPR(empty XOR true, Value::kEmpty); - TEST_EXPR(empty XOR false, Value::kEmpty); - TEST_EXPR(true XOR empty, Value::kEmpty); - TEST_EXPR(false XOR empty, Value::kEmpty); - TEST_EXPR(empty XOR empty, Value::kEmpty); - TEST_EXPR(empty XOR null, Value::kNullValue); - TEST_EXPR(null XOR empty, Value::kNullValue); - TEST_EXPR(true XOR empty XOR false, Value::kEmpty); - - TEST_EXPR(empty OR false AND true AND null XOR empty, Value::kEmpty); - TEST_EXPR(empty OR false AND true XOR empty OR true, true); + { + // empty + auto *expr = ConstantExpression::make(&pool, Value()); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(false).type()); + EXPECT_EQ(eval, Value(false)) << "check failed: " << expr->toString(); + } + { + // true AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // false AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(false).type()); + EXPECT_EQ(eval, Value(false)) << "check failed: " << expr->toString(); + } + { + // empty AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty AND null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null AND empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty AND true AND empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty OR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty OR false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // true OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // false OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null OR empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty OR false OR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeOr(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty XOR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR false + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // true XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // false XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(false)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty XOR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // true XOR empty XOR false + auto *operand1 = ConstantExpression::make(&pool, Value(true)); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *operand3 = LogicalExpression::makeXor(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(false)); + auto *expr = LogicalExpression::makeXor(&pool, operand3, operand4); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + + { + // empty OR false AND true AND null XOR empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand6 = LogicalExpression::makeAnd(&pool, operand4, operand5); + auto *operand7 = LogicalExpression::makeOr(&pool, operand1, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value()); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty OR false AND true XOR empty OR true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(true)); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } // clang-format off - TEST_EXPR((empty OR false) AND true XOR empty XOR null AND 2 / 0, Value::kNullValue); + { + // (empty OR false) AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = LogicalExpression::makeOr(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value(true)); + auto *operand5 = LogicalExpression::makeAnd(&pool, operand3, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + // clang-format on // empty OR false AND 2/0 - TEST_EXPR(empty OR false AND true XOR empty XOR null AND 2 / 0, Value::kEmpty); - TEST_EXPR(empty AND true XOR empty XOR null AND 2 / 0, Value::kNullValue); - TEST_EXPR(empty OR false AND true XOR empty OR null AND 2 / 0, Value::kNullDivByZero); - TEST_EXPR(empty OR false AND empty XOR empty OR null, Value::kNullValue); + { + // empty OR false AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeXor(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + // caution + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } + { + // empty AND true XOR empty XOR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *operand3 = LogicalExpression::makeAnd(&pool, operand1, operand2); + auto *operand4 = ConstantExpression::make(&pool, Value()); + auto *operand5 = LogicalExpression::makeXor(&pool, operand3, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand7 = ConstantExpression::make(&pool, Value(2)); + auto *operand8 = ConstantExpression::make(&pool, Value(0)); + auto *operand9 = ArithmeticExpression::makeDivision(&pool, operand7, operand8); + auto *operand10 = LogicalExpression::makeAnd(&pool, operand6, operand9); + auto *expr = LogicalExpression::makeXor(&pool, operand5, operand10); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty OR false AND true XOR empty OR null AND 2 / 0 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value(true)); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *operand9 = ConstantExpression::make(&pool, Value(2)); + auto *operand10 = ConstantExpression::make(&pool, Value(0)); + auto *operand11 = ArithmeticExpression::makeDivision(&pool, operand9, operand10); + auto *operand12 = LogicalExpression::makeAnd(&pool, operand8, operand11); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand12); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullDivByZero.type()); + EXPECT_EQ(eval, Value::kNullDivByZero) << "check failed: " << expr->toString(); + } + { + // TEST_EXPR(empty OR false AND empty XOR empty OR null, Value::kNullValue); + // 7 OR null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(false)); + auto *operand3 = ConstantExpression::make(&pool, Value()); + auto *operand4 = LogicalExpression::makeAnd(&pool, operand2, operand3); + auto *operand5 = LogicalExpression::makeOr(&pool, operand1, operand4); + auto *operand6 = ConstantExpression::make(&pool, Value()); + auto *operand7 = LogicalExpression::makeXor(&pool, operand5, operand6); + auto *operand8 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = LogicalExpression::makeOr(&pool, operand7, operand8); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } } } } // namespace nebula diff --git a/src/common/expression/test/RelationalExpressionTest.cpp b/src/common/expression/test/RelationalExpressionTest.cpp index 1ae134bc8ae..11bc892c463 100644 --- a/src/common/expression/test/RelationalExpressionTest.cpp +++ b/src/common/expression/test/RelationalExpressionTest.cpp @@ -96,15 +96,78 @@ TEST_F(ExpressionTest, LiteralConstantsRelational) { TEST_EXPR(1 <= 1, true); } { - TEST_EXPR(empty == empty, true); - TEST_EXPR(empty == null, Value::kNullValue); - TEST_EXPR(empty != null, Value::kNullValue); - TEST_EXPR(empty != 1, true); - TEST_EXPR(empty != true, true); - TEST_EXPR(empty > "1", Value::kEmpty); - TEST_EXPR(empty < 1, Value::kEmpty); - TEST_EXPR(empty >= 1.11, Value::kEmpty); - + // empty == empty + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value()); + auto *expr = RelationalExpression::makeEQ(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty == null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = RelationalExpression::makeEQ(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty != null + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(NullType::__NULL__)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kNullValue.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kNullValue) << "check failed: " << expr->toString(); + } + { + // empty != 1 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty != true + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(true)); + auto *expr = RelationalExpression::makeNE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value(true).type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value(true)) << "check failed: " << expr->toString(); + } + { + // empty > "1" + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value("1")); + auto *expr = RelationalExpression::makeGT(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty < 1 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1)); + auto *expr = RelationalExpression::makeLT(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { + // empty >= 1.11 + auto *operand1 = ConstantExpression::make(&pool, Value()); + auto *operand2 = ConstantExpression::make(&pool, Value(1.11)); + auto *expr = RelationalExpression::makeGE(&pool, operand1, operand2); + auto eval = Expression::eval(expr, gExpCtxt); + EXPECT_EQ(eval.type(), Value::kEmpty.type()) << "type check failed: " << expr->toString(); + EXPECT_EQ(eval, Value::kEmpty) << "check failed: " << expr->toString(); + } + { TEST_EXPR(null != 1, Value::kNullValue); TEST_EXPR(null != true, Value::kNullValue); TEST_EXPR(null > "1", Value::kNullValue); diff --git a/src/common/expression/test/TestBase.h b/src/common/expression/test/TestBase.h index 2ba5a6c629a..89fc2b3cb85 100644 --- a/src/common/expression/test/TestBase.h +++ b/src/common/expression/test/TestBase.h @@ -46,162 +46,86 @@ #include "common/expression/VariableExpression.h" #include "common/expression/VertexExpression.h" #include "common/expression/test/ExpressionContextMock.h" +#include "parser/GQLParser.h" nebula::ExpressionContextMock gExpCtxt; nebula::ObjectPool pool; namespace nebula { - -static void InsertSpace(std::string &str) { - for (unsigned int i = 0; i < str.size(); i++) { - if (str[i] == '(') { - str.insert(i + 1, 1, ' '); - } else if (str[i] == ')') { - str.insert(i, 1, ' '); - i += 1; - } else { - continue; - } - } -} - -static std::vector InfixToSuffix(const std::vector &expr) { - std::vector values; - std::stack operators; - std::unordered_map priority = {{"OR", 1}, - {"AND", 2}, - {"XOR", 3}, - {"==", 4}, - {"!=", 4}, - {">=", 5}, - {"<=", 5}, - {">", 5}, - {"<", 5}, - {"+", 6}, - {"-", 6}, - {"*", 7}, - {"/", 7}, - {"%", 7}, - {"!", 8}}; - - for (const auto &str : expr) { - if (priority.find(str) != priority.end() || str == "(") { - if (operators.empty() || str == "(") { - operators.push(str); - } else { - if (operators.top() == "(" || priority[str] > priority[operators.top()]) { - operators.push(str); - } else { - while (!operators.empty() && priority[str] <= priority[operators.top()]) { - values.push_back(operators.top()); - operators.pop(); - } - operators.push(str); - } - } - } else if (str == ")") { - while (!operators.empty() && operators.top() != "(") { - values.push_back(operators.top()); - operators.pop(); - } - operators.pop(); - } else { - values.push_back(str); - } - } - while (!operators.empty()) { - values.push_back(operators.top()); - operators.pop(); - } - return values; -} - class ExpressionTest : public ::testing::Test { public: void SetUp() override {} void TearDown() override {} protected: - static std::unordered_map boolen_; - // static std::unordered_map op_; - static std::unordered_map< - std::string, - std::function> - op_; - - protected: - Expression *ExpressionCalu(const std::vector &expr) { - std::vector relationOp = {">", ">=", "<", "<=", "==", "!="}; - std::vector logicalOp = {"AND", "OR", "XOR"}; - std::vector arithmeticOp = {"+", "-", "*", "/", "%"}; - - std::vector symbol = InfixToSuffix(expr); - if (symbol.size() == 1) { - // TEST_EXPR(true, true) - if (boolen_.find(symbol.front()) != boolen_.end()) { - return ConstantExpression::make(&pool, boolen_[symbol.front()]); - } else if (symbol.front().find('.') != std::string::npos) { - // TEST_EXPR(123.0, 123.0) - return ConstantExpression::make(&pool, ::atof(symbol.front().c_str())); - } - // TEST_EXPR(123, 123) - return ConstantExpression::make(&pool, ::atoi(symbol.front().c_str())); - } - - // calu suffix expression - std::stack value; - for (const auto &str : symbol) { - if (op_.find(str) == op_.end()) { - Expression *ep = nullptr; - if (boolen_.find(str) != boolen_.end()) { - ep = ConstantExpression::make(&pool, boolen_[str.c_str()]); - } else if (str.find('.') != std::string::npos) { - ep = ConstantExpression::make(&pool, ::atof(str.c_str())); - } else { - ep = ConstantExpression::make(&pool, ::atoi(str.c_str())); - } - value.push(ep); - } else { - Expression *result = nullptr; - Expression *rhs = value.top(); - value.pop(); - Expression *lhs = value.top(); - value.pop(); - if (std::find(arithmeticOp.begin(), arithmeticOp.end(), str) != arithmeticOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else if (std::find(relationOp.begin(), relationOp.end(), str) != relationOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else if (std::find(logicalOp.begin(), logicalOp.end(), str) != logicalOp.end()) { - result = op_[str](&pool, lhs, rhs); - } else { - return ConstantExpression::make(&pool, NullType::UNKNOWN_PROP); - } - value.push(result); - } - } - return value.top(); - } - void testExpr(const std::string &exprSymbol, Value expected) { - std::string expr(exprSymbol); - InsertSpace(expr); - std::vector splitString; - boost::split(splitString, expr, boost::is_any_of(" \t")); - Expression *ep = ExpressionCalu(splitString); + std::string query = "RETURN " + exprSymbol; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + Expression *ep = yieldSentence->yield()->yields()->back()->expr(); auto eval = Expression::eval(ep, gExpCtxt); EXPECT_EQ(eval.type(), expected.type()) << "type check failed: " << ep->toString(); EXPECT_EQ(eval, expected) << "check failed: " << ep->toString(); } void testToString(const std::string &exprSymbol, const char *expected) { - std::string expr(exprSymbol); - InsertSpace(expr); - std::vector splitString; - boost::split(splitString, expr, boost::is_any_of(" \t")); - Expression *ep = ExpressionCalu(splitString); + std::string query = "RETURN " + exprSymbol; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + Expression *ep = yieldSentence->yield()->yields()->back()->expr(); + ASSERT_NE(ep, nullptr); EXPECT_EQ(ep->toString(), expected); } void testFunction(const char *name, const std::vector &args, const Value &expected) { + std::string query = "RETURN " + std::string(name) + "("; + for (const auto &i : args) { + query += i.toString() + ","; + } + if (query.back() == ',') { + query.pop_back(); + } + query += ")"; + nebula::graph::QueryContext queryCtxt; + nebula::GQLParser gParser(&queryCtxt); + auto result = gParser.parse(query); + ASSERT_EQ(result.ok(), true); + auto *sequentialSentences = static_cast(result.value().get()); + ASSERT_NE(sequentialSentences, nullptr); + auto sentences = sequentialSentences->sentences(); + ASSERT_GT(sentences.size(), 0); + auto *yieldSentence = static_cast(sentences[0]); + ASSERT_NE(yieldSentence, nullptr); + ASSERT_NE(yieldSentence->yield(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields(), nullptr); + ASSERT_NE(yieldSentence->yield()->yields()->back(), nullptr); + auto eval = Expression::eval(yieldSentence->yield()->yields()->back()->expr(), gExpCtxt); + // EXPECT_EQ(eval.type(), expected.type()); + EXPECT_EQ(eval, expected); + } + + void testPathFunction(const char *name, const std::vector &args, const Value &expected) { ArgumentList *argList = ArgumentList::make(&pool); for (const auto &i : args) { argList->addArgument(ConstantExpression::make(&pool, i)); @@ -212,6 +136,7 @@ class ExpressionTest : public ::testing::Test { EXPECT_EQ(eval, expected); } }; + // expr -- the expression can evaluate by nGQL parser may not evaluated by c++ // expected -- the expected value of expression must evaluated by c++ #define TEST_EXPR(expr, expected) \ @@ -224,6 +149,11 @@ class ExpressionTest : public ::testing::Test { testFunction(#expr, args, expected); \ } while (0) +#define TEST_PATH_FUNCTION(expr, args, expected) \ + do { \ + testPathFunction(#expr, args, expected); \ + } while (0) + #define TEST_TOSTRING(expr, expected) \ do { \ testToString(#expr, expected); \ @@ -239,64 +169,6 @@ class ExpressionTest : public ::testing::Test { path.steps.emplace_back(std::move(step)); \ } while (0) -// Functions used to construct corresponding expressions -using expressionGen = - std::function; - -expressionGen makeAddExpr{&ArithmeticExpression::makeAdd}; -expressionGen makeMinusExpr{&ArithmeticExpression::makeMinus}; -expressionGen makeMultiplyExpr{&ArithmeticExpression::makeMultiply}; -expressionGen makeDivisionExpr{&ArithmeticExpression::makeDivision}; -expressionGen makeModExpr{&ArithmeticExpression::makeMod}; -expressionGen makeOrExpr{ - static_cast( - &LogicalExpression::makeOr)}; -expressionGen makeAndExpr{ - static_cast( - &LogicalExpression::makeAnd)}; -expressionGen makeXorExpr{ - static_cast( - &LogicalExpression::makeXor)}; -expressionGen makeRelGT{ - static_cast( - &RelationalExpression::makeGT)}; -expressionGen makeRelLT{ - static_cast( - &RelationalExpression::makeLT)}; -expressionGen makeRelGE{ - static_cast( - &RelationalExpression::makeGE)}; -expressionGen makeRelLE{ - static_cast( - &RelationalExpression::makeLE)}; -expressionGen makeRelEQ{ - static_cast( - &RelationalExpression::makeEQ)}; -expressionGen makeRelNE{ - static_cast( - &RelationalExpression::makeNE)}; - -std::unordered_map ExpressionTest::op_ = {{"+", makeAddExpr}, - {"-", makeMinusExpr}, - {"*", makeMultiplyExpr}, - {"/", makeDivisionExpr}, - {"%", makeModExpr}, - {"OR", makeOrExpr}, - {"AND", makeAndExpr}, - {"XOR", makeXorExpr}, - {">", makeRelGT}, - {"<", makeRelLT}, - {">=", makeRelGE}, - {"<=", makeRelLE}, - {"==", makeRelEQ}, - {"!=", makeRelNE}}; - -std::unordered_map ExpressionTest::boolen_ = { - {"true", Value(true)}, - {"false", Value(false)}, - {"empty", Value()}, - {"null", Value(NullType::__NULL__)}}; - static std::unordered_map> args_ = { {"null", {}}, {"int", {4}}, @@ -314,6 +186,5 @@ static std::unordered_map> args_ = { {"neg_side", {"abcdefghijklmnopq", -2}}, {"pad", {"abcdefghijkl", 16, "123"}}, {"udf_is_in", {4, 1, 2, 8, 4, 3, 1, 0}}}; - } // namespace nebula #endif // COMMON_EXPRESSION_TEST_TESTBASE_H_ diff --git a/src/graph/executor/query/DataCollectExecutor.cpp b/src/graph/executor/query/DataCollectExecutor.cpp index c15049cec35..c0ecd5926b7 100644 --- a/src/graph/executor/query/DataCollectExecutor.cpp +++ b/src/graph/executor/query/DataCollectExecutor.cpp @@ -274,7 +274,6 @@ Status DataCollectExecutor::collectMultiplePairShortestPath(const std::vector& vars) { DataSet ds; ds.colNames = colNames_; - DCHECK(!ds.colNames.empty()); // 0: vertices's props, 1: Edges's props 2: paths without prop DCHECK_EQ(vars.size(), 3); @@ -284,7 +283,7 @@ Status DataCollectExecutor::collectPathProp(const std::vector& vars DCHECK(vIter->isPropIter()); for (; vIter->valid(); vIter->next()) { const auto& vertexVal = vIter->getVertex(); - if (!vertexVal.isVertex()) { + if (UNLIKELY(!vertexVal.isVertex())) { continue; } const auto& vertex = vertexVal.getVertex(); @@ -296,8 +295,8 @@ Status DataCollectExecutor::collectPathProp(const std::vector& vars edgeMap.reserve(eIter->size()); DCHECK(eIter->isPropIter()); for (; eIter->valid(); eIter->next()) { - auto edgeVal = eIter->getEdge(); - if (!edgeVal.isEdge()) { + const auto& edgeVal = eIter->getEdge(); + if (UNLIKELY(!edgeVal.isEdge())) { continue; } auto& edge = edgeVal.getEdge(); @@ -308,8 +307,8 @@ Status DataCollectExecutor::collectPathProp(const std::vector& vars auto pIter = ectx_->getResult(vars[2]).iter(); DCHECK(pIter->isSequentialIter()); for (; pIter->valid(); pIter->next()) { - auto& pathVal = pIter->getColumn(0); - if (!pathVal.isPath()) { + const auto& pathVal = pIter->getColumn(0); + if (UNLIKELY(!pathVal.isPath())) { continue; } auto path = pathVal.getPath(); @@ -320,13 +319,15 @@ Status DataCollectExecutor::collectPathProp(const std::vector& vars } for (auto& step : path.steps) { auto dst = step.dst.vid; - step.dst = vertexMap[dst]; + found = vertexMap.find(dst); + if (found != vertexMap.end()) { + step.dst = found->second; + } auto type = step.type; auto ranking = step.ranking; if (type < 0) { - dst = src; - src = step.dst.vid; + std::swap(src, dst); type = -type; } auto edgeKey = std::make_tuple(src, type, ranking, dst); diff --git a/src/graph/service/GraphService.cpp b/src/graph/service/GraphService.cpp index 4fd914040fc..4d8141ed8c6 100644 --- a/src/graph/service/GraphService.cpp +++ b/src/graph/service/GraphService.cpp @@ -41,13 +41,16 @@ Status GraphService::init(std::shared_ptr ioExecuto bool loadDataOk = metaClient_->waitForMetadReady(3); if (!loadDataOk) { // Resort to retrying in the background - LOG(WARNING) << "Failed to wait for meta service ready synchronously."; + LOG(ERROR) << "Failed to wait for meta service ready synchronously."; + return Status::Error("Failed to wait for meta service ready synchronously."); } sessionManager_ = std::make_unique(metaClient_.get(), hostAddr); auto initSessionMgrStatus = sessionManager_->init(); if (!initSessionMgrStatus.ok()) { - LOG(WARNING) << "Failed to initialize session manager: " << initSessionMgrStatus.toString(); + LOG(ERROR) << "Failed to initialize session manager: " << initSessionMgrStatus.toString(); + return Status::Error("Failed to initialize session manager: %s", + initSessionMgrStatus.toString().c_str()); } queryEngine_ = std::make_unique(); diff --git a/src/meta/processors/parts/ListHostsProcessor.cpp b/src/meta/processors/parts/ListHostsProcessor.cpp index ff4c7547468..cea7e8b7d66 100644 --- a/src/meta/processors/parts/ListHostsProcessor.cpp +++ b/src/meta/processors/parts/ListHostsProcessor.cpp @@ -90,7 +90,7 @@ nebula::cpp2::ErrorCode ListHostsProcessor::allMetaHostsStatus() { item.set_role(cpp2::HostRole::META); item.set_git_info_sha(gitInfoSha()); item.set_status(cpp2::HostStatus::ONLINE); - item.set_version(versionString(false)); + item.set_version(getOriginVersion()); hostItems_.emplace_back(item); } return nebula::cpp2::ErrorCode::SUCCEEDED; @@ -111,13 +111,11 @@ nebula::cpp2::ErrorCode ListHostsProcessor::allHostsWithStatus(cpp2::HostRole ro return retCode; } - auto iter = nebula::value(ret).get(); auto now = time::WallClock::fastNowInMilliSec(); std::vector removeHostsKey; - while (iter->valid()) { + for (auto iter = nebula::value(ret).get(); iter->valid(); iter->next()) { HostInfo info = HostInfo::decode(iter->val()); if (info.role_ != role) { - iter->next(); continue; } @@ -143,7 +141,6 @@ nebula::cpp2::ErrorCode ListHostsProcessor::allHostsWithStatus(cpp2::HostRole ro } else { removeHostsKey.emplace_back(iter->key()); } - iter->next(); } removeExpiredHosts(std::move(removeHostsKey)); diff --git a/src/version/Version.cpp.in b/src/version/Version.cpp.in index f8675b605e0..c23cd34fc8e 100644 --- a/src/version/Version.cpp.in +++ b/src/version/Version.cpp.in @@ -15,10 +15,10 @@ namespace nebula { std::string gitInfoSha() { return "@GIT_INFO_SHA@"; } std::string versionString(bool verbose) { - std::string version; -#if defined(NEBULA_BUILD_VERSION) - version = folly::stringPrintf("%s, ", "@NEBULA_BUILD_VERSION@"); -#endif + std::string version = getOriginVersion(); + if (!version.empty()) { + version += ", "; + } if (verbose) { version += folly::sformat("Git: {}, ", gitInfoSha()); } diff --git a/tests/tck/features/go/GoYieldVertexEdge.feature b/tests/tck/features/go/GoYieldVertexEdge.feature index b38ebc7f876..4075300fbbf 100644 --- a/tests/tck/features/go/GoYieldVertexEdge.feature +++ b/tests/tck/features/go/GoYieldVertexEdge.feature @@ -226,9 +226,7 @@ Feature: Go Yield Vertex And Edge Sentence """ Then a SyntaxError should be raised at runtime: syntax error near `OVER' - @skip - # reason we not support hash hash hash from now on. line 67 in Value.cpp - Scenario: distinct map + Scenario: distinct map and set When executing query: """ GO FROM "Boris Diaw" OVER like YIELD dst(edge) as id | @@ -236,7 +234,21 @@ Feature: Go Yield Vertex And Edge Sentence GO FROM $-.id OVER serve YIELD DISTINCT dst(edge) as dst, edge as e, properties(edge) as props """ Then the result should be, in any order, with relax comparison: - | dst | e | props | + | dst | e | props | + | "Spurs" | [:serve "Manu Ginobili"->"Spurs" @0 {end_year: 2018, start_year: 2002}] | {end_year: 2018, start_year: 2002} | + | "Spurs" | [:serve "Tim Duncan"->"Spurs" @0 {end_year: 2016, start_year: 1997}] | {end_year: 2016, start_year: 1997} | + | "Hornets" | [:serve "Tony Parker"->"Hornets" @0 {end_year: 2019, start_year: 2018}] | {end_year: 2019, start_year: 2018} | + | "Spurs" | [:serve "Tony Parker"->"Spurs" @0 {end_year: 2018, start_year: 1999}] | {end_year: 2018, start_year: 1999} | + | "Spurs" | [:serve "LaMarcus Aldridge"->"Spurs" @0 {end_year: 2019, start_year: 2015}] | {end_year: 2019, start_year: 2015} | + | "Trail Blazers" | [:serve "LaMarcus Aldridge"->"Trail Blazers" @0 {end_year: 2015, start_year: 2006}] | {end_year: 2015, start_year: 2006} | + When executing query: + """ + GO 2 STEPS FROM "Tim Duncan" OVER like YIELD dst(edge) as id | + YIELD DISTINCT collect($-.id) as a, collect_set($-.id) as b + """ + Then the result should be, in any order, with relax comparison: + | a | b | + | ["Tim Duncan", "LaMarcus Aldridge", "Manu Ginobili", "Tim Duncan"] | {"Manu Ginobili", "LaMarcus Aldridge", "Tim Duncan"} | Scenario: distinct When executing query: diff --git a/tests/tck/features/match/Base.feature b/tests/tck/features/match/Base.feature index 98705f6b212..8cd92190d1a 100644 --- a/tests/tck/features/match/Base.feature +++ b/tests/tck/features/match/Base.feature @@ -211,6 +211,21 @@ Feature: Basic match | ("Paul Gasol" :player{age: 38, name: "Paul Gasol"}) | [:like "Paul Gasol"->"Marc Gasol" @0 {likeness: 99}] | ("Marc Gasol" :player{age: 34, name: "Marc Gasol"}) | | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | [:like "Yao Ming"->"Shaquile O'Neal" @0 {likeness: 90}] | ("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"}) | | ("Yao Ming" :player{age: 38, name: "Yao Ming"}) | [:like "Yao Ming"->"Tracy McGrady" @0 {likeness: 90}] | ("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"}) | + When executing query: + """ + MATCH (v:player)-[e:like]->(v2) where id(v) == "Tim Duncan" RETURN DISTINCT properties(e) as props, e + """ + Then the result should be, in any order, with relax comparison: + | props | e | + | {likeness: 95} | [:like "Tim Duncan"->"Manu Ginobili" @0 {likeness: 95}] | + | {likeness: 95} | [:like "Tim Duncan"->"Tony Parker" @0 {likeness: 95}] | + When executing query: + """ + MATCH (v:player)-[e:like]->(v2) where id(v) == "Tim Duncan" RETURN DISTINCT properties(e) as props + """ + Then the result should be, in any order, with relax comparison: + | props | + | {likeness: 95} | Scenario: two steps When executing query: diff --git a/tests/tck/features/path/AllPath.IntVid.feature b/tests/tck/features/path/AllPath.IntVid.feature index f4599d5897c..b86cfad333a 100644 --- a/tests/tck/features/path/AllPath.IntVid.feature +++ b/tests/tck/features/path/AllPath.IntVid.feature @@ -4,10 +4,8 @@ # attached with Common Clause Condition 1.0, found in the LICENSES directory. Feature: Integer Vid All Path - Background: - Given a graph with space named "nba_int_vid" - Scenario: Integer Vid [1] ALL Path + Given a graph with space named "nba_int_vid" When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tim Duncan") OVER * UPTO 2 STEPS @@ -33,8 +31,6 @@ Feature: Integer Vid All Path | <("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | - - Scenario: Integer Vid [2] ALL Path When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker"), hash("Manu Ginobili") OVER like UPTO 3 STEPS @@ -47,8 +43,6 @@ Feature: Integer Vid All Path | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")> | - - Scenario: Integer Vid [3] ALL Path When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker"), hash("LaMarcus Aldridge") OVER like UPTO 3 STEPS @@ -59,8 +53,6 @@ Feature: Integer Vid All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | - - Scenario: Integer Vid [4] ALL Path When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker"), hash("Spurs") OVER like,serve UPTO 3 STEPS @@ -79,6 +71,7 @@ Feature: Integer Vid All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Manu Ginobili")-[:serve]->("Spurs")> | Scenario: Integer Vid [1] ALL Path Run Time Input + Given a graph with space named "nba_int_vid" When executing query: """ GO FROM hash("Tim Duncan") over * YIELD like._dst AS src, serve._src AS dst @@ -93,8 +86,6 @@ Feature: Integer Vid All Path | <("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | - - Scenario: Integer Vid [2] ALL Path Run Time Input When executing query: """ $a = GO FROM hash("Tim Duncan") over * YIELD like._dst AS src, serve._src AS dst; @@ -109,8 +100,6 @@ Feature: Integer Vid All Path | <("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | - - Scenario: Integer Vid [1] ALL Path With Limit When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker"), hash("Spurs") OVER like,serve UPTO 3 STEPS @@ -123,6 +112,7 @@ Feature: Integer Vid All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:serve]->("Spurs")> | Scenario: Integer Vid [2] ALL Path With Limit + Given a graph with space named "nba_int_vid" When executing query: """ $a = GO FROM hash("Tim Duncan") over * YIELD like._dst AS src, serve._src AS dst; @@ -138,14 +128,13 @@ Feature: Integer Vid All Path | <("Tony Parker")-[:like@0]->("Tim Duncan")-[:like@0]->("Manu Ginobili")-[:like@0]->("Tim Duncan")> | Scenario: Integer Vid [1] ALL PATH REVERSELY + Given a graph with space named "nba_int_vid" When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Nobody"), hash("Spur") OVER like REVERSELY UPTO 3 STEPS """ Then the result should be, in any order, with relax comparison: | path | - - Scenario: Integer Vid [2] ALL PATH REVERSELY When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker") OVER like REVERSELY UPTO 3 STEPS @@ -157,8 +146,6 @@ Feature: Integer Vid All Path | <("Tim Duncan")<-[:like]-("Manu Ginobili")<-[:like]-("Tony Parker")> | | <("Tim Duncan")<-[:like]-("Manu Ginobili")<-[:like]-("Tim Duncan")<-[:like]-("Tony Parker")> | | <("Tim Duncan")<-[:like]-("Tony Parker")<-[:like]-("LaMarcus Aldridge")<-[:like]-("Tony Parker")> | - - Scenario: Integer Vid [3] ALL PATH REVERSELY When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker"), hash("LaMarcus Aldridge") OVER like REVERSELY UPTO 3 STEPS @@ -178,6 +165,7 @@ Feature: Integer Vid All Path | <("Tim Duncan")<-[:like]-("Tony Parker")<-[:like]-("LaMarcus Aldridge")<-[:like]-("Tony Parker")> | Scenario: Integer Vid [2] ALL PATH BIDIRECT + Given a graph with space named "nba_int_vid" When executing query: """ FIND ALL PATH FROM hash("Tim Duncan") TO hash("Tony Parker") OVER like BIDIRECT UPTO 3 STEPS @@ -212,6 +200,7 @@ Feature: Integer Vid All Path | <("Tim Duncan")<-[:like]-("Tiago Splitter")-[:like]->("Manu Ginobili")<-[:like]-("Tony Parker")> | Scenario: Integer Vid ALL Path WITH PROP + Given a graph with space named "nba_int_vid" When executing query: """ FIND ALL PATH WITH PROP FROM hash("Tim Duncan") TO hash("Tony Parker") OVER like UPTO 3 STEPS @@ -223,6 +212,7 @@ Feature: Integer Vid All Path | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})-[:like@0 {likeness: 90}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})-[:like@0 {likeness: 75}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | Scenario: Integer Vid ALL Path WITH FILTER + Given a graph with space named "nba_int_vid" When executing query: """ FIND ALL PATH WITH PROP FROM hash("Tim Duncan") TO hash("Yao Ming") OVER * BIDIRECT @@ -253,3 +243,38 @@ Feature: Integer Vid All Path | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"})-[:like@0 {likeness: 80}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2000, start_year: 1997}]->("Raptors" :team{name: "Raptors"})<-[:serve@0 {end_year: 2019, start_year: 2018}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2013, start_year: 2013}]->("Spurs" :team{name: "Spurs"})<-[:serve@0 {end_year: 2018, start_year: 2010}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | + + Scenario: Integer Vid Dangling edge + Given an empty graph + And load "nba_int_vid" csv data to a new space + When executing query: + """ + INSERT EDGE like(likeness) VALUES hash("Tim Duncan")->hash("Tim Parker"):(99); + INSERT EDGE like(likeness) VALUES hash("Tim Parker")->hash("Tony Parker"):(90); + """ + Then the execution should be successful + When executing query: + """ + FIND ALL PATH WITH PROP FROM hash("Tim Duncan") TO hash("Tony Parker") OVER like UPTO 2 steps + """ + Then the result should be, in any order, with relax comparison: + | path | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 99}]->("Tim Parker")-[:like@0 {likeness: 90}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + When executing query: + """ + FIND ALL PATH WITH PROP FROM hash("Tim Duncan") TO hash("Tony Parker") OVER like BIDIRECT UPTO 2 steps + """ + Then the result should be, in any order, with relax comparison: + | path | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 99}]->("Tim Parker")-[:like@0 {likeness: 90}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 55}]-("Marco Belinelli" :player{age: 32, name: "Marco Belinelli"})-[:like@0 {likeness: 50}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 99}]-("Dejounte Murray" :player{age: 29, name: "Dejounte Murray"})-[:like@0 {likeness: 99}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 75}]-("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})<-[:like@0 {likeness: 90}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 75}]-("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})-[:like@0 {likeness: 75}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 90}]-("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 80}]-("Boris Diaw" :player{age: 36, name: "Boris Diaw"})-[:like@0 {likeness: 80}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + Then drop the used space diff --git a/tests/tck/features/path/AllPath.feature b/tests/tck/features/path/AllPath.feature index 99cb1e697dd..151cb7e64d7 100644 --- a/tests/tck/features/path/AllPath.feature +++ b/tests/tck/features/path/AllPath.feature @@ -4,10 +4,8 @@ # attached with Common Clause Condition 1.0, found in the LICENSES directory. Feature: All Path - Background: - Given a graph with space named "nba" - Scenario: [1] ALL Path + Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tim Duncan" OVER * UPTO 2 STEPS @@ -33,8 +31,6 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | - - Scenario: [2] ALL Path When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker", "Manu Ginobili" OVER like UPTO 3 STEPS @@ -47,8 +43,6 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")> | - - Scenario: [3] ALL Path When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","LaMarcus Aldridge" OVER like UPTO 3 STEPS @@ -59,8 +53,6 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")> | - - Scenario: [4] ALL Path When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","Spurs" OVER like,serve UPTO 3 STEPS @@ -79,6 +71,7 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Manu Ginobili")-[:serve]->("Spurs")> | Scenario: [1] ALL Path Run Time Input + Given a graph with space named "nba" When executing query: """ GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst @@ -93,8 +86,6 @@ Feature: All Path | <("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("LaMarcus Aldridge")-[:like]->("Tony Parker")-[:like]->("Tim Duncan")> | | <("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | - - Scenario: [2] ALL Path Run Time Input When executing query: """ $a = GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst; @@ -111,6 +102,7 @@ Feature: All Path | <("Tony Parker")-[:like]->("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | Scenario: [1] ALL Path With Limit + Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","Spurs" OVER like,serve UPTO 3 STEPS @@ -121,8 +113,6 @@ Feature: All Path | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:serve]->("Spurs")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:serve]->("Spurs")> | | <("Tim Duncan")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")-[:like]->("Tony Parker")> | - - Scenario: [2] ALL Path With Limit When executing query: """ $a = GO FROM "Tim Duncan" over * YIELD like._dst AS src, serve._src AS dst; @@ -138,14 +128,13 @@ Feature: All Path | <("Tony Parker")-[:like]->("Manu Ginobili")-[:like]->("Tim Duncan")> | Scenario: [1] ALL PATH REVERSELY + Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Nobody","Spur" OVER like REVERSELY UPTO 3 STEPS """ Then the result should be, in any order, with relax comparison: | path | - - Scenario: [2] ALL PATH REVERSELY When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker" OVER like REVERSELY UPTO 3 STEPS @@ -157,8 +146,6 @@ Feature: All Path | <("Tim Duncan")<-[:like]-("Manu Ginobili")<-[:like]-("Tony Parker")> | | <("Tim Duncan")<-[:like]-("Manu Ginobili")<-[:like]-("Tim Duncan")<-[:like]-("Tony Parker")> | | <("Tim Duncan")<-[:like]-("Tony Parker")<-[:like]-("LaMarcus Aldridge")<-[:like]-("Tony Parker")> | - - Scenario: [3] ALL PATH REVERSELY When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker","LaMarcus Aldridge" OVER like REVERSELY UPTO 3 STEPS @@ -178,6 +165,7 @@ Feature: All Path | <("Tim Duncan")<-[:like]-("Tony Parker")<-[:like]-("LaMarcus Aldridge")<-[:like]-("Tony Parker")> | Scenario: [2] ALL PATH BIDIRECT + Given a graph with space named "nba" When executing query: """ FIND ALL PATH FROM "Tim Duncan" TO "Tony Parker" OVER like BIDIRECT UPTO 3 STEPS @@ -212,6 +200,7 @@ Feature: All Path | <("Tim Duncan")<-[:like]-("Tiago Splitter")-[:like]->("Manu Ginobili")<-[:like]-("Tony Parker")> | Scenario: ALL Path WITH PROP + Given a graph with space named "nba" When executing query: """ FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Tony Parker" OVER like UPTO 3 STEPS @@ -223,6 +212,7 @@ Feature: All Path | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})-[:like@0 {likeness: 90}]->("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})-[:like@0 {likeness: 75}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | Scenario: ALL Path WITH FILTER + Given a graph with space named "nba" When executing query: """ FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Yao Ming" OVER * BIDIRECT @@ -253,3 +243,38 @@ Feature: All Path | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Shaquile O'Neal" :player{age: 47, name: "Shaquile O'Neal"})-[:like@0 {likeness: 80}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:teammate@0 {end_year: 2016, start_year: 2010}]->("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2000, start_year: 1997}]->("Raptors" :team{name: "Raptors"})<-[:serve@0 {end_year: 2019, start_year: 2018}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | | <("Yao Ming" :player{age: 38, name: "Yao Ming"})-[:like@0 {likeness: 90}]->("Tracy McGrady" :player{age: 39, name: "Tracy McGrady"})-[:serve@0 {end_year: 2013, start_year: 2013}]->("Spurs" :team{name: "Spurs"})<-[:serve@0 {end_year: 2018, start_year: 2010}]-("Danny Green" :player{age: 31, name: "Danny Green"})> | + + Scenario: Dangling edge + Given an empty graph + And load "nba" csv data to a new space + When executing query: + """ + INSERT EDGE like(likeness) VALUES "Tim Duncan"->"Tim Parker":(99); + INSERT EDGE like(likeness) VALUES "Tim Parker"->"Tony Parker":(90); + """ + Then the execution should be successful + When executing query: + """ + FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Tony Parker" OVER like UPTO 2 steps + """ + Then the result should be, in any order, with relax comparison: + | path | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 99}]->("Tim Parker")-[:like@0 {likeness: 90}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + When executing query: + """ + FIND ALL PATH WITH PROP FROM "Tim Duncan" TO "Tony Parker" OVER like BIDIRECT UPTO 2 steps + """ + Then the result should be, in any order, with relax comparison: + | path | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 99}]->("Tim Parker")-[:like@0 {likeness: 90}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 55}]-("Marco Belinelli" :player{age: 32, name: "Marco Belinelli"})-[:like@0 {likeness: 50}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 99}]-("Dejounte Murray" :player{age: 29, name: "Dejounte Murray"})-[:like@0 {likeness: 99}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 75}]-("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})<-[:like@0 {likeness: 90}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 75}]-("LaMarcus Aldridge" :player{age: 33, name: "LaMarcus Aldridge"})-[:like@0 {likeness: 75}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 90}]-("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:like@0 {likeness: 95}]->("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})<-[:like@0 {likeness: 95}]-("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + | <("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})<-[:like@0 {likeness: 80}]-("Boris Diaw" :player{age: 36, name: "Boris Diaw"})-[:like@0 {likeness: 80}]->("Tony Parker" :player{age: 36, name: "Tony Parker"})> | + Then drop the used space