Skip to content
Snippets Groups Projects
Unverified Commit 643e177a authored by jimingquan's avatar jimingquan Committed by GitHub
Browse files

add exists (#808)


* add exists

* fix error

* rewite exists

* add test case

* more test case

* rebase & remove header

Co-authored-by: default avatarcpw <13495049+CPWstatic@users.noreply.github.com>
parent c305bfbe
No related branches found
No related tags found
No related merge requests found
Showing
with 376 additions and 194 deletions
......@@ -31,8 +31,6 @@
#include "util/ParserUtil.h"
#include "context/QueryContext.h"
#include "util/SchemaUtil.h"
#include "util/ParserUtil.h"
#include "context/QueryContext.h"
namespace nebula {
......@@ -801,6 +799,12 @@ predicate_expression
$$ = expr;
delete $3;
}
| KW_EXISTS L_PAREN expression R_PAREN {
if ($3->kind() != Expression::Kind::kLabelAttribute && $3->kind() != Expression::Kind::kAttribute) {
throw nebula::GraphParser::syntax_error(@3, "The exists only accept LabelAttribe OR Attribute");
}
$$ = new PredicateExpression(new std::string("exists"), nullptr, $3, nullptr);
}
;
list_comprehension_expression
......
......@@ -14,10 +14,7 @@
namespace nebula {
namespace graph {
bool SequentialPlanner::match(AstContext* astCtx) {
if (astCtx->sentence->kind() == Sentence::Kind::kSequential) {
return true;
}
return false;
return astCtx->sentence->kind() == Sentence::Kind::kSequential;
}
StatusOr<SubPlan> SequentialPlanner::transform(AstContext* astCtx) {
......
......@@ -16,11 +16,7 @@
namespace nebula {
namespace graph {
bool MatchPlanner::match(AstContext* astCtx) {
if (astCtx->sentence->kind() == Sentence::Kind::kMatch) {
return true;
} else {
return false;
}
return astCtx->sentence->kind() == Sentence::Kind::kMatch;
}
StatusOr<SubPlan> MatchPlanner::transform(AstContext* astCtx) {
......
......@@ -11,6 +11,7 @@ nebula_add_library(
IndexUtil.cpp
ZoneUtil.cpp
ToJson.cpp
ParserUtil.cpp
)
nebula_add_library(
......
/* Copyright (c) 2021 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
*/
#include "util/ParserUtil.h"
#include "common/base/Status.h"
#include "common/base/StatusOr.h"
namespace nebula {
namespace graph {
// static
bool ParserUtil::isLabel(const Expression *expr) {
return expr->kind() == Expression::Kind::kLabel ||
expr->kind() == Expression::Kind::kLabelAttribute;
}
// static
void ParserUtil::rewriteLC(QueryContext *qctx,
ListComprehensionExpression *lc,
const std::string &oldVarName) {
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [&oldVarName, &newVarName](const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret = new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
lc->setOriginString(new std::string(lc->makeString()));
lc->setInnerVar(new std::string(newVarName));
if (lc->hasFilter()) {
Expression *filter = lc->filter();
Expression *newFilter = nullptr;
if (isLabel(filter)) {
newFilter = rewriter(filter);
} else {
newFilter = filter->clone().release();
newFilter->accept(&visitor);
}
lc->setFilter(newFilter);
}
if (lc->hasMapping()) {
Expression *mapping = lc->mapping();
Expression *newMapping = nullptr;
if (isLabel(mapping)) {
newMapping = rewriter(mapping);
} else {
newMapping = mapping->clone().release();
newMapping->accept(&visitor);
}
lc->setMapping(newMapping);
}
}
// static
void ParserUtil::rewritePred(QueryContext *qctx,
PredicateExpression *pred,
const std::string &oldVarName) {
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [&oldVarName, &newVarName](const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret = new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
pred->setOriginString(new std::string(pred->makeString()));
pred->setInnerVar(new std::string(newVarName));
Expression *filter = pred->filter();
Expression *newFilter = nullptr;
if (isLabel(filter)) {
newFilter = rewriter(filter);
} else {
newFilter = filter->clone().release();
newFilter->accept(&visitor);
}
pred->setFilter(newFilter);
}
// static
void ParserUtil::rewriteReduce(QueryContext *qctx,
ReduceExpression *reduce,
const std::string &oldAccName,
const std::string &oldVarName) {
const auto &newAccName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newAccName, Value());
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [oldAccName, newAccName, oldVarName, newVarName](const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldAccName) {
ret = new VariableExpression(new std::string(newAccName));
} else if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldAccName) {
const auto &value = la->right()->value();
ret = new AttributeExpression(new VariableExpression(new std::string(newAccName)),
new ConstantExpression(value));
} else if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret = new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
reduce->setOriginString(new std::string(reduce->makeString()));
reduce->setAccumulator(new std::string(newAccName));
reduce->setInnerVar(new std::string(newVarName));
Expression *mapping = reduce->mapping();
Expression *newMapping = nullptr;
if (isLabel(mapping)) {
newMapping = rewriter(mapping);
} else {
newMapping = mapping->clone().release();
newMapping->accept(&visitor);
}
reduce->setMapping(newMapping);
}
} // namespace graph
} // namespace nebula
/* Copyright (c) 2020 vesoft inc. All rights reserved.
/* Copyright (c) 2021 vesoft inc. All rights reserved.
*
* This source code is licensed under Apache 2.0 License,
* attached with Common Clause Condition 1.0, found in the LICENSES directory.
......@@ -20,168 +20,19 @@ class ParserUtil final {
public:
ParserUtil() = delete;
static bool isLabel(const Expression *expr) {
return expr->kind() == Expression::Kind::kLabel ||
expr->kind() == Expression::Kind::kLabelAttribute;
}
static bool isLabel(const Expression *expr);
static void rewriteLC(QueryContext *qctx,
ListComprehensionExpression *lc,
const std::string &oldVarName) {
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [&oldVarName, &newVarName](const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret =
new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
lc->setOriginString(new std::string(lc->makeString()));
lc->setInnerVar(new std::string(newVarName));
if (lc->hasFilter()) {
Expression *filter = lc->filter();
Expression *newFilter = nullptr;
if (isLabel(filter)) {
newFilter = rewriter(filter);
} else {
newFilter = filter->clone().release();
newFilter->accept(&visitor);
}
lc->setFilter(newFilter);
}
if (lc->hasMapping()) {
Expression *mapping = lc->mapping();
Expression *newMapping = nullptr;
if (isLabel(mapping)) {
newMapping = rewriter(mapping);
} else {
newMapping = mapping->clone().release();
newMapping->accept(&visitor);
}
lc->setMapping(newMapping);
}
}
const std::string &oldVarName);
static void rewritePred(QueryContext *qctx,
PredicateExpression *pred,
const std::string &oldVarName) {
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [&oldVarName, &newVarName](const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret =
new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
pred->setOriginString(new std::string(pred->makeString()));
pred->setInnerVar(new std::string(newVarName));
Expression *filter = pred->filter();
Expression *newFilter = nullptr;
if (isLabel(filter)) {
newFilter = rewriter(filter);
} else {
newFilter = filter->clone().release();
newFilter->accept(&visitor);
}
pred->setFilter(newFilter);
}
const std::string &oldVarName);
static void rewriteReduce(QueryContext *qctx,
ReduceExpression *reduce,
const std::string &oldAccName,
const std::string &oldVarName) {
const auto &newAccName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newAccName, Value());
const auto &newVarName = qctx->vctx()->anonVarGen()->getVar();
qctx->ectx()->setValue(newVarName, Value());
auto rewriter = [oldAccName, newAccName, oldVarName, newVarName](
const Expression *expr) {
Expression *ret = nullptr;
if (expr->kind() == Expression::Kind::kLabel) {
auto *label = static_cast<const LabelExpression *>(expr);
if (*label->name() == oldAccName) {
ret = new VariableExpression(new std::string(newAccName));
} else if (*label->name() == oldVarName) {
ret = new VariableExpression(new std::string(newVarName));
} else {
ret = label->clone().release();
}
} else {
DCHECK(expr->kind() == Expression::Kind::kLabelAttribute);
auto *la = static_cast<const LabelAttributeExpression *>(expr);
if (*la->left()->name() == oldAccName) {
const auto &value = la->right()->value();
ret =
new AttributeExpression(new VariableExpression(new std::string(newAccName)),
new ConstantExpression(value));
} else if (*la->left()->name() == oldVarName) {
const auto &value = la->right()->value();
ret =
new AttributeExpression(new VariableExpression(new std::string(newVarName)),
new ConstantExpression(value));
} else {
ret = la->clone().release();
}
}
return ret;
};
RewriteMatchLabelVisitor visitor(rewriter);
reduce->setOriginString(new std::string(reduce->makeString()));
reduce->setAccumulator(new std::string(newAccName));
reduce->setInnerVar(new std::string(newVarName));
Expression *mapping = reduce->mapping();
Expression *newMapping = nullptr;
if (isLabel(mapping)) {
newMapping = rewriter(mapping);
} else {
newMapping = mapping->clone().release();
newMapping->accept(&visitor);
}
reduce->setMapping(newMapping);
}
const std::string &oldVarName);
};
} // namespace graph
......
......@@ -343,9 +343,8 @@ std::vector<std::string> Validator::deduceColNames(const YieldColumns* cols) con
std::string Validator::deduceColName(const YieldColumn* col) const {
if (col->alias() != nullptr) {
return *col->alias();
} else {
return col->toString();
}
return col->toString();
}
StatusOr<Value::Type> Validator::deduceExprType(const Expression* expr) const {
......
......@@ -160,7 +160,9 @@ void CollectAllExprsVisitor::visit(ListComprehensionExpression* expr) {
void CollectAllExprsVisitor::visit(PredicateExpression *expr) {
collectExpr(expr);
expr->collection()->accept(this);
expr->filter()->accept(this);
if (expr->hasFilter()) {
expr->filter()->accept(this);
}
}
void CollectAllExprsVisitor::visit(ReduceExpression *expr) {
......
......@@ -619,8 +619,12 @@ void DeduceTypeVisitor::visit(CaseExpression *expr) {
}
void DeduceTypeVisitor::visit(PredicateExpression *expr) {
expr->filter()->accept(this);
if (!ok()) return;
if (expr->hasFilter()) {
expr->filter()->accept(this);
if (!ok()) {
return;
}
}
expr->collection()->accept(this);
if (!ok()) return;
......
......@@ -149,8 +149,12 @@ void ExprVisitorImpl::visit(PredicateExpression *expr) {
DCHECK(ok());
expr->collection()->accept(this);
if (!ok()) return;
expr->filter()->accept(this);
if (!ok()) return;
if (expr->hasFilter()) {
expr->filter()->accept(this);
if (!ok()) {
return;
}
}
}
void ExprVisitorImpl::visit(ListComprehensionExpression *expr) {
......
......@@ -92,7 +92,9 @@ void FindAnyExprVisitor::visit(PredicateExpression *expr) {
if (found_) return;
expr->collection()->accept(this);
if (found_) return;
expr->filter()->accept(this);
if (expr->hasFilter()) {
expr->filter()->accept(this);
}
}
void FindAnyExprVisitor::visit(ReduceExpression *expr) {
......
......@@ -391,12 +391,14 @@ void FoldConstantExprVisitor::visit(PredicateExpression *expr) {
canBeFolded = false;
}
}
if (!isConstant(expr->filter())) {
expr->filter()->accept(this);
if (canBeFolded_) {
expr->setFilter(fold(expr->filter()));
} else {
canBeFolded = false;
if (expr->hasFilter()) {
if (!isConstant(expr->filter())) {
expr->filter()->accept(this);
if (canBeFolded_) {
expr->setFilter(fold(expr->filter()));
} else {
canBeFolded = false;
}
}
}
canBeFolded_ = canBeFolded;
......
......@@ -279,9 +279,11 @@ void RewriteInputPropVisitor::visit(PredicateExpression* expr) {
if (ok()) {
expr->setCollection(result_.release());
}
expr->filter()->accept(this);
if (ok()) {
expr->setFilter(result_.release());
if (expr->hasFilter()) {
expr->filter()->accept(this);
if (ok()) {
expr->setFilter(result_.release());
}
}
}
......
......@@ -152,11 +152,13 @@ void RewriteLabelAttrVisitor::visit(PredicateExpression* expr) {
} else {
expr->collection()->accept(this);
}
if (isLabelAttrExpr(expr->filter())) {
auto newExpr = static_cast<LabelAttributeExpression*>(expr->filter());
expr->setFilter(createExpr(newExpr));
} else {
expr->filter()->accept(this);
if (expr->hasFilter()) {
if (isLabelAttrExpr(expr->filter())) {
auto newExpr = static_cast<LabelAttributeExpression*>(expr->filter());
expr->setFilter(createExpr(newExpr));
} else {
expr->filter()->accept(this);
}
}
}
......
......@@ -157,10 +157,12 @@ void RewriteMatchLabelVisitor::visit(PredicateExpression *expr) {
} else {
expr->collection()->accept(this);
}
if (isLabel(expr->filter())) {
expr->setFilter(rewriter_(expr->filter()));
} else {
expr->filter()->accept(this);
if (expr->hasFilter()) {
if (isLabel(expr->filter())) {
expr->setFilter(rewriter_(expr->filter()));
} else {
expr->filter()->accept(this);
}
}
}
......
......@@ -292,9 +292,11 @@ void RewriteSymExprVisitor::visit(PredicateExpression *expr) {
if (expr_) {
expr->setCollection(expr_.release());
}
expr->filter()->accept(this);
if (expr_) {
expr->setFilter(expr_.release());
if (expr->hasFilter()) {
expr->filter()->accept(this);
if (expr_) {
expr->setFilter(expr_.release());
}
}
}
......
......@@ -355,6 +355,57 @@ Feature: Basic match
| p |
| <("LeBron James")-[:like@0]->("Ray Allen")-[:like@0]->("Rajon Rondo")> |
Scenario: exists
When executing query:
"""
MATCH (:player{name:"Tony Parker"})-[r]->() where exists(r.likeness) return r, exists({a:12}.a)
"""
Then the result should be, in any order, with relax comparison:
| r | exists({a:12}.a) |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] | true |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] | true |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | true |
When executing query:
"""
MATCH (:player{name:"Tony Parker"})-[r]->(m) where exists(m.likeness) return r, exists({a:12}.a)
"""
Then the result should be, in any order, with relax comparison:
| r | exists({a:12}.a) |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.abc) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
| [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] |
| [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] |
| [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] |
| [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] |
| [:serve "Tony Parker"->"Hornets" @0 {end_year: 2019, start_year: 2018}] |
| [:serve "Tony Parker"->"Spurs" @0 {end_year: 2018, start_year: 1999}] |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.ab) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
| [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] |
| [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] |
| [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] |
| [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] |
Scenario: No return
When executing query:
"""
......
......@@ -382,6 +382,57 @@ Feature: Basic match
"""
Then a SemanticError should be raised at runtime: Match clause is not supported to be followed by other cypher clauses
Scenario: exists
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->() where exists(r.likeness) return r, exists({a:12}.a)
"""
Then the result should be, in any order, with relax comparison:
| r | exists({a:12}.a) |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] | true |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] | true |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] | true |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.likeness) return r, exists({a:12}.a)
"""
Then the result should be, in any order, with relax comparison:
| r | exists({a:12}.a) |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.abc) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
| [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] |
| [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] |
| [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] |
| [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] |
| [:serve "Tony Parker"->"Hornets" @0 {end_year: 2019, start_year: 2018}] |
| [:serve "Tony Parker"->"Spurs" @0 {end_year: 2018, start_year: 1999}] |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists({abc:123}.ab) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
When executing query:
"""
match (:player{name:"Tony Parker"})-[r]->(m) where exists(m.age) return r
"""
Then the result should be, in any order, with relax comparison:
| r |
| [:teammate "Tony Parker"->"Kyle Anderson" @0 {end_year: 2016, start_year: 2014}] |
| [:teammate "Tony Parker"->"LaMarcus Aldridge" @0 {end_year: 2018, start_year: 2015}] |
| [:teammate "Tony Parker"->"Manu Ginobili" @0 {end_year: 2018, start_year: 2002}] |
| [:teammate "Tony Parker"->"Tim Duncan" @0 {end_year: 2016, start_year: 2001}] |
| [:like "Tony Parker"->"LaMarcus Aldridge" @0 {likeness: 90}] |
| [:like "Tony Parker"->"Manu Ginobili" @0 {likeness: 95}] |
| [:like "Tony Parker"->"Tim Duncan" @0 {likeness: 95}] |
Scenario: No return
When executing query:
"""
......
......@@ -267,3 +267,37 @@ Feature: With clause and Unwind clause
| ("Tony Parker" :player{age: 36, name: "Tony Parker"}) | 36 |
| ("Carmelo Anthony" :player{age: 34, name: "Carmelo Anthony"}) | 34 |
| ("LeBron James" :player{age: 34, name: "LeBron James"}) | 34 |
Scenario: with exists
When executing query:
"""
WITH {abc:123} AS m, "hello" AS b
RETURN exists(m.abc), b
"""
Then the result should be, in any order, with relax comparison:
| exists(m.abc) | b |
| true | "hello" |
When executing query:
"""
WITH {abc:123} AS m, "hello" AS b
RETURN exists(m.abc), exists(m.a), exists({label:"hello"}.label) as t, exists({hello:123}.a)
"""
Then the result should be, in any order, with relax comparison:
| exists(m.abc) | exists(m.a) | t | exists({hello:123}.a) |
| true | false | true | false |
When executing query:
"""
WITH [1,2,3] AS m
RETURN exists(m.abc)
"""
Then the result should be, in any order, with relax comparison:
| exists(m.abc) |
| BAD_TYPE |
When executing query:
"""
WITH null AS m
RETURN exists(m.abc), exists((null).abc)
"""
Then the result should be, in any order, with relax comparison:
| exists(m.abc) | exists(NULL.abc) |
| NULL | NULL |
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment