Skip to content
Snippets Groups Projects
Unverified Commit 1a249e3d authored by Shylock Hg's avatar Shylock Hg Committed by GitHub
Browse files

Enhance the match by id filter eval id (#500)


* Enhance the match by id filter eval id.

* Address @yixinglu's comment that for or logical with other Vertices Seek is can't deduce.

* Format the feature file.

* Support multiple id call in filter. And check the node alias.

* Move the vid extract to visitor.

* Fix the compiler warning.

* Fix the size check.

* Fix the variable overlap.

* Fix the cases.

* Add int vid cases.

* Rebase.

* Remove the duplicate definition.

* Rebase.

* Add some cases.

* Check alias by anonymous

* Rebase.

Co-authored-by: default avatarcpw <13495049+CPWstatic@users.noreply.github.com>
Co-authored-by: default avatarYee <2520865+yixinglu@users.noreply.github.com>
parent d053d91d
No related branches found
No related tags found
No related merge requests found
......@@ -165,7 +165,7 @@ struct NodeContext final : PatternContext {
// Output fields
ScanInfo scanInfo;
const Expression* ids{nullptr};
List ids;
// initialize start expression in project node
std::unique_ptr<Expression> initialExpr;
};
......
......@@ -90,8 +90,8 @@ Status MatchClausePlanner::findStarts(MatchClauseContext* matchClauseCtx,
}
}
if (!foundStart) {
return Status::Error("Can't solve the start vids from the sentence: %s",
matchClauseCtx->sentence->toString().c_str());
return Status::SemanticError("Can't solve the start vids from the sentence: %s",
matchClauseCtx->sentence->toString().c_str());
}
return Status::OK();
......
......@@ -8,101 +8,69 @@
#include "planner/Logic.h"
#include "planner/match/MatchSolver.h"
#include "visitor/VidExtractVisitor.h"
#include "util/ExpressionUtils.h"
#include "util/SchemaUtil.h"
namespace nebula {
namespace graph {
bool VertexIdSeek::matchEdge(EdgeContext* edgeCtx) {
bool VertexIdSeek::matchEdge(EdgeContext *edgeCtx) {
UNUSED(edgeCtx);
return false;
}
StatusOr<SubPlan> VertexIdSeek::transformEdge(EdgeContext* edgeCtx) {
StatusOr<SubPlan> VertexIdSeek::transformEdge(EdgeContext *edgeCtx) {
UNUSED(edgeCtx);
return Status::Error("Unimplement for edge pattern.");
return Status::Error("Unimplemented for edge pattern.");
}
bool VertexIdSeek::matchNode(NodeContext* nodeCtx) {
auto* matchClauseCtx = nodeCtx->matchClauseCtx;
auto where = matchClauseCtx->where.get();
if (where == nullptr || where->filter == nullptr) {
bool VertexIdSeek::matchNode(NodeContext *nodeCtx) {
auto &node = *nodeCtx->info;
auto *matchClauseCtx = nodeCtx->matchClauseCtx;
if (matchClauseCtx->where == nullptr || matchClauseCtx->where->filter == nullptr) {
return false;
}
DCHECK(nodeCtx->info != nullptr);
auto alias = nodeCtx->info->alias;
if (alias == nullptr) {
if (node.alias == nullptr || node.anonymous) {
// require one named node
return false;
}
auto vidResult = extractVids(*alias, where->filter.get());
if (!vidResult.ok()) {
VidExtractVisitor vidExtractVisitor;
matchClauseCtx->where->filter->accept(&vidExtractVisitor);
auto vidResult = vidExtractVisitor.moveVidPattern();
if (vidResult.spec != VidExtractVisitor::VidPattern::Special::kInUsed) {
return false;
}
nodeCtx->ids = vidResult.value();
return true;
}
static Status checkIDFun(const std::string& alias, const FunctionCallExpression* fCallExpr) {
if (*fCallExpr->name() != "id") {
return Status::Error("Only id function is allowed");
}
const auto& args = DCHECK_NOTNULL(fCallExpr->args())->args();
if (args.size() != 1U) {
return Status::Error("Invalid parameter number of id function");
}
auto arg = args.back().get();
if (arg->toString() != alias) {
return Status::Error("Invalid parameter `%s' of id function", alias.c_str());
}
return Status::OK();
}
StatusOr<const Expression*> VertexIdSeek::extractVids(const std::string& alias,
const Expression* filter) {
if (filter->kind() == Expression::Kind::kRelIn) {
const auto* inExpr = static_cast<const RelationalExpression*>(filter);
if (inExpr->left()->kind() != Expression::Kind::kFunctionCall ||
inExpr->right()->kind() != Expression::Kind::kConstant) {
return Status::Error("Not supported expression.");
}
const auto* fCallExpr = static_cast<const FunctionCallExpression*>(inExpr->left());
NG_RETURN_IF_ERROR(checkIDFun(alias, fCallExpr));
auto* constExpr = const_cast<Expression*>(inExpr->right());
return constExpr;
} else if (filter->kind() == Expression::Kind::kRelEQ) {
const auto* eqExpr = static_cast<const RelationalExpression*>(filter);
if (eqExpr->left()->kind() != Expression::Kind::kFunctionCall ||
eqExpr->right()->kind() != Expression::Kind::kConstant) {
return Status::Error("Not supported expression.");
for (auto &nodeVid : vidResult.nodes) {
if (nodeVid.second.kind == VidExtractVisitor::VidPattern::Vids::Kind::kIn) {
if (nodeVid.first == *node.alias) {
nodeCtx->ids = std::move(nodeVid.second.vids);
return true;
}
}
const auto* fCallExpr = static_cast<const FunctionCallExpression*>(eqExpr->left());
NG_RETURN_IF_ERROR(checkIDFun(alias, fCallExpr));
auto* constExpr = const_cast<Expression*>(eqExpr->right());
return constExpr;
} else {
return Status::Error("Not supported expression.");
}
return false;
}
std::pair<std::string, Expression*> VertexIdSeek::listToAnnoVarVid(QueryContext* qctx,
const List& list) {
std::pair<std::string, Expression *> VertexIdSeek::listToAnnoVarVid(QueryContext *qctx,
const List &list) {
auto input = qctx->vctx()->anonVarGen()->getVar();
DataSet vids({kVid});
QueryExpressionContext dummy;
for (auto& v : list.values) {
for (auto &v : list.values) {
vids.emplace_back(Row({std::move(v)}));
}
qctx->ectx()->setResult(input, ResultBuilder().value(Value(std::move(vids))).finish());
auto* src = new VariablePropertyExpression(new std::string(input), new std::string(kVid));
return std::pair<std::string, Expression*>(input, src);
auto *src = new VariablePropertyExpression(new std::string(input), new std::string(kVid));
return std::pair<std::string, Expression *>(input, src);
}
std::pair<std::string, Expression*> VertexIdSeek::constToAnnoVarVid(QueryContext* qctx,
const Value& v) {
std::pair<std::string, Expression *> VertexIdSeek::constToAnnoVarVid(QueryContext *qctx,
const Value &v) {
auto input = qctx->vctx()->anonVarGen()->getVar();
DataSet vids({kVid});
QueryExpressionContext dummy;
......@@ -110,23 +78,17 @@ std::pair<std::string, Expression*> VertexIdSeek::constToAnnoVarVid(QueryContext
qctx->ectx()->setResult(input, ResultBuilder().value(Value(std::move(vids))).finish());
auto* src = new VariablePropertyExpression(new std::string(input), new std::string(kVid));
return std::pair<std::string, Expression*>(input, src);
auto *src = new VariablePropertyExpression(new std::string(input), new std::string(kVid));
return std::pair<std::string, Expression *>(input, src);
}
StatusOr<SubPlan> VertexIdSeek::transformNode(NodeContext* nodeCtx) {
StatusOr<SubPlan> VertexIdSeek::transformNode(NodeContext *nodeCtx) {
SubPlan plan;
auto* matchClauseCtx = nodeCtx->matchClauseCtx;
auto* qctx = matchClauseCtx->qctx;
auto *matchClauseCtx = nodeCtx->matchClauseCtx;
auto *qctx = matchClauseCtx->qctx;
QueryExpressionContext dummy;
const auto& value = const_cast<Expression*>(nodeCtx->ids)->eval(dummy);
std::pair<std::string, Expression*> vidsResult;
if (value.isList()) {
vidsResult = listToAnnoVarVid(qctx, value.getList());
} else {
vidsResult = constToAnnoVarVid(qctx, value);
}
std::pair<std::string, Expression *> vidsResult = listToAnnoVarVid(qctx, nodeCtx->ids);
auto* passThrough = PassThroughNode::make(qctx, nullptr);
passThrough->setOutputVar(vidsResult.first);
......@@ -137,5 +99,6 @@ StatusOr<SubPlan> VertexIdSeek::transformNode(NodeContext* nodeCtx) {
nodeCtx->initialExpr = std::unique_ptr<Expression>(vidsResult.second);
return plan;
}
} // namespace graph
} // namespace nebula
} // namespace graph
} // namespace nebula
......@@ -19,6 +19,7 @@ nebula_add_library(
RewriteInputPropVisitor.cpp
RewriteSymExprVisitor.cpp
RewriteMatchLabelVisitor.cpp
VidExtractVisitor.cpp
)
nebula_add_subdirectory(test)
/* Copyright (c) 2020 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 "visitor/VidExtractVisitor.h"
namespace nebula {
namespace graph {
/*static*/ VidExtractVisitor::VidPattern VidExtractVisitor::intersect(VidPattern &&left,
VidPattern &&right) {
DCHECK(left.spec == VidPattern::Special::kInUsed);
DCHECK(right.spec == VidPattern::Special::kInUsed);
VidPattern v{VidPattern::Special::kInUsed, {std::move(left.nodes)}};
for (auto &node : right.nodes) {
DCHECK(node.second.kind == VidPattern::Vids::Kind::kIn);
auto find = v.nodes.find(node.first);
if (find == v.nodes.end()) {
v.nodes.emplace(std::move(*find));
} else {
std::sort(find->second.vids.values.begin(), find->second.vids.values.end());
std::sort(node.second.vids.values.begin(), node.second.vids.values.end());
std::vector<Value> intersection;
std::set_intersection(find->second.vids.values.begin(),
find->second.vids.values.end(),
node.second.vids.values.begin(),
node.second.vids.values.end(),
std::back_inserter(intersection));
find->second.vids.values = std::move(intersection);
}
}
return v;
}
/*static*/ VidExtractVisitor::VidPattern VidExtractVisitor::intersect(
VidPattern &&left,
std::pair<std::string, VidPattern::Vids> &&right) {
auto find = left.nodes.find(right.first);
if (find == left.nodes.end()) {
left.nodes.emplace(std::move(right));
} else {
std::sort(find->second.vids.values.begin(), find->second.vids.values.end());
std::sort(right.second.vids.values.begin(), right.second.vids.values.end());
std::vector<Value> values;
std::set_intersection(find->second.vids.values.begin(),
find->second.vids.values.end(),
right.second.vids.values.begin(),
right.second.vids.values.end(),
std::back_inserter(values));
find->second.vids.values = std::move(values);
}
return std::move(left);
}
/*static*/ VidExtractVisitor::VidPattern VidExtractVisitor::intersect(
std::pair<std::string, VidPattern::Vids> &&left,
std::pair<std::string, VidPattern::Vids> &&right) {
VidPattern v{VidPattern::Special::kInUsed, {}};
if (left.first != right.first) {
v.nodes.emplace(std::move(left));
v.nodes.emplace(std::move(right));
} else {
std::sort(left.second.vids.values.begin(), left.second.vids.values.end());
std::sort(right.second.vids.values.begin(), right.second.vids.values.end());
std::vector<Value> values;
std::set_intersection(left.second.vids.values.begin(),
left.second.vids.values.end(),
right.second.vids.values.begin(),
right.second.vids.values.end(),
std::back_inserter(values));
v.nodes[left.first].kind = VidPattern::Vids::Kind::kIn;
v.nodes[left.first].vids.values.insert(v.nodes[left.first].vids.values.end(),
std::make_move_iterator(values.begin()),
std::make_move_iterator(values.end()));
}
return v;
}
void VidExtractVisitor::visit(ConstantExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(UnaryExpression *expr) {
if (expr->kind() == Expression::Kind::kUnaryNot) {
// const auto *expr = static_cast<const UnaryExpression *>(expr);
expr->operand()->accept(this);
auto operandResult = moveVidPattern();
if (operandResult.spec == VidPattern::Special::kInUsed) {
for (auto &node : operandResult.nodes) {
switch (node.second.kind) {
case VidPattern::Vids::Kind::kOtherSource:
break;
case VidPattern::Vids::Kind::kIn:
node.second.kind = VidPattern::Vids::Kind::kNotIn;
break;
case VidPattern::Vids::Kind::kNotIn:
node.second.kind = VidPattern::Vids::Kind::kIn;
break;
}
}
}
vidPattern_ = std::move(operandResult);
} else {
vidPattern_ = VidPattern{};
}
}
void VidExtractVisitor::visit(TypeCastingExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(LabelExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(LabelAttributeExpression *expr) {
if (expr->kind() == Expression::Kind::kLabelAttribute) {
const auto *labelExpr = static_cast<const LabelAttributeExpression *>(expr);
vidPattern_ = VidPattern{
VidPattern::Special::kInUsed,
{{labelExpr->left()->toString(), {VidPattern::Vids::Kind::kOtherSource, {}}}}};
} else {
vidPattern_ = VidPattern{};
}
}
void VidExtractVisitor::visit(ArithmeticExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(RelationalExpression *expr) {
if (expr->kind() == Expression::Kind::kRelIn) {
// const auto *inExpr = static_cast<const RelationalExpression *>(expr);
if (expr->left()->kind() == Expression::Kind::kLabelAttribute) {
const auto *labelExpr = static_cast<const LabelAttributeExpression *>(expr->left());
const auto &label = labelExpr->left()->toString();
vidPattern_ = VidPattern{VidPattern::Special::kInUsed,
{{label, {VidPattern::Vids::Kind::kOtherSource, {}}}}};
return;
}
if (expr->left()->kind() != Expression::Kind::kFunctionCall ||
expr->right()->kind() != Expression::Kind::kConstant) {
vidPattern_ = VidPattern{};
return;
}
const auto *fCallExpr = static_cast<const FunctionCallExpression *>(expr->left());
if (*fCallExpr->name() != "id" && fCallExpr->args()->numArgs() != 1 &&
fCallExpr->args()->args().front()->kind() != Expression::Kind::kLabel) {
vidPattern_ = VidPattern{};
return;
}
const auto *constExpr = static_cast<const ConstantExpression *>(expr->right());
if (constExpr->value().type() != Value::Type::LIST) {
vidPattern_ = VidPattern{};
return;
}
vidPattern_ = VidPattern{VidPattern::Special::kInUsed,
{{fCallExpr->args()->args().front()->toString(),
{VidPattern::Vids::Kind::kIn, constExpr->value().getList()}}}};
return;
} else if (expr->kind() == Expression::Kind::kRelEQ) {
// const auto *eqExpr = static_cast<const RelationalExpression *>(expr);
if (expr->left()->kind() == Expression::Kind::kLabelAttribute) {
const auto *labelExpr = static_cast<const LabelAttributeExpression *>(expr->left());
const auto &label = labelExpr->left()->toString();
vidPattern_ = VidPattern{VidPattern::Special::kInUsed,
{{label, {VidPattern::Vids::Kind::kOtherSource, {}}}}};
return;
}
if (expr->left()->kind() != Expression::Kind::kFunctionCall ||
expr->right()->kind() != Expression::Kind::kConstant) {
vidPattern_ = VidPattern{};
return;
}
const auto *fCallExpr = static_cast<const FunctionCallExpression *>(expr->left());
if (*fCallExpr->name() != "id" && fCallExpr->args()->numArgs() != 1 &&
fCallExpr->args()->args().front()->kind() != Expression::Kind::kLabel) {
vidPattern_ = VidPattern{};
return;
}
const auto *constExpr = static_cast<const ConstantExpression *>(expr->right());
if (!SchemaUtil::isValidVid(constExpr->value())) {
vidPattern_ = VidPattern{};
return;
}
vidPattern_ = VidPattern{VidPattern::Special::kInUsed,
{{fCallExpr->args()->args().front()->toString(),
{VidPattern::Vids::Kind::kIn, List({constExpr->value()})}}}};
return;
} else {
vidPattern_ = VidPattern{};
return;
}
}
void VidExtractVisitor::visit(SubscriptExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(AttributeExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(LogicalExpression *expr) {
if (expr->kind() == Expression::Kind::kLogicalAnd) {
// const auto *expr = static_cast<const LogicalExpression *>(expr);
std::vector<VidPattern> operandsResult;
operandsResult.reserve(expr->operands().size());
for (const auto &operand : expr->operands()) {
// operandsResult.emplace_back(reverseEvalVids(operand.get()));
operand->accept(this);
operandsResult.emplace_back(moveVidPattern());
}
VidPattern inResult{VidPattern::Special::kInUsed, {}};
VidPattern notInResult{VidPattern::Special::kInUsed, {}};
for (auto &operandResult : operandsResult) {
if (operandResult.spec == VidPattern::Special::kInUsed) {
for (auto &node : operandResult.nodes) {
if (node.second.kind == VidPattern::Vids::Kind::kNotIn) {
notInResult.nodes[node.first].vids.values.insert(
notInResult.nodes[node.first].vids.values.end(),
std::make_move_iterator(node.second.vids.values.begin()),
std::make_move_iterator(node.second.vids.values.end()));
}
}
}
}
// intersect all in list
std::vector<std::pair<std::string, VidPattern::Vids>> inOperandsResult;
for (auto &operandResult : operandsResult) {
if (operandResult.spec == VidPattern::Special::kInUsed) {
for (auto &node : operandResult.nodes) {
if (node.second.kind == VidPattern::Vids::Kind::kIn) {
inOperandsResult.emplace_back(std::move(node));
}
}
}
}
if (inOperandsResult.empty()) {
// noting
} else if (inOperandsResult.size() == 1) {
inResult.nodes.emplace(std::move(inOperandsResult.front()));
} else {
inResult = intersect(std::move(inOperandsResult[0]), std::move(inOperandsResult[1]));
for (std::size_t i = 2; i < inOperandsResult.size(); ++i) {
inResult = intersect(std::move(inResult), std::move(inOperandsResult[i]));
}
}
// remove that not in item
for (auto &node : inResult.nodes) {
auto find = notInResult.nodes.find(node.first);
if (find != notInResult.nodes.end()) {
List removeNotIn;
for (auto &v : node.second.vids.values) {
if (std::find(find->second.vids.values.begin(),
find->second.vids.values.end(),
v) == find->second.vids.values.end()) {
removeNotIn.emplace_back(std::move(v));
}
}
node.second.vids = std::move(removeNotIn);
}
}
vidPattern_ = std::move(inResult);
return;
} else if (expr->kind() == Expression::Kind::kLogicalOr) {
// const auto *andExpr = static_cast<const LogicalExpression *>(expr);
std::vector<VidPattern> operandsResult;
operandsResult.reserve(expr->operands().size());
for (const auto &operand : expr->operands()) {
operand->accept(this);
operandsResult.emplace_back(moveVidPattern());
}
VidPattern inResult{VidPattern::Special::kInUsed, {}};
for (auto &result : operandsResult) {
if (result.spec == VidPattern::Special::kInUsed) {
for (auto &node : result.nodes) {
// Can't deduce with outher source (e.g. PropertiesIndex)
switch (node.second.kind) {
case VidPattern::Vids::Kind::kOtherSource:
vidPattern_ = VidPattern{};
return;
case VidPattern::Vids::Kind::kIn: {
inResult.nodes[node.first].kind = VidPattern::Vids::Kind::kIn;
inResult.nodes[node.first].vids.values.insert(
inResult.nodes[node.first].vids.values.end(),
std::make_move_iterator(node.second.vids.values.begin()),
std::make_move_iterator(node.second.vids.values.end()));
}
case VidPattern::Vids::Kind::kNotIn:
// nothing
break;
}
}
}
}
vidPattern_ = std::move(inResult);
return;
} else {
vidPattern_ = VidPattern{};
return;
}
}
// function call
void VidExtractVisitor::visit(FunctionCallExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(UUIDExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
// variable expression
void VidExtractVisitor::visit(VariableExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(VersionedVariableExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
// container expression
void VidExtractVisitor::visit(ListExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(SetExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(MapExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
// property Expression
void VidExtractVisitor::visit(TagPropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgePropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(InputPropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(VariablePropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(DestPropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(SourcePropertyExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgeSrcIdExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgeTypeExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgeRankExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgeDstIdExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(VertexExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(EdgeExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(ColumnExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(CaseExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visitBinaryExpr(BinaryExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(PathBuildExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(ListComprehensionExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(AggregateExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(PredicateExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
void VidExtractVisitor::visit(ReduceExpression *expr) {
UNUSED(expr);
vidPattern_ = VidPattern{};
}
std::ostream &operator<<(std::ostream &os, const VidExtractVisitor::VidPattern &vp) {
switch (vp.spec) {
case VidExtractVisitor::VidPattern::Special::kIgnore:
os << "Ignore.";
break;
case VidExtractVisitor::VidPattern::Special::kInUsed:
os << "InUsed: " << std::endl;
for (const auto &node : vp.nodes) {
os << node.first;
switch (node.second.kind) {
case VidExtractVisitor::VidPattern::Vids::Kind::kOtherSource:
os << " is OtherSource";
break;
case VidExtractVisitor::VidPattern::Vids::Kind::kIn:
os << " in " << node.second.vids;
break;
case VidExtractVisitor::VidPattern::Vids::Kind::kNotIn:
os << " not in " << node.second.vids;
break;
}
os << std::endl;
}
break;
}
return os;
}
} // namespace graph
} // namespace nebula
/* Copyright (c) 2020 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.
*/
#ifndef VISITOR_VIDEXTRACTVISITOR_H_
#define VISITOR_VIDEXTRACTVISITOR_H_
#include <memory>
#include "common/expression/ExprVisitor.h"
#include "util/SchemaUtil.h"
namespace nebula {
class Expression;
namespace graph {
// Reverse Eval the id(V) IN [List] or id(V) == vid that suppose result is TRUE
// Keep the constraint :
// Put any vid in result VidPattern.vids to id(V) make the expression evaluate to TRUE!(Ignore
// not related expression component e.g. attribute expr, so the filter still need eval
// when filter final result) and non any other vid make it eval to TRUE!
class VidExtractVisitor final : public ExprVisitor {
public:
VidExtractVisitor() = default;
struct VidPattern {
enum class Special {
kIgnore,
kInUsed,
} spec{Special::kIgnore};
struct Vids {
enum class Kind {
kOtherSource, // e.g. PropertiesSeek
kIn,
kNotIn,
} kind{Kind::kOtherSource};
List vids;
};
std::unordered_map<std::string, Vids> nodes;
};
VidPattern moveVidPattern() {
return std::move(vidPattern_);
}
static VidPattern intersect(VidPattern &&left, VidPattern &&right);
static VidPattern intersect(VidPattern &&left,
std::pair<std::string, VidPattern::Vids> &&right);
static VidPattern intersect(std::pair<std::string, VidPattern::Vids> &&left,
std::pair<std::string, VidPattern::Vids> &&right);
void visit(ConstantExpression *expr) override;
void visit(UnaryExpression *expr) override;
void visit(TypeCastingExpression *expr) override;
void visit(LabelExpression *expr) override;
void visit(LabelAttributeExpression *expr) override;
// binary expression
void visit(ArithmeticExpression *expr) override;
void visit(RelationalExpression *expr) override;
void visit(SubscriptExpression *expr) override;
void visit(AttributeExpression *expr) override;
void visit(LogicalExpression *expr) override;
// function call
void visit(FunctionCallExpression *expr) override;
void visit(UUIDExpression *expr) override;
// variable expression
void visit(VariableExpression *expr) override;
void visit(VersionedVariableExpression *expr) override;
// container expression
void visit(ListExpression *expr) override;
void visit(SetExpression *expr) override;
void visit(MapExpression *expr) override;
// property Expression
void visit(TagPropertyExpression *expr) override;
void visit(EdgePropertyExpression *expr) override;
void visit(InputPropertyExpression *expr) override;
void visit(VariablePropertyExpression *expr) override;
void visit(DestPropertyExpression *expr) override;
void visit(SourcePropertyExpression *expr) override;
void visit(EdgeSrcIdExpression *expr) override;
void visit(EdgeTypeExpression *expr) override;
void visit(EdgeRankExpression *expr) override;
void visit(EdgeDstIdExpression *expr) override;
// vertex/edge expression
void visit(VertexExpression *expr) override;
void visit(EdgeExpression *expr) override;
// case expression
void visit(CaseExpression *expr) override;
// path build expression
void visit(PathBuildExpression *expr) override;
// column expression
void visit(ColumnExpression *expr) override;
void visit(ListComprehensionExpression *expr) override;
void visit(AggregateExpression *expr) override;
void visit(PredicateExpression *expr) override;
void visit(ReduceExpression *expr) override;
private:
void visitBinaryExpr(BinaryExpression *expr);
VidPattern vidPattern_{};
};
std::ostream& operator<<(std::ostream &os, const VidExtractVisitor::VidPattern &vp);
} // namespace graph
} // namespace nebula
#endif // VISITOR_VIDEXTRACTVISITOR_H_
......@@ -367,34 +367,34 @@ Feature: Basic match
"""
MATCH (v) return v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v) RETURN v
When executing query:
"""
MATCH (v{name: "Tim Duncan"}) return v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v{name:"Tim Duncan"}) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v{name:"Tim Duncan"}) RETURN v
When executing query:
"""
MATCH (v:player:bachelor) RETURN v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player:bachelor) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player:bachelor) RETURN v
When executing query:
"""
MATCH (v:player{age:23}:bachelor) RETURN v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player{age:23}:bachelor) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player{age:23}:bachelor) RETURN v
When executing query:
"""
MATCH () -[r:serve]-> () return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-[r:serve]->() RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-[r:serve]->() RETURN *
When executing query:
"""
MATCH () -[]-> (v) return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v) RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v) RETURN *
When executing query:
"""
MATCH () --> (v) --> () return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v)-->() RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v)-->() RETURN *
......@@ -382,34 +382,34 @@ Feature: Basic match
"""
MATCH (v) return v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v) RETURN v
When executing query:
"""
MATCH (v{name: "Tim Duncan"}) return v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v{name:"Tim Duncan"}) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v{name:"Tim Duncan"}) RETURN v
When executing query:
"""
MATCH (v:player:bachelor) RETURN v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player:bachelor) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player:bachelor) RETURN v
When executing query:
"""
MATCH (v:player{age:23}:bachelor) RETURN v
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player{age:23}:bachelor) RETURN v
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH (v:player{age:23}:bachelor) RETURN v
When executing query:
"""
MATCH () -[r:serve]-> () return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-[r:serve]->() RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-[r:serve]->() RETURN *
When executing query:
"""
MATCH () -[]-> (v) return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v) RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v) RETURN *
When executing query:
"""
MATCH () --> (v) --> () return *
"""
Then a ExecutionError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v)-->() RETURN *
Then a SemanticError should be raised at runtime: Can't solve the start vids from the sentence: MATCH ()-->(v)-->() RETURN *
Feature: Match seek by id
Background: Prepare space
Given a graph with space named "nba"
Scenario: basic
When executing query:
"""
MATCH (v)
WHERE id(v) == 'Paul Gasol'
RETURN v.name AS Name, v.age AS Age
"""
Then the result should be, in any order:
| Name | Age |
| 'Paul Gasol' | 38 |
When executing query:
"""
MATCH (v)
WHERE id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical not
When executing query:
"""
MATCH (v)
WHERE NOT NOT id(v) == 'Paul Gasol'
RETURN v.name AS Name, v.age AS Age
"""
Then the result should be, in any order:
| Name | Age |
| 'Paul Gasol' | 38 |
When executing query:
"""
MATCH (v)
WHERE NOT NOT id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical and
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray', 'Paul Gasol']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
Scenario: basic logical or
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') OR id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') OR id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray', 'Paul Gasol']
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical with noise
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) == 'Paul Gasol'
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) != 'Paul Gasol'
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
When executing query:
"""
MATCH (v)
WHERE id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray', 'Paul Gasol']
OR false
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray', 'Paul Gasol']
AND (id(v) == 'James Harden' OR v.age == 23)
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
Scenario: complicate logical
When executing query:
"""
MATCH (v)
WHERE ((NOT NOT id(v) == 'Paul Gasol')
OR id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray'])
AND id(v) != 'Paul Gasol'
AND v.name != 'Jonathon Simmons'
AND v.age == 29
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE (id(v) == "Tim Duncan" AND v.age>10) OR (id(v) == "Tony Parker" AND v.age>10)
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| "Tim Duncan" |
| "Tony Parker" |
Scenario: with extend
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) == 'Paul Gasol'
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
| 'Paul Gasol' | 'Bulls' |
| 'Paul Gasol' | 'Spurs' |
| 'Paul Gasol' | 'Bucks' |
Scenario: multiple nodes
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE (NOT NOT id(v) == 'Paul Gasol') AND id(v) == 'Paul Gasol' AND id(t) IN ['Grizzlies', 'Lakers']
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE ((NOT NOT id(v) == 'Paul Gasol') AND id(v) == 'Paul Gasol') OR id(t) IN ['Grizzlies', 'Lakers']
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
| 'Paul Gasol' | 'Bulls' |
| 'Paul Gasol' | 'Spurs' |
| 'Paul Gasol' | 'Bucks' |
Scenario: can't refer
When executing query:
"""
MATCH (v)
WHERE NOT id(v) == 'Paul Gasol'
RETURN v.name AS Name, v.age AS Age
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE NOT id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(v) IN ['James Harden', 'Jonathon Simmons', 'Klay Thompson', 'Dejounte Murray']
OR v.age == 23
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(v) == 'James Harden'
OR v.age == 23
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(x) == 'James Harden'
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE (id(v) + '') == 'James Harden'
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
Scenario: Start from end
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE id(t) == 'Pistons'
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Aron Baynes' |
| 'Blake Griffin' |
| 'Grant Hill' |
Feature: Match seek by id
Background: Prepare space
Given a graph with space named "nba_int_vid"
Scenario: basic
When executing query:
"""
MATCH (v)
WHERE id(v) == hash('Paul Gasol')
RETURN v.name AS Name, v.age AS Age
"""
Then the result should be, in any order:
| Name | Age |
| 'Paul Gasol' | 38 |
When executing query:
"""
MATCH (v)
WHERE id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical not
When executing query:
"""
MATCH (v)
WHERE NOT NOT id(v) == hash('Paul Gasol')
RETURN v.name AS Name, v.age AS Age
"""
Then the result should be, in any order:
| Name | Age |
| 'Paul Gasol' | 38 |
When executing query:
"""
MATCH (v)
WHERE NOT NOT id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical and
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray'), hash('Paul Gasol')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
Scenario: basic logical or
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) OR id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) OR id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray'), hash('Paul Gasol')]
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
Scenario: basic logical with noise
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) == hash('Paul Gasol')
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
When executing query:
"""
MATCH (v)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) != hash('Paul Gasol')
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
When executing query:
"""
MATCH (v)
WHERE id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray'), hash('Paul Gasol')]
OR false
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Paul Gasol' |
| 'James Harden' |
| 'Jonathon Simmons' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray'), hash('Paul Gasol')]
AND (id(v) == hash('James Harden') OR v.age == 23)
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
Scenario: complicate logical
When executing query:
"""
MATCH (v)
WHERE ((NOT NOT id(v) == hash('Paul Gasol'))
OR id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')])
AND id(v) != hash('Paul Gasol')
AND v.name != 'Jonathon Simmons'
AND v.age == 29
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'James Harden' |
| 'Klay Thompson' |
| 'Dejounte Murray' |
When executing query:
"""
MATCH (v)
WHERE (id(v) == hash("Tim Duncan") AND v.age>10) OR (id(v) == hash("Tony Parker") AND v.age>10)
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| "Tim Duncan" |
| "Tony Parker" |
Scenario: with extend
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) == hash('Paul Gasol')
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
| 'Paul Gasol' | 'Bulls' |
| 'Paul Gasol' | 'Spurs' |
| 'Paul Gasol' | 'Bucks' |
Scenario: multiple nodes
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE (NOT NOT id(v) == hash('Paul Gasol')) AND id(v) == hash('Paul Gasol') AND id(t) IN [hash('Grizzlies'), hash('Lakers')]
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE ((NOT NOT id(v) == hash('Paul Gasol')) AND id(v) == hash('Paul Gasol')) OR id(t) IN [hash('Grizzlies'), hash('Lakers')]
RETURN v.name AS Name, t.name AS Team
"""
Then the result should be, in any order:
| Name | Team |
| 'Paul Gasol' | 'Grizzlies' |
| 'Paul Gasol' | 'Lakers' |
| 'Paul Gasol' | 'Bulls' |
| 'Paul Gasol' | 'Spurs' |
| 'Paul Gasol' | 'Bucks' |
Scenario: can't refer
When executing query:
"""
MATCH (v)
WHERE NOT id(v) == hash('Paul Gasol')
RETURN v.name AS Name, v.age AS Age
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE NOT id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(v) IN [hash('James Harden'), hash('Jonathon Simmons'), hash('Klay Thompson'), hash('Dejounte Murray')]
OR v.age == 23
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(v) == hash('James Harden')
OR v.age == 23
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE id(x) == hash('James Harden')
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
When executing query:
"""
MATCH (v)
WHERE (id(v) + 1) == hash('James Harden')
RETURN v.name AS Name
"""
Then a SemanticError should be raised at runtime:
Scenario: Start from end
When executing query:
"""
MATCH (v)-[:serve]->(t)
WHERE id(t) == hash('Pistons')
RETURN v.name AS Name
"""
Then the result should be, in any order:
| Name |
| 'Aron Baynes' |
| 'Blake Griffin' |
| 'Grant Hill' |
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