From 120d852adc6cb9c1fcd298aa2c349f39d86fd0af Mon Sep 17 00:00:00 2001 From: nebula-bots <88429921+nebula-bots@users.noreply.github.com> Date: Tue, 16 Aug 2022 13:37:04 +0800 Subject: [PATCH] Feature/pattern expression ref local variable (#1169) * Initial. * Get pattern expression value by expression. * Filter path by local variable. * Add tests. Co-authored-by: Sophie <84560950+Sophie-Xie@users.noreply.github.com> Co-authored-by: shylock <33566796+Shylock-Hg@users.noreply.github.com> Co-authored-by: Sophie <84560950+Sophie-Xie@users.noreply.github.com> --- .../expression/MatchPathPatternExpression.cpp | 8 +- .../expression/MatchPathPatternExpression.h | 18 +- src/common/function/FunctionManager.cpp | 24 +++ src/graph/util/AnonVarGenerator.h | 4 + src/graph/util/ParserUtil.cpp | 12 +- src/graph/validator/MatchValidator.cpp | 14 +- src/graph/visitor/CMakeLists.txt | 1 + src/graph/visitor/ExprVisitorImpl.cpp | 4 +- src/graph/visitor/FindVisitor.cpp | 4 +- src/graph/visitor/RewriteSymExprVisitor.cpp | 11 +- .../ValidatePatternExpressionVisitor.cpp | 70 +++++++ .../ValidatePatternExpressionVisitor.h | 61 ++++++ src/parser/MatchPath.h | 16 ++ src/parser/parser.yy | 6 +- tests/data/ldbc_v0_3_3/config.yaml | 1 + .../match/PathExprRefLocalVariable.feature | 194 ++++++++++++++++++ .../PushFilterDownGetNbrsRule.feature | 8 +- .../interactive_workload/ComplexReads.feature | 14 +- 18 files changed, 426 insertions(+), 44 deletions(-) create mode 100644 src/graph/visitor/ValidatePatternExpressionVisitor.cpp create mode 100644 src/graph/visitor/ValidatePatternExpressionVisitor.h create mode 100644 tests/tck/features/match/PathExprRefLocalVariable.feature diff --git a/src/common/expression/MatchPathPatternExpression.cpp b/src/common/expression/MatchPathPatternExpression.cpp index 7f778f996b1..76180776a48 100644 --- a/src/common/expression/MatchPathPatternExpression.cpp +++ b/src/common/expression/MatchPathPatternExpression.cpp @@ -10,7 +10,7 @@ namespace nebula { const Value& MatchPathPatternExpression::eval(ExpressionContext& ctx) { - result_ = DCHECK_NOTNULL(prop_)->eval(ctx); + result_ = DCHECK_NOTNULL(genList_)->eval(ctx); return result_; } @@ -23,7 +23,7 @@ bool MatchPathPatternExpression::operator==(const Expression& rhs) const { return false; } - // The prop_ field is used for evaluation internally, so it don't identify the expression. + // The genList_ field is used for evaluation internally, so it don't identify the expression. // We don't compare it here. // Ditto for result_ field. @@ -41,8 +41,8 @@ void MatchPathPatternExpression::accept(ExprVisitor* visitor) { Expression* MatchPathPatternExpression::clone() const { auto expr = MatchPathPatternExpression::make(pool_, std::make_unique(matchPath_->clone())); - if (prop_ != nullptr) { - expr->setInputProp(static_cast(prop_->clone())); + if (genList_ != nullptr) { + expr->setGenList(genList_->clone()); } return expr; } diff --git a/src/common/expression/MatchPathPatternExpression.h b/src/common/expression/MatchPathPatternExpression.h index a80afe6345e..1438ddcc293 100644 --- a/src/common/expression/MatchPathPatternExpression.h +++ b/src/common/expression/MatchPathPatternExpression.h @@ -33,17 +33,16 @@ class MatchPathPatternExpression final : public Expression { Expression* clone() const override; - // Evaluate expression by fetch result from input variable - void setInputProp(const std::string& prop) { - prop_ = InputPropertyExpression::make(pool_, prop); + void setGenList(Expression* expr) { + genList_ = expr; } - void setInputProp(InputPropertyExpression* expr) { - prop_ = expr; + const Expression* genList() const { + return genList_; } - InputPropertyExpression* inputProp() const { - return prop_; + Expression* genList() { + return genList_; } const MatchPath& matchPath() const { @@ -71,8 +70,9 @@ class MatchPathPatternExpression final : public Expression { private: std::unique_ptr matchPath_; - InputPropertyExpression* prop_{ - nullptr}; // The column of input stored the result of the expression + // The column of input stored the result of the expression + // The filter apply to each path in result List and generate a new result List. + Expression* genList_{nullptr}; Value result_; }; } // namespace nebula diff --git a/src/common/function/FunctionManager.cpp b/src/common/function/FunctionManager.cpp index d83a0c131b3..d60b8ab0b1a 100644 --- a/src/common/function/FunctionManager.cpp +++ b/src/common/function/FunctionManager.cpp @@ -418,6 +418,7 @@ std::unordered_map> FunctionManager::typ {TypeSignature({Value::Type::STRING}, Value::Type::DURATION), TypeSignature({Value::Type::MAP}, Value::Type::DURATION)}}, {"extract", {TypeSignature({Value::Type::STRING, Value::Type::STRING}, Value::Type::LIST)}}, + {"_nodeid", {TypeSignature({Value::Type::PATH, Value::Type::INT}, Value::Type::INT)}}, }; // static @@ -2728,6 +2729,29 @@ FunctionManager::FunctionManager() { return res; }; } + { + auto &attr = functions_["_nodeid"]; + attr.minArity_ = 2; + attr.maxArity_ = 2; + attr.isAlwaysPure_ = true; + attr.body_ = [](const auto &args) -> Value { + if (!args[0].get().isPath() || !args[1].get().isInt()) { + return Value::kNullBadType; + } + + const auto &p = args[0].get().getPath(); + const std::size_t nodeIndex = args[1].get().getInt(); + if (nodeIndex < 0 || nodeIndex >= (1 + p.steps.size())) { + DLOG(FATAL) << "Out of range node index."; + return Value::kNullBadData; + } + if (nodeIndex == 0) { + return p.src.vid; + } else { + return p.steps[nodeIndex - 1].dst.vid; + } + }; + } } // NOLINT // static diff --git a/src/graph/util/AnonVarGenerator.h b/src/graph/util/AnonVarGenerator.h index 4cd4e07c26e..ad0efcb1694 100644 --- a/src/graph/util/AnonVarGenerator.h +++ b/src/graph/util/AnonVarGenerator.h @@ -29,6 +29,10 @@ class AnonVarGenerator final { return var; } + void createVar(const std::string& var) const { + symTable_->newVariable(var); + } + // Check is variable anonymous // The parser don't allow user name variable started with `_`, // `_` started variable is generated by nebula only. diff --git a/src/graph/util/ParserUtil.cpp b/src/graph/util/ParserUtil.cpp index de0e8c0132a..fe2727add76 100644 --- a/src/graph/util/ParserUtil.cpp +++ b/src/graph/util/ParserUtil.cpp @@ -14,8 +14,8 @@ namespace graph { void ParserUtil::rewriteLC(QueryContext *qctx, ListComprehensionExpression *lc, const std::string &oldVarName) { - const auto &newVarName = qctx->vctx()->anonVarGen()->getVar(); - qctx->ectx()->setValue(newVarName, Value()); + qctx->vctx()->anonVarGen()->createVar(oldVarName); + qctx->ectx()->setValue(oldVarName, Value()); auto *pool = qctx->objPool(); auto matcher = [](const Expression *expr) -> bool { @@ -23,12 +23,12 @@ void ParserUtil::rewriteLC(QueryContext *qctx, expr->kind() == Expression::Kind::kLabelAttribute; }; - auto rewriter = [&, pool, newVarName](const Expression *expr) { + auto rewriter = [&, pool, oldVarName](const Expression *expr) { Expression *ret = nullptr; if (expr->kind() == Expression::Kind::kLabel) { auto *label = static_cast(expr); if (label->name() == oldVarName) { - ret = VariableExpression::make(pool, newVarName, true); + ret = VariableExpression::make(pool, oldVarName, true); } else { ret = label->clone(); } @@ -38,7 +38,7 @@ void ParserUtil::rewriteLC(QueryContext *qctx, if (la->left()->name() == oldVarName) { const auto &value = la->right()->value(); ret = AttributeExpression::make(pool, - VariableExpression::make(pool, newVarName, true), + VariableExpression::make(pool, oldVarName, true), ConstantExpression::make(pool, value)); } else { ret = la->clone(); @@ -48,7 +48,7 @@ void ParserUtil::rewriteLC(QueryContext *qctx, }; lc->setOriginString(lc->toString()); - lc->setInnerVar(newVarName); + lc->setInnerVar(oldVarName); if (lc->hasFilter()) { Expression *filter = lc->filter(); auto *newFilter = RewriteVisitor::transform(filter, matcher, rewriter); diff --git a/src/graph/validator/MatchValidator.cpp b/src/graph/validator/MatchValidator.cpp index 99914c78d14..8fe2f611d45 100644 --- a/src/graph/validator/MatchValidator.cpp +++ b/src/graph/validator/MatchValidator.cpp @@ -9,6 +9,7 @@ #include "graph/util/ExpressionUtils.h" #include "graph/visitor/ExtractGroupSuiteVisitor.h" #include "graph/visitor/RewriteVisitor.h" +#include "graph/visitor/ValidatePatternExpressionVisitor.h" namespace nebula { namespace graph { @@ -1007,6 +1008,9 @@ Status MatchValidator::validateMatchPathExpr( Expression *expr, const std::unordered_map &availableAliases, std::vector &paths) { + auto *pool = qctx_->objPool(); + ValidatePatternExpressionVisitor visitor(pool, vctx_); + expr->accept(&visitor); auto matchPathExprs = ExpressionUtils::collectAll(expr, {Expression::Kind::kMatchPathPattern}); for (auto &matchPathExpr : matchPathExprs) { // auto matchClauseCtx = getContext(); @@ -1021,7 +1025,11 @@ Status MatchValidator::validateMatchPathExpr( auto &matchPath = matchPathExprImpl->matchPath(); auto pathAlias = matchPath.toString(); matchPath.setAlias(new std::string(pathAlias)); - matchPathExprImpl->setInputProp(pathAlias); + if (matchPathExprImpl->genList() == nullptr) { + // Don't done in expression visitor + Expression *genList = InputPropertyExpression::make(pool, pathAlias); + matchPathExprImpl->setGenList(genList); + } paths.emplace_back(); NG_RETURN_IF_ERROR(validatePath(&matchPath, paths.back())); NG_RETURN_IF_ERROR(buildRollUpPathInfo(&matchPath, paths.back())); @@ -1045,6 +1053,10 @@ Status MatchValidator::validateMatchPathExpr( } } for (const auto &node : matchPath.nodes()) { + if (node->variableDefinedSource() == MatchNode::VariableDefinedSource::kExpression) { + // Checked in visitor + continue; + } if (!node->alias().empty()) { const auto find = availableAliases.find(node->alias()); if (find == availableAliases.end()) { diff --git a/src/graph/visitor/CMakeLists.txt b/src/graph/visitor/CMakeLists.txt index c9a5d405852..cdd75ac16fc 100644 --- a/src/graph/visitor/CMakeLists.txt +++ b/src/graph/visitor/CMakeLists.txt @@ -17,6 +17,7 @@ nebula_add_library( VidExtractVisitor.cpp EvaluableExprVisitor.cpp ExtractGroupSuiteVisitor.cpp + ValidatePatternExpressionVisitor.cpp ) nebula_add_library( diff --git a/src/graph/visitor/ExprVisitorImpl.cpp b/src/graph/visitor/ExprVisitorImpl.cpp index 444f39a3f43..b8c710f775d 100644 --- a/src/graph/visitor/ExprVisitorImpl.cpp +++ b/src/graph/visitor/ExprVisitorImpl.cpp @@ -202,8 +202,8 @@ void ExprVisitorImpl::visit(SubscriptRangeExpression *expr) { void ExprVisitorImpl::visit(MatchPathPatternExpression *expr) { DCHECK(ok()) << expr->toString(); - if (expr->inputProp() != nullptr) { - expr->inputProp()->accept(this); + if (expr->genList() != nullptr) { + expr->genList()->accept(this); if (!ok()) { return; } diff --git a/src/graph/visitor/FindVisitor.cpp b/src/graph/visitor/FindVisitor.cpp index 5ce46adee23..a86efc4e0f7 100644 --- a/src/graph/visitor/FindVisitor.cpp +++ b/src/graph/visitor/FindVisitor.cpp @@ -245,8 +245,8 @@ void FindVisitor::visit(SubscriptRangeExpression* expr) { void FindVisitor::visit(MatchPathPatternExpression* expr) { findInCurrentExpr(expr); if (!needFindAll_ && !foundExprs_.empty()) return; - if (expr->inputProp() != nullptr) { - expr->inputProp()->accept(this); + if (expr->genList() != nullptr) { + expr->genList()->accept(this); if (!needFindAll_ && !foundExprs_.empty()) return; } } diff --git a/src/graph/visitor/RewriteSymExprVisitor.cpp b/src/graph/visitor/RewriteSymExprVisitor.cpp index f1eda852ab1..a1266a8a266 100644 --- a/src/graph/visitor/RewriteSymExprVisitor.cpp +++ b/src/graph/visitor/RewriteSymExprVisitor.cpp @@ -337,14 +337,11 @@ void RewriteSymExprVisitor::visit(SubscriptRangeExpression *expr) { } void RewriteSymExprVisitor::visit(MatchPathPatternExpression *expr) { - if (expr->inputProp() != nullptr) { - expr->inputProp()->accept(this); + if (expr->genList() != nullptr) { + expr->genList()->accept(this); if (expr_) { - if (expr_->kind() != Expression::Kind::kInputProperty) { - hasWrongType_ = true; - return; - } - expr->setInputProp(static_cast(expr_)); + expr->setGenList(expr_); + expr_ = nullptr; } } } diff --git a/src/graph/visitor/ValidatePatternExpressionVisitor.cpp b/src/graph/visitor/ValidatePatternExpressionVisitor.cpp new file mode 100644 index 00000000000..a1e5fe2a5c3 --- /dev/null +++ b/src/graph/visitor/ValidatePatternExpressionVisitor.cpp @@ -0,0 +1,70 @@ +// Copyright (c) 2022 vesoft inc. All rights reserved. +// +// This source code is licensed under Apache 2.0 License. +#include "graph/visitor/ValidatePatternExpressionVisitor.h" + +#include "ExprVisitorImpl.h" +#include "graph/context/ValidateContext.h" + +namespace nebula { +namespace graph { + +void ValidatePatternExpressionVisitor::visit(ListComprehensionExpression *expr) { + DCHECK(ok()) << expr->toString(); + // Store current available variables in expression + localVariables_.push_front(expr->innerVar()); + SCOPE_EXIT { + localVariables_.pop_front(); + }; + ExprVisitorImpl::visit(expr); +} + +void ValidatePatternExpressionVisitor::visit(MatchPathPatternExpression *expr) { + DCHECK(ok()) << expr->toString(); + // don't need to process sub-expression + const auto &matchPath = expr->matchPath(); + std::vector nodeFilters; + auto *pathList = InputPropertyExpression::make(pool_, matchPath.toString()); + auto listElementVar = vctx_->anonVarGen()->getVar(); + for (std::size_t i = 0; i < matchPath.nodes().size(); ++i) { + const auto &node = matchPath.nodes()[i]; + if (!node->alias().empty()) { + const auto find = std::find(localVariables_.begin(), localVariables_.end(), node->alias()); + if (find != localVariables_.end()) { + // TODO we should check variable is Node type + // from local variable + node->setVariableDefinedSource(MatchNode::VariableDefinedSource::kExpression); + auto *listElement = VariableExpression::make(pool_, listElementVar); + // Note: this require build path by node pattern order + auto *listElementId = FunctionCallExpression::make( + pool_, + "_nodeid", + {listElement, ConstantExpression::make(pool_, static_cast(i))}); + auto *nodeValue = VariableExpression::make(pool_, node->alias()); + auto *nodeId = FunctionCallExpression::make(pool_, "id", {nodeValue}); + auto *equal = RelationalExpression::makeEQ(pool_, listElementId, nodeId); + nodeFilters.emplace_back(equal); + } + } + } + if (!nodeFilters.empty()) { + auto genList = ListComprehensionExpression::make( + pool_, listElementVar, pathList, andAll(nodeFilters), nullptr); + expr->setGenList(genList); + } +} + +Expression *ValidatePatternExpressionVisitor::andAll(const std::vector &exprs) const { + CHECK(!exprs.empty()); + if (exprs.size() == 1) { + return exprs[0]; + } + auto *expr = exprs[0]; + for (std::size_t i = 1; i < exprs.size(); ++i) { + expr = LogicalExpression::makeAnd(pool_, expr, exprs[i]); + } + return expr; +} + +} // namespace graph +} // namespace nebula diff --git a/src/graph/visitor/ValidatePatternExpressionVisitor.h b/src/graph/visitor/ValidatePatternExpressionVisitor.h new file mode 100644 index 00000000000..72bdf2b5945 --- /dev/null +++ b/src/graph/visitor/ValidatePatternExpressionVisitor.h @@ -0,0 +1,61 @@ +// Copyright (c) 2022 vesoft inc. All rights reserved. +// +// This source code is licensed under Apache 2.0 License. + +#pragma once + +#include "common/expression/Expression.h" +#include "graph/visitor/ExprVisitorImpl.h" + +namespace nebula { +namespace graph { + +class ValidateContext; + +class ValidatePatternExpressionVisitor final : public ExprVisitorImpl { + public: + explicit ValidatePatternExpressionVisitor(ObjectPool *pool, ValidateContext *vctx) + : pool_(pool), vctx_(vctx) {} + + bool ok() const override { + // TODO: delete this interface + return true; + } + + private: + using ExprVisitorImpl::visit; + void visit(ConstantExpression *) override {} + void visit(LabelExpression *) override {} + void visit(UUIDExpression *) override {} + void visit(VariableExpression *) override {} + void visit(VersionedVariableExpression *) override {} + void visit(TagPropertyExpression *) override {} + void visit(LabelTagPropertyExpression *) override {} + void visit(EdgePropertyExpression *) override {} + void visit(InputPropertyExpression *) override {} + void visit(VariablePropertyExpression *) override {} + void visit(DestPropertyExpression *) override {} + void visit(SourcePropertyExpression *) override {} + void visit(EdgeSrcIdExpression *) override {} + void visit(EdgeTypeExpression *) override {} + void visit(EdgeRankExpression *) override {} + void visit(EdgeDstIdExpression *) override {} + void visit(VertexExpression *) override {} + void visit(EdgeExpression *) override {} + void visit(ColumnExpression *) override {} + + void visit(ListComprehensionExpression *expr) override; + // match path pattern expression + void visit(MatchPathPatternExpression *expr) override; + + Expression *andAll(const std::vector &exprs) const; + + private: + ObjectPool *pool_{nullptr}; + ValidateContext *vctx_{nullptr}; + + std::list localVariables_; // local variable defined in List Comprehension +}; + +} // namespace graph +} // namespace nebula diff --git a/src/parser/MatchPath.h b/src/parser/MatchPath.h index 5908da54b05..f46970b2c12 100644 --- a/src/parser/MatchPath.h +++ b/src/parser/MatchPath.h @@ -255,10 +255,26 @@ class MatchNode final { return me; } + enum class VariableDefinedSource { + kUnknown, + kExpression, // from upper expression + kMatchClause, // from previous match clause + }; + + VariableDefinedSource variableDefinedSource() const { + return variableDefinedSource_; + } + + void setVariableDefinedSource(VariableDefinedSource source) { + variableDefinedSource_ = source; + } + private: std::string alias_; std::unique_ptr labels_; MapExpression* props_{nullptr}; + // Only used for pattern expression + VariableDefinedSource variableDefinedSource_{VariableDefinedSource::kUnknown}; }; class MatchPath final { diff --git a/src/parser/parser.yy b/src/parser/parser.yy index 408618a112e..46401a834b4 100644 --- a/src/parser/parser.yy +++ b/src/parser/parser.yy @@ -739,9 +739,6 @@ expression_internal | predicate_expression { $$ = $1; } - | list_comprehension_expression { - $$ = $1; - } | reduce_expression { $$ = $1; } @@ -788,6 +785,9 @@ compound_expression | container_expression { $$ = $1; } + | list_comprehension_expression { + $$ = $1; + } | subscript_expression { $$ = $1; } diff --git a/tests/data/ldbc_v0_3_3/config.yaml b/tests/data/ldbc_v0_3_3/config.yaml index 200afa3d2c7..382c90462f3 100644 --- a/tests/data/ldbc_v0_3_3/config.yaml +++ b/tests/data/ldbc_v0_3_3/config.yaml @@ -37,5 +37,6 @@ schema: | CREATE EDGE IF NOT EXISTS `HAS_TYPE`(); CREATE TAG INDEX message_create_date ON `Message`(`creationDate`(20)); CREATE TAG INDEX person_first_name ON `Person`(`firstName`(20)); + CREATE TAG INDEX index_comment_tag ON `Comment`(); files: diff --git a/tests/tck/features/match/PathExprRefLocalVariable.feature b/tests/tck/features/match/PathExprRefLocalVariable.feature new file mode 100644 index 00000000000..3c3e4b07cbe --- /dev/null +++ b/tests/tck/features/match/PathExprRefLocalVariable.feature @@ -0,0 +1,194 @@ +# Copyright (c) 2022 vesoft inc. All rights reserved. +# +# This source code is licensed under Apache 2.0 License. +Feature: Path expression reference local defined variables + + Background: + Given a graph with space named "nba" + + Scenario: In Where + When executing query: + """ + MATCH (v:player) WHERE [t in [v] | (v)-[:like]->(t)] RETURN v.player.name AS name + """ + Then the result should be, in any order: + | name | + | "Nobody" | + | "Amar'e Stoudemire" | + | "Russell Westbrook" | + | "James Harden" | + | "Kobe Bryant" | + | "Tracy McGrady" | + | "Chris Paul" | + | "Boris Diaw" | + | "LeBron James" | + | "Klay Thompson" | + | "Kristaps Porzingis" | + | "Jonathon Simmons" | + | "Marco Belinelli" | + | "Luka Doncic" | + | "David West" | + | "Tony Parker" | + | "Danny Green" | + | "Rudy Gay" | + | "LaMarcus Aldridge" | + | "Tim Duncan" | + | "Kevin Durant" | + | "Stephen Curry" | + | "Ray Allen" | + | "Tiago Splitter" | + | "DeAndre Jordan" | + | "Paul Gasol" | + | "Aron Baynes" | + | "Cory Joseph" | + | "Vince Carter" | + | "Marc Gasol" | + | "Ricky Rubio" | + | "Ben Simmons" | + | "Giannis Antetokounmpo" | + | "Rajon Rondo" | + | "Manu Ginobili" | + | "Kyrie Irving" | + | "Carmelo Anthony" | + | "Dwyane Wade" | + | "Joel Embiid" | + | "Damian Lillard" | + | "Yao Ming" | + | "Kyle Anderson" | + | "Dejounte Murray" | + | "Blake Griffin" | + | "Steve Nash" | + | "Jason Kidd" | + | "Dirk Nowitzki" | + | "Paul George" | + | "Grant Hill" | + | "Shaquille O'Neal" | + | "JaVale McGee" | + | "Dwight Howard" | + | NULL | + | NULL | + | NULL | + | NULL | + When executing query: + """ + MATCH (v:player) WHERE [t in [v] | (v)-[:like]->(t)] AND (v)-[:serve]->(:team{name: "Spurs"}) RETURN v.player.name AS name + """ + Then the result should be, in any order: + | name | + | "Tracy McGrady" | + | "Boris Diaw" | + | "Jonathon Simmons" | + | "Marco Belinelli" | + | "David West" | + | "Tony Parker" | + | "Danny Green" | + | "Rudy Gay" | + | "LaMarcus Aldridge" | + | "Tim Duncan" | + | "Tiago Splitter" | + | "Paul Gasol" | + | "Aron Baynes" | + | "Cory Joseph" | + | "Manu Ginobili" | + | "Kyle Anderson" | + | "Dejounte Murray" | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[:like]->() WHERE [t in [v] | (v)-->(t)] RETURN v.player.name AS name + """ + Then the result should be, in any order: + | name | + | "Tim Duncan" | + | "Tim Duncan" | + + Scenario: In With + When executing query: + """ + MATCH (v:player), (t:player{name: "Tim Duncan"}) WITH [t in [t] | (v)-[:teammate]->(t)] AS p RETURN p + """ + Then the result should be, in any order: + | p | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[<("Tony Parker" :player{age: 36, name: "Tony Parker"})-[:teammate@0 {end_year: 2016, start_year: 2001}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})>]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[<("Manu Ginobili" :player{age: 41, name: "Manu Ginobili"})-[:teammate@0 {end_year: 2016, start_year: 2002}]->("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})>]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + | [[]] | + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[:like]->(), (t:team {name: "Spurs"}) WITH [t in [t] | (v)-[:serve]->(t)] AS p RETURN p + """ + Then the result should be, in any order: + | p | + | [[<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>]] | + | [[<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>]] | + + Scenario: In Return + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[:like]->(), (t:team {name: "Spurs"}) return [t in [t] | (v)-[:serve]->(t)] AS p + """ + Then the result should be, in any order: + | p | + | [[<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>]] | + | [[<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>]] | + + Scenario: In Unwind + When executing query: + """ + MATCH (v:player{name: 'Tim Duncan'})-[:like]->(), (t:team {name: "Spurs"}) UNWIND [t in [t] | (v)-[:serve]->(t)] AS p RETURN p + """ + Then the result should be, in any order: + | p | + | [<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>] | + | [<("Tim Duncan" :bachelor{name: "Tim Duncan", speciality: "psychology"} :player{age: 42, name: "Tim Duncan"})-[:serve@0 {end_year: 2016, start_year: 1997}]->("Spurs" :team{name: "Spurs"})>] | diff --git a/tests/tck/features/optimizer/PushFilterDownGetNbrsRule.feature b/tests/tck/features/optimizer/PushFilterDownGetNbrsRule.feature index 5988b56d0dd..358b2c23f21 100644 --- a/tests/tck/features/optimizer/PushFilterDownGetNbrsRule.feature +++ b/tests/tck/features/optimizer/PushFilterDownGetNbrsRule.feature @@ -72,10 +72,10 @@ Feature: Push Filter down GetNeighbors rule | "Manu Ginobili" | 95 | | "Tim Duncan" | 95 | And the execution plan should be: - | id | name | dependencies | operator info | - | 0 | Project | 1 | | - | 1 | GetNeighbors | 2 | {"filter": "(like.likeness IN [__VAR_0 IN [95,99] WHERE ($__VAR_0>0)])"} | - | 2 | Start | | | + | id | name | dependencies | operator info | + | 0 | Project | 1 | | + | 1 | GetNeighbors | 2 | {"filter": "(like.likeness IN [v IN [95,99] WHERE ($v>0)])"} | + | 2 | Start | | | When profiling query: """ GO FROM "Tony Parker" OVER like diff --git a/tests/tck/ldbc/interactive_workload/ComplexReads.feature b/tests/tck/ldbc/interactive_workload/ComplexReads.feature index f1b136076b8..dc7321b1be1 100644 --- a/tests/tck/ldbc/interactive_workload/ComplexReads.feature +++ b/tests/tck/ldbc/interactive_workload/ComplexReads.feature @@ -341,16 +341,18 @@ Feature: LDBC Interactive Workload - Complex Reads """ Then a SyntaxError should be raised at runtime: - @skip Scenario: 14. Trusted connection paths # TODO: allShortestPaths + # Modification: ldbc_snb_interactive_impls 0.3.4 modify `length` function to `size` function, + # for the `length` don't accept LIST arguments again When executing query: """ - MATCH path = allShortestPaths((person1:Person {id:$person1Id})-[:KNOWS*..15]-(person2:Person {id:$person2Id})) - WITH nodes(path) AS pathNodes + MATCH `path` = allShortestPaths((person1:Person {id:"123"})-[:KNOWS*..15]-(person2:Person {id:"321"})) + WITH nodes(`path`) AS pathNodes RETURN - extract(n IN pathNodes | n.id) AS personIdsInPath, - reduce(weight=0.0, idx IN range(1,size(pathNodes)-1) | extract(prev IN [pathNodes[idx-1]] | extract(curr IN [pathNodes[idx]] | weight + length((curr)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(:Post)-[:HAS_CREATOR]->(prev))*1.0 + length((prev)<-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]->(:Post)-[:HAS_CREATOR]->(curr))*1.0 + length((prev)-[:HAS_CREATOR]-(:Comment)-[:REPLY_OF]-(:Comment)-[:HAS_CREATOR]-(curr))*0.5) )[0][0]) AS pathWight + [n IN pathNodes | n.id] AS personIdsInPath, + reduce(weight=0.0, idx IN range(1,size(pathNodes)-1) | [prev IN [pathNodes[idx-1]] | [curr IN [pathNodes[idx]] | weight + size((curr)<-[:HAS_CREATOR]-(:`Comment`)-[:REPLY_OF]->(:Post)-[:HAS_CREATOR]->(prev))*1.0 + size((prev)<-[:HAS_CREATOR]-(:`Comment`)-[:REPLY_OF]->(:Post)-[:HAS_CREATOR]->(curr))*1.0 + size((prev)-[:HAS_CREATOR]-(:`Comment`)-[:REPLY_OF]-(:`Comment`)-[:HAS_CREATOR]-(curr))*0.5] ][0][0]) AS pathWight ORDER BY pathWight DESC """ - Then a SyntaxError should be raised at runtime: + Then the result should be, in any order: + | personIdsInPath | pathWight |