diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h b/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h index b6e5782921..f9cc7a326c 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstDumper.h @@ -1470,6 +1470,26 @@ class AstDumper : public AstNodeVisitor { VISIT_PARAM_AND_CHECK_WITH_MSG(yield); return GEAXErrorCode::GEAX_SUCCEED; } + std::any visit(UnwindStatement* node) override { + INDET_GUARD(); + VARIABLE_GUARD_WITH_TYPE_NAME(UnwindStatement); + auto& v = node->variable(); + auto list = node->list(); + VISIT_PARAM_AND_CHECK_WITH_MSG(v); + VISIT_PARAM_AND_CHECK_WITH_MSG(list); + return GEAXErrorCode::GEAX_SUCCEED; + } + std::any visit(InQueryProcedureCall* node) override { + INDET_GUARD(); + VARIABLE_GUARD_WITH_TYPE_NAME(InQueryProcedureCall); + auto& name = node->name(); + auto& args = node->args(); + auto& yield = node->yield(); + VISIT_PARAM_AND_CHECK_WITH_MSG(name); + VISIT_PARAM_AND_CHECK_WITH_MSG(args); + VISIT_PARAM_AND_CHECK_WITH_MSG(yield); + return GEAXErrorCode::GEAX_SUCCEED; + } std::any visit(DummyNode* node) override { return reportError(node); } protected: diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstNode.h b/deps/geax-front-end/include/geax-front-end/ast/AstNode.h index 4b0f3f7089..220262bc20 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstNode.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstNode.h @@ -188,6 +188,8 @@ using StrArray = std::array; TYPE(MkRecord, kMkRecord, "MkRecord") \ TYPE(MkSet, kMkSet, "MkSet") \ TYPE(MkTuple, kMkTuple, "MkTuple") \ + TYPE(UnwindStatement, kUnwindStatement, "UnwindStatement") \ + TYPE(InQueryProcedureCall, kInQueryProcedureCall, "InQueryProcedureCall") \ TYPE(DummyNode, kNotDefined, "NotDefined") // This should always be the last one diff --git a/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h b/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h index 129e67e51e..2634a3ac57 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h +++ b/deps/geax-front-end/include/geax-front-end/ast/AstNodeVisitor.h @@ -179,6 +179,8 @@ class MergeStatement; class ShowProcessListStatement; class KillStatement; class ManagerStatement; +class UnwindStatement; +class InQueryProcedureCall; class DummyNode; @@ -359,6 +361,8 @@ class AstNodeVisitor { virtual std::any visit(ShowProcessListStatement* node) = 0; virtual std::any visit(KillStatement* node) = 0; virtual std::any visit(ManagerStatement* node) = 0; + virtual std::any visit(UnwindStatement* node) = 0; + virtual std::any visit(InQueryProcedureCall* node) = 0; virtual std::any visit(DummyNode* node) = 0; @@ -724,6 +728,12 @@ class AstExprNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(ManagerStatement*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + virtual std::any visit(UnwindStatement*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } + virtual std::any visit(InQueryProcedureCall*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } virtual std::any visit(DummyNode*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; @@ -1197,6 +1207,12 @@ class AstLabelTreeNodeVisitorImpl : public AstNodeVisitor { virtual std::any visit(ManagerStatement*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; } + virtual std::any visit(UnwindStatement*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } + virtual std::any visit(InQueryProcedureCall*) override { + return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; + } virtual std::any visit(DummyNode*) override { return GEAXErrorCode::GEAX_COMMON_NOT_SUPPORT; diff --git a/deps/geax-front-end/include/geax-front-end/ast/clause/YieldField.h b/deps/geax-front-end/include/geax-front-end/ast/clause/YieldField.h index caece35caa..27b86b48e9 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/clause/YieldField.h +++ b/deps/geax-front-end/include/geax-front-end/ast/clause/YieldField.h @@ -19,6 +19,8 @@ #define GEAXFRONTEND_AST_CLAUSE_YIELDFIELD_H_ #include "geax-front-end/ast/AstNode.h" +#include +#include namespace geax { namespace frontend { diff --git a/deps/geax-front-end/include/geax-front-end/ast/expr/AggFunc.h b/deps/geax-front-end/include/geax-front-end/ast/expr/AggFunc.h index 72a83a3e83..47bd68da24 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/expr/AggFunc.h +++ b/deps/geax-front-end/include/geax-front-end/ast/expr/AggFunc.h @@ -33,12 +33,17 @@ enum class GeneralSetFunction { kStdDevSamp, kStdDevPop, kGroupConcat, + kStDev, + kStDevP, + kVariance, + kVarianceP, kLastButNotUse }; inline const char* ToString(GeneralSetFunction type) { static const StrArray kDict = { - "avg", "count", "max", "min", "sum", "collect", "stdDevSamp", "stdDevPop", "groupConcat"}; + "avg", "count", "max", "min", "sum", "collect", "stdDevSamp", "stdDevPop", "groupConcat", + "stDev", "stDevP", "variance", "varianceP"}; const auto idx = static_cast(type); return idx < kDict.size() ? kDict[idx] : geax::frontend::kUnknown; } @@ -53,7 +58,11 @@ inline bool ToEnum(std::string_view sv, GeneralSetFunction& type) { {"collect", GeneralSetFunction::kCollect}, {"stdDevSamp", GeneralSetFunction::kStdDevSamp}, {"stdDevPop", GeneralSetFunction::kStdDevPop}, - {"groutConcat", GeneralSetFunction::kGroupConcat}}; + {"groutConcat", GeneralSetFunction::kGroupConcat}, + {"stDev", GeneralSetFunction::kStDev}, + {"stDevP", GeneralSetFunction::kStDevP}, + {"variance", GeneralSetFunction::kVariance}, + {"varianceP", GeneralSetFunction::kVarianceP}}; auto it = kTypeMap.find(sv); return it != kTypeMap.end() && (type = it->second, true); } diff --git a/deps/geax-front-end/include/geax-front-end/ast/expr/MkList.h b/deps/geax-front-end/include/geax-front-end/ast/expr/MkList.h index 2dd5b10fc4..f9c27a8f71 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/expr/MkList.h +++ b/deps/geax-front-end/include/geax-front-end/ast/expr/MkList.h @@ -18,6 +18,7 @@ #ifndef GEAXFRONTEND_AST_EXPR_MKLIST_H_ #define GEAXFRONTEND_AST_EXPR_MKLIST_H_ +#include #include "geax-front-end/ast/expr/Expr.h" namespace geax { diff --git a/deps/geax-front-end/include/geax-front-end/ast/stmt/InQueryProcedureCall.h b/deps/geax-front-end/include/geax-front-end/ast/stmt/InQueryProcedureCall.h new file mode 100644 index 0000000000..82f4b3aca9 --- /dev/null +++ b/deps/geax-front-end/include/geax-front-end/ast/stmt/InQueryProcedureCall.h @@ -0,0 +1,53 @@ +/** + * Copyright 2023 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#ifndef GEAXFRONTEND_AST_STMT_INQUERYPROCEDURECALL_H_ +#define GEAXFRONTEND_AST_STMT_INQUERYPROCEDURECALL_H_ + +#include "geax-front-end/ast/stmt/ProcedureCall.h" +#include "geax-front-end/ast/expr/Param.h" +#include "geax-front-end/ast/expr/Expr.h" +#include "geax-front-end/ast/clause/YieldField.h" + +namespace geax { +namespace frontend { + +class InQueryProcedureCall : public ProcedureCall { +public: + InQueryProcedureCall() : ProcedureCall(AstNodeType::kInQueryProcedureCall) {} + ~InQueryProcedureCall() = default; + + void setName(StringParam&& name) { name_ = std::move(name); } + const StringParam name() const { return name_; } + + void appendArg(Expr* arg) { args_.emplace_back(arg); } + void setArgs(std::vector&& args) { args_ = std::move(args); } + const std::vector& args() const { return args_; } + + void setYield(YieldField* yield) { yield_ = yield; } + const std::optional& yield() const { return yield_; } + + std::any accept(AstNodeVisitor& visitor) override { return visitor.visit(this); } + +private: + StringParam name_; + std::vector args_; + std::optional yield_; +}; // class NamedProcedureCall + +} // namespace frontend +} // namespace geax + +#endif // GEAXFRONTEND_AST_STMT_INQUERYPROCEDURECALL_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/stmt/StmtNodeFwd.h b/deps/geax-front-end/include/geax-front-end/ast/stmt/StmtNodeFwd.h index 927352844a..27cde6ba19 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/stmt/StmtNodeFwd.h +++ b/deps/geax-front-end/include/geax-front-end/ast/stmt/StmtNodeFwd.h @@ -65,5 +65,7 @@ #include "geax-front-end/ast/stmt/StatementWithYield.h" #include "geax-front-end/ast/stmt/TransactionActivity.h" #include "geax-front-end/ast/stmt/MergeStatement.h" +#include "geax-front-end/ast/stmt/UnwindStatement.h" +#include "geax-front-end/ast/stmt/InQueryProcedureCall.h" #endif // GEAXFRONTEND_AST_STMT_STMTNODEFWD_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/stmt/UnwindStatement.h b/deps/geax-front-end/include/geax-front-end/ast/stmt/UnwindStatement.h new file mode 100644 index 0000000000..b518e52d0f --- /dev/null +++ b/deps/geax-front-end/include/geax-front-end/ast/stmt/UnwindStatement.h @@ -0,0 +1,49 @@ +/** + * Copyright 2023 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#ifndef GEAXFRONTEND_AST_STMT_UnwindStatement_H_ +#define GEAXFRONTEND_AST_STMT_UnwindStatement_H_ + +#include +#include "geax-front-end/ast/expr/Expr.h" +#include "geax-front-end/ast/stmt/SimpleQueryStatement.h" + +namespace geax { +namespace frontend { + +class UnwindStatement : public SimpleQueryStatement { +public: + explicit UnwindStatement() + : SimpleQueryStatement(AstNodeType::kUnwindStatement) {} + ~UnwindStatement() = default; + + void setVariable(std::string&& v) {variable_ = std::move(v);} + + const std::string& variable() {return variable_;} + + void setList(Expr* expr) {list_ = expr;} + + Expr* list() const { return list_; } + + std::any accept(AstNodeVisitor& visitor) override { return visitor.visit(this); } +private: + std::string variable_; + Expr* list_; +}; // class SchemaRef + +} // namespace frontend +} // namespace geax + +#endif // GEAXFRONTEND_AST_STMT_UnwindStatement_H_ diff --git a/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h b/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h index e3db25131b..494f34311e 100644 --- a/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h +++ b/deps/geax-front-end/include/geax-front-end/ast/utils/AstUtil.h @@ -85,6 +85,7 @@ class AstExprToString : public AstExprNodeVisitorImpl { std::any visit(geax::frontend::BDiv* node) override { BINARY_EXPR_TOSTRING("/"); } std::any visit(geax::frontend::BMul* node) override { BINARY_EXPR_TOSTRING("*"); } std::any visit(geax::frontend::BMod* node) override { BINARY_EXPR_TOSTRING("%"); } + std::any visit(geax::frontend::BSquare* node) override { BINARY_EXPR_TOSTRING("^"); } std::any visit(geax::frontend::BAnd* node) override { BINARY_EXPR_TOSTRING("and"); } std::any visit(geax::frontend::BOr* node) override { BINARY_EXPR_TOSTRING("or"); } std::any visit(geax::frontend::BXor* node) override { BINARY_EXPR_TOSTRING("xor"); } diff --git a/src/BuildCypherLib.cmake b/src/BuildCypherLib.cmake index d646f56225..08edf416fc 100644 --- a/src/BuildCypherLib.cmake +++ b/src/BuildCypherLib.cmake @@ -51,6 +51,7 @@ set(LGRAPH_CYPHER_SRC # find cypher/ -name "*.cpp" | sort cypher/execution_plan/ops/op_var_len_expand.cpp cypher/execution_plan/ops/op_var_len_expand_into.cpp cypher/execution_plan/ops/op_merge.cpp + cypher/execution_plan/ops/op_gql_merge.cpp cypher/execution_plan/ops/op_node_by_id_seek.cpp cypher/execution_plan/ops/op_traversal.cpp cypher/execution_plan/scheduler.cpp diff --git a/src/cypher/arithmetic/arithmetic_expression.cpp b/src/cypher/arithmetic/arithmetic_expression.cpp index 3adf8f5828..6b4740dba3 100644 --- a/src/cypher/arithmetic/arithmetic_expression.cpp +++ b/src/cypher/arithmetic/arithmetic_expression.cpp @@ -1284,9 +1284,8 @@ cypher::FieldData BuiltinFunction::Regexp(RTContext *ctx, const Record &record, cypher::FieldData BuiltinFunction::Exists(RTContext *ctx, const Record &record, const std::vector &args) { - // TODO(lingsu): implement in future - CYPHER_TODO(); - return cypher::FieldData(); + auto res = args[1].Evaluate(ctx, record); + return cypher::FieldData(lgraph_api::FieldData(!res.IsNull())); } cypher::FieldData BuiltinFunction::Bin(RTContext *ctx, const Record &record, diff --git a/src/cypher/arithmetic/ast_agg_expr_detector.cpp b/src/cypher/arithmetic/ast_agg_expr_detector.cpp index 39546709e7..ba3c26ba75 100644 --- a/src/cypher/arithmetic/ast_agg_expr_detector.cpp +++ b/src/cypher/arithmetic/ast_agg_expr_detector.cpp @@ -16,7 +16,6 @@ #include #include "cypher/arithmetic/arithmetic_expression.h" #include "cypher/arithmetic/ast_agg_expr_detector.h" -#include "geax-front-end/ast/expr/Ref.h" namespace cypher { @@ -53,7 +52,9 @@ std::any AstAggExprDetector::visit(geax::frontend::AggFunc* node) { } in_agg_func_ += 1; auto agg_funcs = ArithOpNode::RegisterAggFuncs(); - if (agg_funcs.find(ToString(node->funcName())) != agg_funcs.end()) { + std::string func_name = ToString(node->funcName()); + std::transform(func_name.begin(), func_name.end(), func_name.begin(), ::tolower); + if (agg_funcs.find(func_name) != agg_funcs.end()) { agg_exprs_.emplace_back(node); } ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->expr()); @@ -61,7 +62,26 @@ std::any AstAggExprDetector::visit(geax::frontend::AggFunc* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any AstAggExprDetector::visit(geax::frontend::Ref* node) { +std::any AstAggExprDetector::visit(geax::frontend::BAggFunc* node) { + if (in_agg_func_ > 0) { + nested_agg_func_ = true; + error_msg_ = "Agg function cannot be nested"; + return geax::frontend::GEAXErrorCode::GEAX_ERROR; + } + in_agg_func_ += 1; + auto agg_funcs = ArithOpNode::RegisterAggFuncs(); + std::string func_name = ToString(node->funcName()); + std::transform(func_name.begin(), func_name.end(), func_name.begin(), ::tolower); + if (agg_funcs.find(func_name) != agg_funcs.end()) { + agg_exprs_.emplace_back(node); + } + ACCEPT_AND_CHECK_WITH_ERROR_MSG(std::get<1>(node->lExpr())); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->rExpr()); + in_agg_func_ -= 1; + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + +std::any AstAggExprDetector::visit(geax::frontend::GetField* node) { if (in_agg_func_ == 0) { outside_var_ = true; } diff --git a/src/cypher/arithmetic/ast_agg_expr_detector.h b/src/cypher/arithmetic/ast_agg_expr_detector.h index f83d8185c6..717b23a505 100644 --- a/src/cypher/arithmetic/ast_agg_expr_detector.h +++ b/src/cypher/arithmetic/ast_agg_expr_detector.h @@ -44,7 +44,8 @@ class AstAggExprDetector : public cypher::AstNodeVisitorImpl { private: std::any visit(geax::frontend::AggFunc* node) override; - std::any visit(geax::frontend::Ref* node) override; + std::any visit(geax::frontend::BAggFunc* node) override; + std::any visit(geax::frontend::GetField* node) override; bool nested_agg_func_ = false; size_t in_agg_func_ = 0; bool outside_var_ = false; diff --git a/src/cypher/arithmetic/ast_expr_evaluator.cpp b/src/cypher/arithmetic/ast_expr_evaluator.cpp index e122c2e863..176a7dd65c 100644 --- a/src/cypher/arithmetic/ast_expr_evaluator.cpp +++ b/src/cypher/arithmetic/ast_expr_evaluator.cpp @@ -14,7 +14,8 @@ */ #include -#include "geax-front-end/ast/expr/VDouble.h" +#include +#include "cypher/cypher_types.h" #include "core/data_type.h" #include "cypher/arithmetic/arithmetic_expression.h" #include "cypher/resultset/record.h" @@ -227,7 +228,18 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::BIndex* node) { NOT_SUP std::any cypher::AstExprEvaluator::visit(geax::frontend::BLike* node) { NOT_SUPPORT_AND_THROW(); } -std::any cypher::AstExprEvaluator::visit(geax::frontend::BIn* node) { NOT_SUPPORT_AND_THROW(); } +std::any cypher::AstExprEvaluator::visit(geax::frontend::BIn* node) { + auto l_val = std::any_cast(node->left()->accept(*this)); + auto r_val = std::any_cast(node->right()->accept(*this)); + if (!l_val.IsScalar()) NOT_SUPPORT_AND_THROW(); + if (!r_val.IsArray()) NOT_SUPPORT_AND_THROW(); + for (auto& val : *r_val.constant.array) { + if (l_val.constant.scalar == val) { + return Entry(cypher::FieldData(lgraph::FieldData(true))); + } + } + return Entry(cypher::FieldData(lgraph::FieldData(false))); +} std::any cypher::AstExprEvaluator::visit(geax::frontend::If* node) { NOT_SUPPORT_AND_THROW(); } @@ -260,7 +272,7 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::Case* node) { if (node->elseBody().has_value()) { return node->elseBody().value()->accept(*this); } else { - NOT_SUPPORT_AND_THROW(); + return Entry(); } } else { @@ -290,12 +302,14 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::MatchCase* node) { std::any cypher::AstExprEvaluator::visit(geax::frontend::AggFunc* node) { std::unordered_map()>> registered_agg_funcs = ArithOpNode::RegisterAggFuncs(); - auto agg_it = registered_agg_funcs.find(ToString(node->funcName())); + std::string func_name = ToString(node->funcName()); + std::transform(func_name.begin(), func_name.end(), func_name.begin(), ::tolower); + auto agg_it = registered_agg_funcs.find(func_name); if (agg_it != registered_agg_funcs.end()) { // Evalute Mode if (visit_mode_ == VisitMode::EVALUATE) { if (agg_pos_ >= agg_ctxs_.size()) { - NOT_SUPPORT_AND_THROW(); + return Entry(cypher::FieldData(lgraph_api::FieldData(0))); } return agg_ctxs_[agg_pos_++]->result; } else if (visit_mode_ == VisitMode::AGGREGATE) { @@ -318,6 +332,36 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::AggFunc* node) { } std::any cypher::AstExprEvaluator::visit(geax::frontend::BAggFunc* node) { + std::unordered_map()>> registered_agg_funcs = + ArithOpNode::RegisterAggFuncs(); + std::string func_name = ToString(node->funcName()); + std::transform(func_name.begin(), func_name.end(), func_name.begin(), ::tolower); + auto agg_it = registered_agg_funcs.find(func_name); + if (agg_it != registered_agg_funcs.end()) { + // Evalute Mode + if (visit_mode_ == VisitMode::EVALUATE) { + if (agg_pos_ >= agg_ctxs_.size()) { + return Entry(cypher::FieldData(lgraph_api::FieldData(0))); + } + return agg_ctxs_[agg_pos_++]->result; + } else if (visit_mode_ == VisitMode::AGGREGATE) { + // todo(...): registered_agg_funcs cannot be static and need improvement + // return Entry(agg_it->second()); + if (agg_pos_ == agg_ctxs_.size()) { + agg_ctxs_.emplace_back(agg_it->second()); + } + if (agg_pos_ >= agg_ctxs_.size()) { + NOT_SUPPORT_AND_THROW(); + } + std::vector args; + auto& left = node->lExpr(); + args.emplace_back(Entry(cypher::FieldData(lgraph::FieldData(std::get<0>(left))))); + args.emplace_back(std::any_cast(std::get<1>(left)->accept(*this))); + args.emplace_back(std::any_cast(node->rExpr()->accept(*this))); + agg_ctxs_[agg_pos_]->Step(args); + return Entry(cypher::FieldData()); + } + } NOT_SUPPORT_AND_THROW(); } @@ -342,7 +386,18 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::MkList* node) { return Entry(cypher::FieldData(list)); } -std::any cypher::AstExprEvaluator::visit(geax::frontend::MkMap* node) { NOT_SUPPORT_AND_THROW(); } +std::any cypher::AstExprEvaluator::visit(geax::frontend::MkMap* node) { + const auto& elems = node->elems(); + std::unordered_map map; + for (const auto& pair : elems) { + auto key = std::any_cast(std::get<0>(pair)->accept(*this)); + auto val = std::any_cast(std::get<1>(pair)->accept(*this)); + if (!key.IsString()) NOT_SUPPORT_AND_THROW(); + if (!val.IsScalar()) NOT_SUPPORT_AND_THROW(); + map.emplace(key.constant.ToString(), val.constant.scalar); + } + return Entry(cypher::FieldData(map)); +} std::any cypher::AstExprEvaluator::visit(geax::frontend::MkRecord* node) { NOT_SUPPORT_AND_THROW(); @@ -411,7 +466,17 @@ std::any cypher::AstExprEvaluator::visit(geax::frontend::Ref* node) { return std::any(); } -std::any cypher::AstExprEvaluator::visit(geax::frontend::Param* node) { NOT_SUPPORT_AND_THROW(); } +std::any cypher::AstExprEvaluator::visit(geax::frontend::Param* node) { + auto& variabel = node->name(); + auto it = sym_tab_->symbols.find(variabel); + if (it == sym_tab_->symbols.end()) { + throw lgraph::CypherException("Parameter not defined: " + variabel); + } + if (record_->values[it->second.id].type == Entry::UNKNOWN) { + throw lgraph::CypherException("Undefined parameter: " + variabel); + } + return record_->values[it->second.id]; +} std::any cypher::AstExprEvaluator::visit(geax::frontend::SingleLabel* node) { std::unordered_set set; diff --git a/src/cypher/cypher_types.h b/src/cypher/cypher_types.h index f046f08a44..e11644108a 100644 --- a/src/cypher/cypher_types.h +++ b/src/cypher/cypher_types.h @@ -17,19 +17,19 @@ // #pragma once +#include #include "core/data_type.h" #include "cypher/cypher_exception.h" namespace cypher { struct FieldData { - enum FieldType { - SCALAR, - ARRAY, - } type; + // TODO(lingsu) : a default state should be added + enum FieldType { SCALAR, ARRAY, MAP } type; ::lgraph::FieldData scalar; std::vector<::lgraph::FieldData>* array = nullptr; + std::unordered_map* map = nullptr; FieldData() : type(SCALAR) {} @@ -47,36 +47,88 @@ struct FieldData { array = new std::vector<::lgraph::FieldData>(std::move(rhs)); } + explicit FieldData(const std::unordered_map& rhs) { + type = MAP; + map = new std::unordered_map(rhs); + } + + explicit FieldData(std::unordered_map&& rhs) { + type = MAP; + map = new std::unordered_map(std::move(rhs)); + } + ~FieldData() { - if (type == ARRAY) delete array; + switch (type) { + case ARRAY: + delete array; + break; + case MAP: + delete map; + break; + case SCALAR: + break; + } } FieldData(const FieldData& rhs) { type = rhs.type; - if (rhs.type == ARRAY) { + switch (type) { + case ARRAY: array = new std::vector<::lgraph::FieldData>(*rhs.array); - } else { + break; + case MAP: + map = new std::unordered_map(*rhs.map); + break; + case SCALAR: scalar = rhs.scalar; + break; } } FieldData(FieldData&& rhs) { type = rhs.type; - scalar = std::move(rhs.scalar); - array = rhs.array; - // rhs.scalar becomes FieldType::NUL after move + switch (type) { + case ARRAY: + array = rhs.array; + break; + case MAP: + map = rhs.map; + break; + case SCALAR: + scalar = std::move(rhs.scalar); + break; + } + // TODO(lingsu) : rhs should return to the default state rhs.type = SCALAR; } FieldData& operator=(const ::lgraph::FieldData& rhs) { - if (type == ARRAY) delete array; + switch (type) { + case ARRAY: + delete array; + break; + case MAP: + delete map; + break; + case SCALAR: + break; + } type = SCALAR; scalar = rhs; return *this; } FieldData& operator=(::lgraph::FieldData&& rhs) { - if (type == ARRAY) delete array; + switch (type) { + case ARRAY: + delete array; + break; + case MAP: + delete map; + break; + case SCALAR: + break; + } type = SCALAR; scalar = std::move(rhs); return *this; @@ -84,32 +136,76 @@ struct FieldData { FieldData& operator=(const ::cypher::FieldData& rhs) { if (this == &rhs) return *this; - if (type == ARRAY) delete array; + switch (type) { + case ARRAY: + delete array; + break; + case MAP: + delete map; + break; + case SCALAR: + break; + } + type = rhs.type; - if (rhs.type == ARRAY) { + + switch (type) { + case ARRAY: array = new std::vector<::lgraph::FieldData>(*rhs.array); - } else { + break; + case MAP: + map = new std::unordered_map(*rhs.map); + break; + case SCALAR: scalar = rhs.scalar; + break; } return *this; } FieldData& operator=(::cypher::FieldData&& rhs) { if (this == &rhs) return *this; - if (type == ARRAY) delete array; + switch (type) { + case ARRAY: + delete array; + array = nullptr; + break; + case MAP: + delete map; + map = nullptr; + break; + case SCALAR: + break; + } type = rhs.type; - scalar = std::move(rhs.scalar); - array = rhs.array; - // rhs.scalar becomes FieldType::NUL after move + switch (type) { + case ARRAY: + std::swap(array, rhs.array); + break; + case MAP: + std::swap(map, rhs.map); + break; + case SCALAR: + scalar = std::move(rhs.scalar); + break; + } + // TODO(lingsu) : rhs should return to the default state rhs.type = SCALAR; return *this; } bool operator==(const FieldData& rhs) const { if (type != rhs.type) - throw std::runtime_error("Unable to compare between SCALAR and ARRAY."); - if (type == SCALAR) return scalar == rhs.scalar; - if (type == ARRAY) CYPHER_TODO(); + throw std::runtime_error("Unable to compare between different field data types"); + switch (type) { + case ARRAY: + CYPHER_TODO(); + case MAP: + CYPHER_TODO(); + case SCALAR: + return scalar == rhs.scalar; + } + CYPHER_TODO(); return false; } @@ -117,17 +213,31 @@ struct FieldData { bool operator>(const FieldData& rhs) const { if (type != rhs.type) - throw std::runtime_error("Unable to compare between SCALAR and ARRAY."); - if (type == SCALAR) return scalar > rhs.scalar; - if (type == ARRAY) CYPHER_TODO(); + throw std::runtime_error("Unable to compare between different field data types"); + switch (type) { + case ARRAY: + CYPHER_TODO(); + case MAP: + CYPHER_TODO(); + case SCALAR: + return scalar > rhs.scalar; + } + CYPHER_TODO(); return false; } bool operator>=(const FieldData& rhs) const { if (type != rhs.type) - throw std::runtime_error("Unable to compare between SCALAR and ARRAY."); - if (type == SCALAR) return scalar >= rhs.scalar; - if (type == ARRAY) CYPHER_TODO(); + throw std::runtime_error("Unable to compare between different field data types"); + switch (type) { + case ARRAY: + CYPHER_TODO(); + case MAP: + CYPHER_TODO(); + case SCALAR: + return scalar >= rhs.scalar; + } + CYPHER_TODO(); return false; } @@ -136,12 +246,16 @@ struct FieldData { bool operator<=(const FieldData& rhs) const { return !(*this > rhs); } bool EqualNull() const { - if (type == SCALAR) { - return scalar.is_null(); - } else { - CYPHER_THROW_ASSERT(type == ARRAY); + switch (type) { + case ARRAY: return !array || array->empty(); + case MAP: + return !map || map->empty(); + case SCALAR: + return scalar.is_null(); } + CYPHER_TODO(); + return false; } bool IsNull() const { return type == SCALAR && scalar.is_null(); } @@ -168,24 +282,40 @@ struct FieldData { bool IsPolygon() const { return type == SCALAR && scalar.type == lgraph::FieldType::POLYGON; } - bool IsSpatial() const { return (IsPoint() || IsLineString() || IsPolygon()) || - (type == SCALAR && scalar.type == lgraph::FieldType::SPATIAL); } + bool IsSpatial() const { + return (IsPoint() || IsLineString() || IsPolygon()) || + (type == SCALAR && scalar.type == lgraph::FieldType::SPATIAL); + } bool IsArray() const { return type == ARRAY; } static FieldData Array(size_t n) { return FieldData(std::vector<::lgraph::FieldData>(n)); } + bool IsMap() const { return type == MAP; } + std::string ToString(const std::string& null_value = "NUL") const { - if (type == SCALAR) { - return scalar.ToString(null_value); - } else { - CYPHER_THROW_ASSERT(type == ARRAY && array); + switch (type) { + case ARRAY: { std::string str("["); for (auto& s : *array) str.append(s.ToString(null_value)).append(","); if (str.size() > 1) str.pop_back(); str.append("]"); return str; } + case MAP: { + std::string str("{"); + for (auto& pair : *map) { + str.append("{"); + str.append(pair.first).append(",").append(pair.second.ToString(null_value)); + str.append("},"); + } + if (str.size() > 1) str.pop_back(); + str.append("}"); + return str; + } + case SCALAR: + return scalar.ToString(null_value); + } throw std::runtime_error("internal error: unhandled type: " + std::to_string((int)type)); } }; diff --git a/src/cypher/execution_plan/execution_plan_maker.cpp b/src/cypher/execution_plan/execution_plan_maker.cpp index 5d1bc2a375..0c11278ac6 100644 --- a/src/cypher/execution_plan/execution_plan_maker.cpp +++ b/src/cypher/execution_plan/execution_plan_maker.cpp @@ -283,13 +283,14 @@ std::any ExecutionPlanMaker::visit(geax::frontend::PathChain* node) { auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; auto head = node->head(); ACCEPT_AND_CHECK_WITH_ERROR_MSG(head); + std::vector expand_ops; + if (last_op_) { + expand_ops.emplace_back(last_op_); + last_op_ = nullptr; + } // todo: ... ClauseGuard cg(node->type(), cur_types_); auto& tails = node->tails(); - std::vector expand_ops; - if (tails.size() == 0) { - return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; - } // TODO(lingsu): generate the pattern graph // and select the starting Node and end Node according to the pattern graph for (auto [edge, end_node] : tails) { @@ -323,8 +324,46 @@ std::any ExecutionPlanMaker::visit(geax::frontend::PathChain* node) { ++filter_level_; } std::reverse(expand_ops.begin(), expand_ops.end()); + // The handling here is for the Match Clause, other situations need to be considered. + // The current design of the Visitor results in different logic for handling PathChain in + // different Clauses (Match, Create, and even Exists), which makes the code overly complex and + // needs refactoring. + if (!ClauseGuard::InClause(geax::frontend::AstNodeType::kMatchStatement, cur_types_)) { + return geax::frontend::GEAXErrorCode::GEAX_ERROR; + } + if (auto op = _SingleBranchConnect(expand_ops)) { - _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + // Handle the case where multiple PathChains are connected, with special handling + // for cases where multiple PathChains are mutually independent. + // Considering that _UpdateStreamRoot connections are more complex and AllNodeScan + // cannot be connected directly, this determination requires additional processing. + bool is_pathchain_independent = true; + if (is_pathchain_independent) { + if (pattern_graph_root_[cur_pattern_graph_] && + (pattern_graph_root_[cur_pattern_graph_]->type != OpType::ARGUMENT && + pattern_graph_root_[cur_pattern_graph_]->type != OpType::UNWIND)) { + auto connection = new CartesianProduct(); + connection->AddChild(pattern_graph_root_[cur_pattern_graph_]); + connection->AddChild(op); + pattern_graph_root_[cur_pattern_graph_] = connection; + } else if (pattern_graph_root_[cur_pattern_graph_] && + pattern_graph_root_[cur_pattern_graph_]->type == OpType::UNWIND && + (*expand_ops.rbegin())->IsScan()) { + auto op = *expand_ops.rbegin(); + auto connection = new CartesianProduct(); + connection->AddChild(pattern_graph_root_[cur_pattern_graph_]); + connection->AddChild(op); + pattern_graph_root_[cur_pattern_graph_] = connection; + } else if (pattern_graph_root_[cur_pattern_graph_]) { + _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + } else { + pattern_graph_root_[cur_pattern_graph_] = op; + } + } else { + // For the associated case, currently handled through ast node rewriter, but this has + // performance issues. In the future, MultiMatch will be rewritten. + CYPHER_TODO(); + } } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } @@ -348,7 +387,9 @@ std::any ExecutionPlanMaker::visit(geax::frontend::Node* node) { } std::reverse(expand_ops.begin(), expand_ops.end()); if (auto op = _SingleBranchConnect(expand_ops)) { - _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + // Do not connect op to pattern_graph_root_[cur_pattern_graph_] for now, + // use last_op_ to store it. + last_op_ = op; } } return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; @@ -476,6 +517,22 @@ std::any ExecutionPlanMaker::visit(geax::frontend::PropStruct* node) { right->setVal(((geax::frontend::VInt*)value)->val()); expr->setRight(right); } + } else if (value->type() == geax::frontend::AstNodeType::kVBool) { + p.type = Property::VALUE; + p.value = lgraph::FieldData(((geax::frontend::VBool*)value)->val()); + if (is_end_path_) { + auto right = objAlloc_.allocate(); + right->setVal(((geax::frontend::VBool*)value)->val()); + expr->setRight(right); + } + } else if (value->type() == geax::frontend::AstNodeType::kVDouble) { + p.type = Property::VALUE; + p.value = lgraph::FieldData(((geax::frontend::VDouble*)value)->val()); + if (is_end_path_) { + auto right = objAlloc_.allocate(); + right->setVal(((geax::frontend::VDouble*)value)->val()); + expr->setRight(right); + } } else if (value->type() == geax::frontend::AstNodeType::kRef) { p.type = Property::VARIABLE; p.value = lgraph::FieldData(((geax::frontend::Ref*)value)->name()); @@ -802,7 +859,11 @@ std::any ExecutionPlanMaker::visit(geax::frontend::FilterStatement* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any ExecutionPlanMaker::visit(geax::frontend::CallQueryStatement* node) { NOT_SUPPORT(); } +std::any ExecutionPlanMaker::visit(geax::frontend::CallQueryStatement* node) { + auto procedureStatement = node->procedureStatement(); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(procedureStatement); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any ExecutionPlanMaker::visit(geax::frontend::CallProcedureStatement* node) { auto procedure = node->procedureCall(); @@ -1033,7 +1094,13 @@ std::any ExecutionPlanMaker::visit(geax::frontend::DeleteStatement* node) { std::any ExecutionPlanMaker::visit(geax::frontend::RemoveStatement* node) { NOT_SUPPORT(); } -std::any ExecutionPlanMaker::visit(geax::frontend::MergeStatement* node) { NOT_SUPPORT(); } +std::any ExecutionPlanMaker::visit(geax::frontend::MergeStatement* node) { + auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; + auto op = + new OpGqlMerge(node->onMatch(), node->onCreate(), node->pathPattern(), &pattern_graph); + _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any ExecutionPlanMaker::visit(geax::frontend::OtherWise* node) { NOT_SUPPORT(); } @@ -1053,6 +1120,26 @@ std::any ExecutionPlanMaker::visit(geax::frontend::KillStatement* node) { NOT_SU std::any ExecutionPlanMaker::visit(geax::frontend::ManagerStatement* node) { NOT_SUPPORT(); } +std::any ExecutionPlanMaker::visit(geax::frontend::UnwindStatement* node) { + ArithExprNode exp(node->list(), pattern_graphs_[cur_pattern_graph_].symbol_table); + auto unwind = + new Unwind(exp, node->variable(), &pattern_graphs_[cur_pattern_graph_].symbol_table); + _UpdateStreamRoot(unwind, pattern_graph_root_[cur_pattern_graph_]); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + +std::any ExecutionPlanMaker::visit(geax::frontend::InQueryProcedureCall* node) { + std::string name = std::get(node->name()); + std::vector expand_ops; + auto op = + new GqlInQueryCall(name, node->args(), node->yield(), &pattern_graphs_[cur_pattern_graph_]); + expand_ops.emplace_back(op); + if (auto op = _SingleBranchConnect(expand_ops)) { + _UpdateStreamRoot(op, pattern_graph_root_[cur_pattern_graph_]); + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + std::any ExecutionPlanMaker::visit(geax::frontend::DummyNode* node) { NOT_SUPPORT(); } std::any ExecutionPlanMaker::reportError() { return error_msg_; } diff --git a/src/cypher/execution_plan/execution_plan_maker.h b/src/cypher/execution_plan/execution_plan_maker.h index c22f0b8781..9a729b0096 100644 --- a/src/cypher/execution_plan/execution_plan_maker.h +++ b/src/cypher/execution_plan/execution_plan_maker.h @@ -43,6 +43,9 @@ class ExecutionPlanMaker : public geax::frontend::AstNodeVisitor { size_t cur_pattern_graph_; size_t pattern_graph_size_; + // last_op_ is used to store the root node of the operators created during pre-order traversal + OpBase* last_op_ = nullptr; + std::shared_ptr node_t_; std::shared_ptr start_t_; std::shared_ptr relp_t_; @@ -228,6 +231,8 @@ class ExecutionPlanMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::ShowProcessListStatement* node) override; std::any visit(geax::frontend::KillStatement* node) override; std::any visit(geax::frontend::ManagerStatement* node) override; + std::any visit(geax::frontend::UnwindStatement* node) override; + std::any visit(geax::frontend::InQueryProcedureCall* node) override; std::any visit(geax::frontend::DummyNode* node) override; std::any reportError() override; diff --git a/src/cypher/execution_plan/execution_plan_v2.cpp b/src/cypher/execution_plan/execution_plan_v2.cpp index cc2c2a9c83..4ab203a65c 100644 --- a/src/cypher/execution_plan/execution_plan_v2.cpp +++ b/src/cypher/execution_plan/execution_plan_v2.cpp @@ -23,11 +23,12 @@ namespace cypher { ExecutionPlanV2::~ExecutionPlanV2() { OpBase::FreeStream(root_); } -geax::frontend::GEAXErrorCode ExecutionPlanV2::Build(geax::frontend::AstNode* astNode) { +geax::frontend::GEAXErrorCode ExecutionPlanV2::Build(geax::frontend::AstNode* astNode, + RTContext* ctx) { geax::frontend::GEAXErrorCode ret = geax::frontend::GEAXErrorCode::GEAX_SUCCEED; // build pattern graph PatternGraphMaker pattern_graph_maker(pattern_graphs_); - ret = pattern_graph_maker.Build(astNode); + ret = pattern_graph_maker.Build(astNode, ctx); if (ret != geax::frontend::GEAXErrorCode::GEAX_SUCCEED) { error_msg_ = pattern_graph_maker.ErrorMsg(); return ret; @@ -134,7 +135,7 @@ std::string ExecutionPlanV2::DumpPlan(int indent, bool statistics) const { std::string ExecutionPlanV2::DumpGraph() const { std::string s; - for (auto &g : pattern_graphs_) s.append(g.DumpGraph()); + for (auto& g : pattern_graphs_) s.append(g.DumpGraph()); return s; } diff --git a/src/cypher/execution_plan/execution_plan_v2.h b/src/cypher/execution_plan/execution_plan_v2.h index 46cb022154..140f974069 100644 --- a/src/cypher/execution_plan/execution_plan_v2.h +++ b/src/cypher/execution_plan/execution_plan_v2.h @@ -23,7 +23,7 @@ class ExecutionPlanV2 { public: ExecutionPlanV2() = default; ~ExecutionPlanV2(); - geax::frontend::GEAXErrorCode Build(geax::frontend::AstNode* astNode); + geax::frontend::GEAXErrorCode Build(geax::frontend::AstNode* astNode, RTContext* ctx); int Execute(RTContext* ctx); std::string DumpPlan(int indent, bool statistics) const; std::string DumpGraph() const; diff --git a/src/cypher/execution_plan/ops/op.h b/src/cypher/execution_plan/ops/op.h index 9e7f1f95a6..e67c4ff55e 100644 --- a/src/cypher/execution_plan/ops/op.h +++ b/src/cypher/execution_plan/ops/op.h @@ -74,7 +74,9 @@ enum OpType { GQL_STANDALONE_CALL, GQL_CREATE, GQL_DELETE, - GQL_UPDATE + GQL_UPDATE, + GQL_INQUERY_CALL, + GQL_MERGE }; struct OpStats { diff --git a/src/cypher/execution_plan/ops/op_gql_inquery_call.h b/src/cypher/execution_plan/ops/op_gql_inquery_call.h new file mode 100644 index 0000000000..2527359439 --- /dev/null +++ b/src/cypher/execution_plan/ops/op_gql_inquery_call.h @@ -0,0 +1,239 @@ +/** + * Copyright 2022 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include +#include "procedure/procedure_v2.h" +#include "cypher/execution_plan/ops/op.h" +#include "cypher/procedure/utils.h" +#include "geax-front-end/ast/clause/ClauseNodeFwd.h" +#include "resultset/record.h" +#include "tools/lgraph_log.h" + +namespace cypher { +class GqlInQueryCall : public OpBase { + const std::string func_name_; + const std::vector &args_; + const std::optional &yield_; + const PatternGraph *pattern_graph_ = nullptr; + std::vector buffer_; + std::vector yield_idx_; + std::vector params_; + std::unique_ptr plugin_adapter_; + + OpResult HandOff(RTContext *ctx, std::shared_ptr &r) { + if (buffer_.empty()) return OP_DEPLETED; + auto &rec = buffer_.back(); + for (int i = 0; i < (int)yield_idx_.size(); i++) { + if (rec.values[i].IsNode()) { + cypher::Node &node = + const_cast(pattern_graph_->GetNode(rec.values[i].node->Alias())); + node.SetVid(rec.values[i].node->ID()); + node.ItRef()->Initialize(ctx->txn_->GetTxn().get(), lgraph::VIter::VERTEX_ITER, + rec.values[i].node->ID()); + r->values[yield_idx_[i]].type = Entry::NODE; + r->values[yield_idx_[i]].node = &node; + } else { + r->values[yield_idx_[i]] = rec.values[i]; + } + } + buffer_.pop_back(); + return OP_OK; + } + + public: + GqlInQueryCall(const std::string &func_name, const std::vector &args, + const std::optional &yield, + const PatternGraph *pattern_graph) + : OpBase(OpType::GQL_INQUERY_CALL, "Gql In Query Call"), + func_name_(func_name), + args_(args), + yield_(yield), + pattern_graph_(pattern_graph) { + state = StreamUnInitialized; + std::vector yield_names; + CYPHER_THROW_ASSERT(yield_.has_value()); + for (auto &item : yield_.value()->items()) { + yield_names.emplace_back(std::get<0>(item)); + } + modifies = std::move(yield_names); + } + + OpResult Initialize(RTContext *ctx) override { + auto &sym_tab = pattern_graph_->symbol_table; + if (children.empty()) { + record = std::make_shared(sym_tab.symbols.size(), &sym_tab, ctx->param_tab_); + } else { + CYPHER_THROW_ASSERT(children.size() == 1); + if (children[0]->Initialize(ctx) != OP_OK) return OP_ERR; + record = children[0]->record; + } + for (auto &item : yield_.value()->items()) { + auto it = sym_tab.symbols.find(std::get<0>(item)); + if (it == sym_tab.symbols.end()) CYPHER_TODO(); + yield_idx_.emplace_back(it->second.id); + } + + // build plugin adapter + auto names = fma_common::Split(func_name_, "."); + if (names.size() > 2 && names[0] == "plugin" && names[2] != "list") { + std::string input, output; + auto type = names[1] == "cpp" ? lgraph::PluginManager::PluginType::CPP + : lgraph::PluginManager::PluginType::PYTHON; + if (type == lgraph::PluginManager::PluginType::PYTHON) { + throw lgraph::EvaluationException( + "Calling python plugin in CYPHER is disabled in this release."); + } + auto pm = ctx->ac_db_->GetLightningGraph()->GetPluginManager(); + lgraph_api::SigSpec *sig_spec = nullptr; + pm->GetPluginSignature(type, "A_DUMMY_TOKEN_FOR_CPP_PLUGIN", names[2], &sig_spec); + // it's ok for old plugins without signature + // plugins without signature not support to be called using plugin_adapter_ + // use Utils::CallPlugin instead + if (sig_spec) { + plugin_adapter_ = std::make_unique(sig_spec, type, names[2]); + for (const auto &expr : args_) { + params_.emplace_back(expr, sym_tab); + } + } + } + return OP_OK; + } + + OpResult RealConsume(RTContext *ctx) override { + auto names = fma_common::Split(func_name_, "."); + if (names.size() > 2 && names[0] == "plugin") { + auto type = names[1] == "cpp" ? lgraph::PluginManager::PluginType::CPP + : lgraph::PluginManager::PluginType::PYTHON; + if (type == lgraph::PluginManager::PluginType::PYTHON) { + throw lgraph::EvaluationException( + "Calling python plugin in CYPHER is disabled in this release."); + } + auto token = "A_DUMMY_TOKEN_FOR_CPP_PLUGIN"; + CYPHER_THROW_ASSERT(ctx->ac_db_); + if (names[2] == "list") { + buffer_.clear(); + auto plugins = ctx->ac_db_->ListPlugins(type, token); + for (auto &p : plugins) { + std::string s; + s.append(p.name).append(" | ").append(p.desc).append(" | ").append( + p.read_only ? "READ" : "WRITE"); + Record r; + r.AddConstant(lgraph::FieldData(s)); + buffer_.emplace_back(std::move(r)); + } + } else { + if (!plugin_adapter_) CYPHER_TODO(); + // N.B. HERE NOT ABORT TXN, OTHERWISE WILL INVALID CHILDREN OPERATORS' ITERATOR + // THE ROOT PROBLEM IS PLUGIN DESIGN PROBLEM. + // `Process` function cannot accept ctx parameter or txn parameter + // + // N.B. signatured plugins cannot open txn in process, otherwise it will + // cause sub-txn problem. + // call custom plugin should have plugin_adapter + CYPHER_THROW_ASSERT(plugin_adapter_ != nullptr); + if (children.empty()) { + if (HandOff(ctx, record) == OP_OK) return OP_OK; + if (state == StreamDepleted) return OP_DEPLETED; + plugin_adapter_->Process(ctx, params_, *record, &buffer_); + std::reverse(buffer_.begin(), buffer_.end()); + state = StreamDepleted; + return HandOff(ctx, record); + } else { + if (HandOff(ctx, record) == OP_OK) return OP_OK; + auto child = children[0]; + while (child->Consume(ctx) == OP_OK) { + plugin_adapter_->Process(ctx, params_, *record, &buffer_); + std::reverse(buffer_.begin(), buffer_.end()); + if (HandOff(ctx, record) == OP_OK) return OP_OK; + } + return OP_DEPLETED; + } + } + } else { + if (children.empty()) { + if (HandOff(ctx, record) == OP_OK) return OP_OK; + if (state == StreamDepleted) return OP_DEPLETED; + auto p = global_ptable_v2.GetProcedureV2(func_name_); + if (!p) { + throw lgraph::EvaluationException("unregistered standalone function: " + + func_name_); + } + std::vector _yield_items; + if (yield_.has_value()) { + for (auto &pair : yield_.value()->items()) { + _yield_items.emplace_back(std::get<0>(pair)); + } + } + std::vector parameters; + parameters.reserve(args_.size()); + for (auto expr : args_) { + ArithExprNode node(expr, pattern_graph_->symbol_table); + // TODO(lingsu): dummy record + parameters.emplace_back(node.Evaluate(ctx, *record.get())); + } + p->function(ctx, nullptr, parameters, _yield_items, &buffer_); + std::reverse(buffer_.begin(), buffer_.end()); + state = StreamDepleted; + return HandOff(ctx, record); + } else { + if (HandOff(ctx, record) == OP_OK) return OP_OK; + auto child = children[0]; + while (child->Consume(ctx) == OP_OK) { + auto p = global_ptable_v2.GetProcedureV2(func_name_); + if (!p) { + throw lgraph::EvaluationException("unregistered standalone function: " + + func_name_); + } + std::vector records; + std::vector _yield_items; + if (yield_.has_value()) { + for (auto &pair : yield_.value()->items()) { + _yield_items.emplace_back(std::get<0>(pair)); + } + } + std::vector parameters; + parameters.reserve(args_.size()); + for (auto expr : args_) { + ArithExprNode node(expr, pattern_graph_->symbol_table); + // TODO(lingsu): dummy record + parameters.emplace_back(node.Evaluate(ctx, Record(1))); + } + p->function(ctx, nullptr, parameters, _yield_items, &records); + std::reverse(buffer_.begin(), buffer_.end()); + if (HandOff(ctx, record) == OP_OK) return OP_OK; + } + return OP_DEPLETED; + } + } + return OP_DEPLETED; + } + + OpResult ResetImpl(bool complete = false) override { + state = StreamUnInitialized; + return OP_OK; + } + + std::string ToString() const override { + std::string str(name); + str.append(" [").append(func_name_).append("]"); + return str; + } + + CYPHER_DEFINE_VISITABLE() + + CYPHER_DEFINE_CONST_VISITABLE() +}; +} // namespace cypher diff --git a/src/cypher/execution_plan/ops/op_gql_merge.cpp b/src/cypher/execution_plan/ops/op_gql_merge.cpp new file mode 100644 index 0000000000..44ce7da9e5 --- /dev/null +++ b/src/cypher/execution_plan/ops/op_gql_merge.cpp @@ -0,0 +1,15 @@ +/** + * Copyright 2022 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#include "cypher/execution_plan/ops/op_gql_merge.h" diff --git a/src/cypher/execution_plan/ops/op_gql_merge.h b/src/cypher/execution_plan/ops/op_gql_merge.h new file mode 100644 index 0000000000..546feb6138 --- /dev/null +++ b/src/cypher/execution_plan/ops/op_gql_merge.h @@ -0,0 +1,544 @@ +/** + * Copyright 2022 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include "cypher/execution_plan/ops/op.h" +#include "cypher/utils/geax_util.h" +#include "cypher/arithmetic//arithmetic_expression.h" +#include "geax-front-end/ast/clause/ClauseNodeFwd.h" + +namespace cypher { + +class OpGqlMerge : public OpBase { + const std::vector &on_match_items_; + const std::vector &on_create_items_; + const geax::frontend::PathPattern *path_pattern_; + const SymbolTable &sym_tab_; + PatternGraph *pattern_graph_ = nullptr; + bool standalone_; + bool summary_; + + void ExtractProperties(RTContext *ctx, std::vector &fields, + std::vector &values, + const geax::frontend::ElementFiller *filler) { + auto &props = filler->predicates(); + if (props.size() > 1) CYPHER_TODO(); + for (auto p : props) { + geax::frontend::PropStruct *prop = nullptr; + checkedCast(p, prop); + if (!prop) CYPHER_TODO(); + for (auto &property : prop->properties()) { + fields.emplace_back(std::get<0>(property)); + ArithExprNode ae(std::get<1>(property), *record->symbol_table); + auto value = ae.Evaluate(ctx, *record); + CYPHER_THROW_ASSERT(value.IsScalar()); + values.emplace_back(value.constant.scalar); + } + } + } + + void ExtractLabelTree(std::vector &labels, const geax::frontend::LabelTree *root) { + if (typeid(*root) == typeid(geax::frontend::SingleLabel)) { + labels.emplace_back(((geax::frontend::SingleLabel *)root)->label()); + } else if (typeid(*root) == typeid(geax::frontend::LabelOr)) { + geax::frontend::LabelOr *label_or = (geax::frontend::LabelOr *)root; + ExtractLabelTree(labels, label_or->left()); + ExtractLabelTree(labels, label_or->right()); + } else { + CYPHER_TODO(); + } + } + + void ExtractProperties(RTContext *ctx, const std::string &var, std::vector &fields, + std::vector &values, + const std::vector set_items) { + for (auto set_item : set_items) { + for (auto &item : set_item->items()) { + if (typeid(*item) != typeid(geax::frontend::UpdateProperties)) CYPHER_TODO(); + geax::frontend::UpdateProperties *props = nullptr; + checkedCast(item, props); + if (props->v() != var) { + throw lgraph::CypherException( + fma_common::StringFormatter::Format("Variable `{}` not found", props->v())); + } + auto properties = props->structs()->properties(); + for (auto property : properties) { + fields.emplace_back(std::get<0>(property)); + ArithExprNode ae(std::get<1>(property), *record->symbol_table); + auto val = ae.Evaluate(ctx, *record); + if (!val.IsScalar()) CYPHER_TODO(); + values.emplace_back(val.constant.scalar); + } + } + } + } + + struct MatchIterator { + std::string label; + std::vector field_names; + std::vector field_values; + MatchIterator(lgraph::Transaction *txn, const std::string &l, + const std::vector &fns, + const std::vector &fvs, Node *n) + : label(l), field_names(fns), field_values(fvs), _txn(txn), node_p(n) { + if (field_names.size() == field_values.size()) { + /* there must be at least 1 valid index */ + for (int i = 0; i < (int)field_names.size(); i++) { + if (!_txn->IsIndexed(label, field_names[i])) continue; + _iit = new lgraph::VertexIndexIterator(_txn->GetVertexIndexIterator( + label, field_names[i], field_values[i], field_values[i])); + while (_iit->IsValid()) { + if (CheckFields()) { + /* ON MATCH */ + valid_ = true; + break; + } + _iit->Next(); + } + break; + } + } + } + + DISABLE_COPY(MatchIterator); + + ~MatchIterator() { + delete _iit; + _iit = nullptr; + } + + bool Next() { + if (!_iit) return false; + valid_ = false; + while (_iit->IsValid()) { + _iit->Next(); + if (_iit->IsValid() && CheckFields()) { + valid_ = true; + break; + } + } + return valid_; + } + + bool IsMatch() const { return node_p->derivation_ == Node::Derivation::MATCHED; } + + bool IsValid() const { return _iit && valid_; } + + bool IsIndexValid() const { return _iit != nullptr; } + + int64_t GetVid() const { + return IsMatch() ? node_p->PullVid() : IsValid() ? _iit->GetVid() : -1; + } + + private: + lgraph::Transaction *_txn = nullptr; + lgraph::VertexIndexIterator *_iit = nullptr; + bool valid_ = false; + Node *node_p; + + bool CheckFields() const { + if (!_iit || !_iit->IsValid()) return false; + auto vit = _txn->GetVertexIterator(_iit->GetVid()); + size_t j = 0; + for (auto &fn : field_names) { + if (_txn->GetVertexField(vit, fn) != field_values[j]) break; + j++; + } + return j == field_names.size(); + } + }; + + lgraph::VertexId MergeVertex(RTContext *ctx, MatchIterator &&vertex, + const std::vector &field_names_on_create, + const std::vector &field_values_on_create, + const std::vector &field_names_on_match, + const std::vector &field_values_on_match, + std::string &err) { + lgraph::VertexId res = 0; + /* check txn */ + if (!ctx->txn_->IsValid() || ctx->txn_->IsReadOnly()) { + err = "invalid txn"; + return -1; + } + if (vertex.IsValid()) { + /* ON MATCH */ + std::vector match_vids; + do { + match_vids.emplace_back(vertex.GetVid()); + vertex.Next(); + } while (vertex.IsValid()); + auto vit = ctx->txn_->GetTxn()->GetVertexIterator(); + for (auto vid : match_vids) { + vit.Goto(vid); + ctx->txn_->GetTxn()->SetVertexProperty(vit, field_names_on_match, + field_values_on_match); + } + res = match_vids.back(); + } else if (vertex.IsIndexValid()) { + /* ON CREATE */ + std::vector fns; + std::vector fvs; + fns.insert(fns.end(), vertex.field_names.begin(), vertex.field_names.end()); + fns.insert(fns.end(), field_names_on_create.begin(), field_names_on_create.end()); + fvs.insert(fvs.end(), vertex.field_values.begin(), vertex.field_values.end()); + fvs.insert(fvs.end(), field_values_on_create.begin(), field_values_on_create.end()); + int64_t vid = ctx->txn_->AddVertex(vertex.label, fns, fvs); + res = vid; + } else { + err = "invalid index "; + return -1; + } + return res; + } + + lgraph_api::EdgeUid MergeEdge(RTContext *ctx, MatchIterator &&src, MatchIterator &&dst, + const std::string &label, + const std::vector &field_names, + const std::vector &field_values, + const std::vector &field_names_on_create, + const std::vector &field_values_on_create, + const std::vector &field_names_on_match, + const std::vector &field_values_on_match, + std::string &err) { + /* check txn */ + lgraph_api::EdgeUid eid_res; + if (!ctx->txn_->IsValid() || ctx->txn_->IsReadOnly()) { + err = "invalid txn"; + return eid_res; + } + if ((!src.IsValid() && !src.IsMatch()) || (!dst.IsValid() && !dst.IsMatch())) { + err = "invalid vertex"; + return eid_res; + } + if (field_names.size() != field_values.size()) { + err = "number of fields and data values do not match"; + return eid_res; + } + std::vector src_ids, dst_ids; + if (src.IsMatch()) { + src_ids.emplace_back(src.GetVid()); + } else { + while (src.IsValid()) { + src_ids.emplace_back(src.GetVid()); + src.Next(); + } + } + if (dst.IsMatch()) { + dst_ids.emplace_back(dst.GetVid()); + } else { + while (dst.IsValid()) { + dst_ids.emplace_back(dst.GetVid()); + dst.Next(); + } + } + for (auto src_id : src_ids) { + for (auto dst_id : dst_ids) { + auto eit = ctx->txn_->GetTxn()->GetVertexIterator(src_id).GetOutEdgeIterator(); + std::vector match_eids; + while (eit.IsValid()) { + if (eit.GetDst() == dst_id) { + size_t j = 0; + for (auto &fn : field_names) { + if (ctx->txn_->GetTxn()->GetEdgeField(eit, fn) != field_values[j]) + break; + j++; + } + if (ctx->txn_->GetTxn()->GetEdgeLabel(eit) == label && + j == field_names.size()) { + match_eids.emplace_back(src_id, dst_id, eit.GetLabelId(), + eit.GetTemporalId(), eit.GetEdgeId()); + } + } + eit.Next(); + } + if (!match_eids.empty()) { + /* ON MATCH */ + for (auto uid : match_eids) { + eit.Goto(uid, true); + ctx->txn_->GetTxn()->SetEdgeProperty(eit, field_names_on_match, + field_values_on_match); + } + eid_res = match_eids.back(); + } else { + /* ON CREATE */ + std::vector fns; + std::vector fvs; + fns.insert(fns.end(), field_names.begin(), field_names.end()); + fns.insert(fns.end(), field_names_on_create.begin(), + field_names_on_create.end()); + fvs.insert(fvs.end(), field_values.begin(), field_values.end()); + fvs.insert(fvs.end(), field_values_on_create.begin(), + field_values_on_create.end()); + eid_res = ctx->txn_->AddEdge(src_id, dst_id, label, fns, fvs); + } + } + } + return eid_res; + } + + void MergeNode(RTContext *ctx, const geax::frontend::Node *node_pattern) { + std::vector field_names; + std::vector field_values; + std::vector field_names_on_match; + std::vector field_values_on_match; + std::vector field_names_on_create; + std::vector field_values_on_create; // need to be initalized by "" + std::string err_msg; + if (!node_pattern->filler()->v().has_value()) CYPHER_TODO(); + auto node_variable = node_pattern->filler()->v().value(); // string + std::vector labels; + if (node_pattern->filler()->label().has_value()) { + ExtractLabelTree(labels, node_pattern->filler()->label().value()); + } + std::vector fields; + std::vector values; + ExtractProperties(ctx, field_names, field_values, node_pattern->filler()); + auto &node_patt = pattern_graph_->GetNode(node_variable); + if (labels.empty()) throw lgraph::EvaluationException("vertex label missing in merge."); + const std::string &node_label = *labels.begin(); + + ExtractProperties(ctx, node_variable, field_names_on_match, field_values_on_match, + on_match_items_); + ExtractProperties(ctx, node_variable, field_names_on_create, field_values_on_create, + on_create_items_); + auto node_res = MergeVertex(ctx, + MatchIterator(ctx->txn_->GetTxn().get(), node_label, + field_names, field_values, &node_patt), + field_names_on_create, field_values_on_create, + field_names_on_match, field_values_on_match, err_msg); + // check node_res whether valid + // + // When given merge node pattern, if none of field names is indexed in given label + // MergeVertex return a invalid node_res(-1) + // + // # Error + // invalid cypher example: + // MERGE (n:null {id: 2909}) RETURN n + // see issue: https://code.alipay.com/fma/tugraph-db/issues/340 + if (node_res == -1) { + throw lgraph::CypherException("cannot match node with given label and properties"); + } + + // TODO(anyone) When multiple nodes are matched, all data should be processed + ctx->result_info_->statistics.vertices_created++; + if (!node_variable.empty()) { + auto node = &pattern_graph_->GetNode(node_variable); + if (node->Empty()) CYPHER_TODO(); + node->Visited() = true; + node->ItRef()->Initialize(ctx->txn_->GetTxn().get(), lgraph::VIter::VERTEX_ITER, + node_res); + if (!summary_) { + auto it = sym_tab_.symbols.find(node_variable); + CYPHER_THROW_ASSERT(it != sym_tab_.symbols.end()); + record->values[it->second.id].type = Entry::NODE; + record->values[it->second.id].node = node; + } + } + } + + void MergeChains(RTContext *ctx, const geax::frontend::Node *node_patt, + const geax::frontend::Node *rls_node_pattern, + const geax::frontend::Edge *rls_patt) { + using namespace parser; + auto src_node_var = node_patt->filler()->v().value(); + auto dst_node_var = rls_node_pattern->filler()->v().value(); + auto direction = rls_patt->direction(); + if (direction == geax::frontend::EdgeDirection::kPointLeft) { + std::swap(src_node_var, dst_node_var); + } else if (direction != geax::frontend::EdgeDirection::kPointRight) { + throw lgraph::CypherException("Only directed relationships are supported in merge"); + } + auto &edge_variable = rls_patt->filler()->v().value(); + std::vector edge_labels; + if (rls_patt->filler()->label().has_value()) { + ExtractLabelTree(edge_labels, rls_patt->filler()->label().value()); + } + if (edge_labels.empty()) throw lgraph::CypherException("edge label missing in merge"); + auto &src_node = pattern_graph_->GetNode(src_node_var); + auto &dst_node = pattern_graph_->GetNode(dst_node_var); // get node.Lable() get node.Prop() + auto src = src_node.ItRef(); + auto dst = dst_node.ItRef(); + auto &label = edge_labels[0]; + if (!src->IsValid() || !dst->IsValid()) { + std::string err = src->IsValid() ? dst_node_var : src_node_var; + throw lgraph::CypherException("Invalid vertex when create edge: " + err); + } + const std::string &src_label = src_node.Label(); + // TODO(anyone) Currently only one field can be obtained + const std::vector &src_field_name{src_node.Prop().field}; + const std::vector &src_field_value{src_node.Prop().value}; + const std::string &dst_label = dst_node.Label(); + const std::vector dst_field_name{dst_node.Prop().field}; + const std::vector dst_field_value{dst_node.Prop().value}; + std::vector fields; + std::vector values; + std::vector field_names_on_match; + std::vector field_values_on_match; + std::vector field_names_on_create; + std::vector field_values_on_create; + std::string err_msg; + + ExtractProperties(ctx, fields, values, rls_patt->filler()); + ExtractProperties(ctx, edge_variable, field_names_on_match, field_values_on_match, + on_match_items_); + ExtractProperties(ctx, edge_variable, field_names_on_create, field_values_on_create, + on_create_items_); + + auto res = MergeEdge(ctx, + MatchIterator(ctx->txn_->GetTxn().get(), src_label, src_field_name, + src_field_value, &src_node), + MatchIterator(ctx->txn_->GetTxn().get(), dst_label, dst_field_name, + dst_field_value, &dst_node), + label, fields, values, field_names_on_create, field_values_on_create, + field_names_on_match, field_values_on_match, err_msg); + ctx->result_info_->statistics.edges_created++; + if (!edge_variable.empty()) { + auto relp = &pattern_graph_->GetRelationship(edge_variable); + if (relp->Empty()) CYPHER_TODO(); + relp->ItRef()->Initialize(ctx->txn_->GetTxn().get(), res); + if (!summary_) { + auto it = sym_tab_.symbols.find(edge_variable); + CYPHER_THROW_ASSERT(it != sym_tab_.symbols.end()); + record->values[it->second.id].type = Entry::RELATIONSHIP; + record->values[it->second.id].relationship = relp; + } + } + } + + void MergeVE(RTContext *ctx) { + if (path_pattern_->prefix().has_value()) CYPHER_TODO(); + auto pattern_element = path_pattern_->chains(); // TUP_PATTERN_ELEMENT + for (auto chain : path_pattern_->chains()) { + auto start_node = chain->head(); + auto pattern_element_chains = chain->tails(); + if (pattern_element_chains.empty()) { // create vertex + MergeNode(ctx, start_node); + } else { // create chains + for (auto &chain : pattern_element_chains) { + auto rel = (geax::frontend::Edge *)std::get<0>(chain); + auto end_node = std::get<1>(chain); + if (!pattern_graph_->GetNode(start_node->filler()->v().value()).Visited()) { + // for eg: match(node1),(node2) or + // merge(m:person{name:""}) + // -[r:konws{}]->(n:person{name:""}) + MergeNode(ctx, start_node); + } + if (!pattern_graph_->GetNode(end_node->filler()->v().value()).Visited()) { + // for eg: merge(m:person{name:""}) + // -[r:konws{}]->(n:person{name:""}) + MergeNode(ctx, end_node); + } + MergeChains(ctx, start_node, end_node, rel); + start_node = end_node; + } + } // end else + } + } + + void ResultSummary(RTContext *ctx) { + if (summary_) { + std::string summary; + summary.append("merged ") + .append(std::to_string(ctx->result_info_->statistics.vertices_created)) + .append(" vertices, merged ") + .append(std::to_string(ctx->result_info_->statistics.edges_created)) + .append(" edges."); + CYPHER_THROW_ASSERT(ctx->result_->Header().size() == 1); + CYPHER_THROW_ASSERT(record); + record->values.clear(); + record->AddConstant(lgraph::FieldData(summary)); + } else { + /* There should be a "Project" operation on top of this + * "Create", so leave result set to it. */ + } + } + + public: + OpGqlMerge(const std::vector &on_match_items, + const std::vector &on_create_items, + const geax::frontend::PathPattern *path_pattern, PatternGraph *pattern_graph) + : OpBase(OpType::GQL_MERGE, "Gql Merge"), + on_match_items_(on_match_items), + on_create_items_(on_create_items), + path_pattern_(path_pattern), + sym_tab_(pattern_graph->symbol_table), + pattern_graph_(pattern_graph) { + for (auto &node : pattern_graph_->GetNodes()) { + if (node.derivation_ == Node::MERGED) modifies.emplace_back(node.Alias()); + for (auto rr : node.RhsRelps()) { + auto &r = pattern_graph_->GetRelationship(rr); + if (r.derivation_ == Relationship::MERGED) modifies.emplace_back(r.Alias()); + } + } + } + + OpResult Initialize(RTContext *ctx) override { + CYPHER_THROW_ASSERT(parent && children.size() < 2); + summary_ = !parent->parent; + standalone_ = children.empty(); + for (auto child : children) { + child->Initialize(ctx); + } + record = children.empty() + ? std::make_shared(sym_tab_.symbols.size(), &sym_tab_, ctx->param_tab_) + : children[0]->record; + return OP_OK; + } + + OpResult RealConsume(RTContext *ctx) override { + if (state == StreamDepleted) return OP_DEPLETED; + if (standalone_) { + MergeVE(ctx); + ResultSummary(ctx); + state = StreamDepleted; + return OP_OK; + } else { + if (children.size() > 1) CYPHER_TODO(); + auto child = children[0]; + if (summary_) { + while (child->Consume(ctx) == OP_OK) MergeVE(ctx); + ResultSummary(ctx); + state = StreamDepleted; + return OP_OK; + } else { + if (child->Consume(ctx) == OP_OK) { + MergeVE(ctx); + return OP_OK; + } else { + return OP_DEPLETED; + } + } + } + } + + OpResult ResetImpl(bool complete) override { + state = StreamUnInitialized; + return OP_OK; + } + + std::string ToString() const override { + std::string str(name); + str.append(" ["); + for (auto &m : modifies) str.append(m).append(","); + if (!modifies.empty()) str.pop_back(); + str.append("]"); + return str; + } + + CYPHER_DEFINE_VISITABLE() + + CYPHER_DEFINE_CONST_VISITABLE() +}; +} // namespace cypher diff --git a/src/cypher/execution_plan/ops/op_gql_set.h b/src/cypher/execution_plan/ops/op_gql_set.h index 3511fee594..a530d3b08f 100644 --- a/src/cypher/execution_plan/ops/op_gql_set.h +++ b/src/cypher/execution_plan/ops/op_gql_set.h @@ -114,7 +114,7 @@ class OpGqlSet : public OpBase { public: OpGqlSet(const std::vector& set_items, PatternGraph *pattern_graph) - : OpBase(OpType::GQL_UPDATE, "Gql Set") + : OpBase(OpType::GQL_UPDATE, "GqlSet") , set_items_(set_items) { state = StreamUnInitialized; } diff --git a/src/cypher/execution_plan/ops/op_gql_standalone_call.cpp b/src/cypher/execution_plan/ops/op_gql_standalone_call.cpp index 7f01e29384..e705a62d88 100644 --- a/src/cypher/execution_plan/ops/op_gql_standalone_call.cpp +++ b/src/cypher/execution_plan/ops/op_gql_standalone_call.cpp @@ -13,20 +13,87 @@ */ #include "procedure/utils.h" +#include "cypher/procedure/procedure_v2.h" #include "cypher/execution_plan/ops/op_gql_standalone_call.h" -#include "procedure/procedure.h" #include "resultset/record.h" #include "server/json_convert.h" #include "arithmetic/arithmetic_expression.h" -#include "tools/lgraph_log.h" +#include "db/galaxy.h" cypher::OpBase::OpResult cypher::GqlStandaloneCall::RealConsume(RTContext *ctx) { auto names = fma_common::Split(func_name_, "."); if (names.size() > 2 && names[0] == "plugin") { - CYPHER_TODO(); + std::string input, output; + auto type = names[1] == "cpp" ? lgraph::PluginManager::PluginType::CPP + : lgraph::PluginManager::PluginType::PYTHON; + if (type == lgraph::PluginManager::PluginType::PYTHON) { + throw lgraph::EvaluationException( + "Calling python plugin in CYPHER is disabled in this release."); + } + auto token = "A_DUMMY_TOKEN_FOR_CPP_PLUGIN"; + auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + if (names[2] == "list") { + auto plugins = ac_db.ListPlugins(type, token); + // TODO(anyone): return records other than just strings + auto &header = ctx->result_->Header(); + if (header.size() > 1) + THROW_CODE(InputError, "Plugin [{}] header is excaption.", names[2]); + auto title = header[0].first; + for (auto &p : plugins) { + std::string s; + s.append(p.name).append(" | ").append(p.desc).append(" | ").append( + p.read_only ? "READ" : "WRITE"); + + auto r = ctx->result_->MutableRecord(); + r->Insert(title, lgraph::FieldData(s)); + } + } else { + if (ctx->txn_) ctx->txn_->Abort(); + bool exists = Utils::CallPlugin(*ctx, type, names[2], args_, output); + if (!exists) THROW_CODE(InputError, "Plugin [{}] does not exist.", names[2]); + ctx->result_->Load(output); +#if 0 + /* TODO(anyone): redundant parse */ + try { + std::vector headers; + std::vector records; + size_t i = 0, size = 0; + auto res = nlohmann::json::parse(output); + CYPHER_THROW_ASSERT(res.is_array()); + for (auto it = res.begin(); it != res.end(); it++) { + CYPHER_THROW_ASSERT(it.value().is_object()); + Record r; + bool fill_header = headers.empty(); + for (auto it2 = it.value().begin(); it2 != it.value().end(); it2++) { + if (fill_header) headers.emplace_back(it2.key()); + std::stringstream ss; + ss << it2.value(); + r.AddConstant(lgraph::FieldData(ss.str())); + } + records.emplace_back(r); + } + ctx->result_info_->header.colums.clear(); + for (auto &h : headers) { + ctx->result_info_->header.colums.emplace_back(h); + } + for (auto &r : records) ctx->result_info_->AddRecord(r); + } catch (std::exception &e) { + throw lgraph::EvaluationException(std::string("parse json error: ") + e.what()); + } +#endif + } } else { + auto p = global_ptable_v2.GetProcedureV2(func_name_); + if (!p) { + throw lgraph::EvaluationException("unregistered standalone function: " + func_name_); + } std::vector records; std::vector _yield_items; + if (yield_.has_value()) { + for (auto &pair : yield_.value()->items()) { + _yield_items.emplace_back(std::get<0>(pair)); + } + } std::vector parameters; parameters.reserve(args_.size()); for (auto expr : args_) { @@ -34,7 +101,7 @@ cypher::OpBase::OpResult cypher::GqlStandaloneCall::RealConsume(RTContext *ctx) // TODO(lingsu): dummy record parameters.emplace_back(node.Evaluate(ctx, Record(1))); } - procedure_->function(ctx, nullptr, parameters, _yield_items, &records); + p->function(ctx, nullptr, parameters, _yield_items, &records); auto &header = ctx->result_->Header(); for (auto &r : records) { auto record = ctx->result_->MutableRecord(); diff --git a/src/cypher/execution_plan/ops/op_gql_standalone_call.h b/src/cypher/execution_plan/ops/op_gql_standalone_call.h index 9f903c800e..ca49350493 100644 --- a/src/cypher/execution_plan/ops/op_gql_standalone_call.h +++ b/src/cypher/execution_plan/ops/op_gql_standalone_call.h @@ -14,7 +14,6 @@ #pragma once -#include "procedure/procedure_v2.h" #include "cypher/execution_plan/ops/op.h" namespace cypher { @@ -23,7 +22,6 @@ class GqlStandaloneCall : public OpBase { const std::string func_name_; const std::vector& args_; const std::optional& yield_; - ProcedureV2 *procedure_ = nullptr; const SymbolTable& symbol_table_; public: @@ -38,11 +36,6 @@ class GqlStandaloneCall : public OpBase { , symbol_table_(symbol_table) {} OpResult Initialize(RTContext *ctx) override { - auto p = global_ptable_v2.GetProcedureV2(func_name_); - if (!p) { - throw lgraph::EvaluationException("unregistered standalone function: " + func_name_); - } - procedure_ = p; return OP_OK; } diff --git a/src/cypher/execution_plan/ops/ops.h b/src/cypher/execution_plan/ops/ops.h index e41d11c2b4..0783461db4 100644 --- a/src/cypher/execution_plan/ops/ops.h +++ b/src/cypher/execution_plan/ops/ops.h @@ -30,6 +30,7 @@ #include "cypher/execution_plan/ops/op_var_len_expand.h" #include "cypher/execution_plan/ops/op_var_len_expand_into.h" #include "cypher/execution_plan/ops/op_inquery_call.h" +#include "cypher/execution_plan/ops/op_gql_inquery_call.h" #include "cypher/execution_plan/ops/op_aggregate.h" #include "cypher/execution_plan/ops/op_project.h" #include "cypher/execution_plan/ops/op_sort.h" @@ -47,6 +48,7 @@ #include "cypher/execution_plan/ops/op_relationship_count.h" #include "cypher/execution_plan/ops/op_distinct.h" #include "cypher/execution_plan/ops/op_merge.h" +#include "cypher/execution_plan/ops/op_gql_merge.h" #include "cypher/execution_plan/ops/op_remove.h" #include "cypher/execution_plan/ops/op_skip.h" #include "cypher/execution_plan/ops/op_all_node_scan_dynamic.h" diff --git a/src/cypher/execution_plan/pattern_graph_maker.cpp b/src/cypher/execution_plan/pattern_graph_maker.cpp index 032bb002a0..6064277caa 100644 --- a/src/cypher/execution_plan/pattern_graph_maker.cpp +++ b/src/cypher/execution_plan/pattern_graph_maker.cpp @@ -16,10 +16,14 @@ #include "cypher/utils/geax_util.h" #include "cypher/execution_plan/clause_guard.h" #include "cypher/execution_plan/pattern_graph_maker.h" +#include "cypher/procedure/procedure_v2.h" +#include "db/galaxy.h" namespace cypher { -geax::frontend::GEAXErrorCode PatternGraphMaker::Build(geax::frontend::AstNode* astNode) { +geax::frontend::GEAXErrorCode PatternGraphMaker::Build(geax::frontend::AstNode* astNode, + RTContext* ctx) { + ctx_ = ctx; cur_pattern_graph_ = -1; cur_types_.clear(); return std::any_cast(astNode->accept(*this)); @@ -182,6 +186,9 @@ std::any PatternGraphMaker::visit(geax::frontend::ElementFiller* node) { } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kInsertStatement, cur_types_)) { node_t_->derivation_ = Node::Derivation::CREATED; + } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kMergeStatement, + cur_types_)) { + node_t_->derivation_ = Node::Derivation::MERGED; } else { NOT_SUPPORT(); } @@ -190,6 +197,11 @@ std::any PatternGraphMaker::visit(geax::frontend::ElementFiller* node) { if (it->second.scope == cypher::SymbolNode::ARGUMENT) { node_t_->derivation_ = Node::Derivation::ARGUMENT; } + if (ClauseGuard::InClause(geax::frontend::AstNodeType::kMergeStatement, cur_types_)) { + if (it->second.scope == cypher::SymbolNode::LOCAL) { + node_t_->Visited() = true; + } + } } } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kEdge, cur_types_)) { relp_t_->SetAlias(variable.value()); @@ -203,6 +215,9 @@ std::any PatternGraphMaker::visit(geax::frontend::ElementFiller* node) { } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kInsertStatement, cur_types_)) { relp_t_->derivation_ = Relationship::Derivation::CREATED; + } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kMergeStatement, + cur_types_)) { + relp_t_->derivation_ = Relationship::Derivation::MERGED; } else { NOT_SUPPORT(); } @@ -267,7 +282,8 @@ std::any PatternGraphMaker::visit(geax::frontend::LabelAnd* node) { NOT_SUPPORT( std::any PatternGraphMaker::visit(geax::frontend::LabelNot* node) { NOT_SUPPORT(); } std::any PatternGraphMaker::visit(geax::frontend::PropStruct* node) { - if (ClauseGuard::InClause(geax::frontend::AstNodeType::kMatchStatement, cur_types_)) { + if (ClauseGuard::InClause(geax::frontend::AstNodeType::kMatchStatement, cur_types_) || + ClauseGuard::InClause(geax::frontend::AstNodeType::kMergeStatement, cur_types_)) { auto& properties = node->properties(); if (properties.size() != 1) { NOT_SUPPORT(); @@ -282,6 +298,12 @@ std::any PatternGraphMaker::visit(geax::frontend::PropStruct* node) { } else if (value->type() == geax::frontend::AstNodeType::kVInt) { p.type = Property::VALUE; p.value = lgraph::FieldData(((geax::frontend::VInt*)value)->val()); + } else if (value->type() == geax::frontend::AstNodeType::kVBool) { + p.type = Property::VALUE; + p.value = lgraph::FieldData(((geax::frontend::VBool*)value)->val()); + } else if (value->type() == geax::frontend::AstNodeType::kVDouble) { + p.type = Property::VALUE; + p.value = lgraph::FieldData(((geax::frontend::VDouble*)value)->val()); } else if (value->type() == geax::frontend::AstNodeType::kRef) { p.type = Property::VARIABLE; p.value = lgraph::FieldData(((geax::frontend::Ref*)value)->name()); @@ -297,7 +319,7 @@ std::any PatternGraphMaker::visit(geax::frontend::PropStruct* node) { node_t_->SetProperty(p); return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kEdge, cur_types_)) { - NOT_SUPPORT(); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } NOT_SUPPORT(); } else if (ClauseGuard::InClause(geax::frontend::AstNodeType::kInsertStatement, cur_types_)) { @@ -307,7 +329,67 @@ std::any PatternGraphMaker::visit(geax::frontend::PropStruct* node) { } } -std::any PatternGraphMaker::visit(geax::frontend::YieldField* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::YieldField* node) { + auto p = global_ptable_v2.GetProcedureV2(curr_procedure_name_); + if (p) { + for (auto& pair : node->items()) { + const std::string& name = std::get<0>(pair); + auto type = lgraph_api::LGraphType::NUL; + for (auto& r : p->signature.result_list) { + if (r.name == name) { + type = r.type; + } + } + switch (type) { + case lgraph_api::LGraphType::NODE: + AddSymbol(name, cypher::SymbolNode::NODE, cypher::SymbolNode::LOCAL); + break; + default: + AddSymbol(name, cypher::SymbolNode::CONSTANT, cypher::SymbolNode::LOCAL); + break; + } + } + } else { + auto names = fma_common::Split(curr_procedure_name_, "."); + if (names.size() > 2 && names[0] == "plugin") { + std::string input, output; + auto type = names[1] == "cpp" ? lgraph::PluginManager::PluginType::CPP + : lgraph::PluginManager::PluginType::PYTHON; + if (type == lgraph::PluginManager::PluginType::PYTHON) { + NOT_SUPPORT(); + } + auto db = ctx_->galaxy_->OpenGraph(ctx_->user_, ctx_->graph_); + auto pm = db.GetLightningGraph()->GetPluginManager(); + lgraph_api::SigSpec* sig_spec = nullptr; + if (!pm->GetPluginSignature(type, ctx_->user_, names[2], &sig_spec) || + sig_spec == nullptr) { + NOT_SUPPORT(); + } + + for (auto& pair : node->items()) { + const std::string& name = std::get<0>(pair); + auto type = lgraph_api::LGraphType::NUL; + const auto iter = + std::find_if(sig_spec->result_list.cbegin(), sig_spec->result_list.cend(), + [&name](const auto& param) { return name == param.name; }); + if (iter == sig_spec->result_list.cend()) { + NOT_SUPPORT(); + } + switch (type) { + case lgraph_api::LGraphType::NODE: + AddSymbol(name, cypher::SymbolNode::NODE, cypher::SymbolNode::LOCAL); + break; + default: + AddSymbol(name, cypher::SymbolNode::CONSTANT, cypher::SymbolNode::LOCAL); + break; + } + } + } else { + NOT_SUPPORT(); + } + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::TableFunctionClause* node) { NOT_SUPPORT(); } @@ -575,7 +657,12 @@ std::any PatternGraphMaker::visit(geax::frontend::AggFunc* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any PatternGraphMaker::visit(geax::frontend::BAggFunc* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::BAggFunc* node) { + auto& left = node->lExpr(); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(std::get<1>(left)); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->rExpr()); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::MultiCount* node) { NOT_SUPPORT(); } @@ -836,13 +923,30 @@ std::any PatternGraphMaker::visit(geax::frontend::FilterStatement* node) { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } -std::any PatternGraphMaker::visit(geax::frontend::CallQueryStatement* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::CallQueryStatement* node) { + auto procedureStatement = node->procedureStatement(); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(procedureStatement); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} -std::any PatternGraphMaker::visit(geax::frontend::CallProcedureStatement* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::CallProcedureStatement* node) { + auto procedure = node->procedureCall(); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(procedure); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::InlineProcedureCall* node) { NOT_SUPPORT(); } -std::any PatternGraphMaker::visit(geax::frontend::NamedProcedureCall* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::NamedProcedureCall* node) { + curr_procedure_name_ = std::get(node->name()); + if (node->yield().has_value()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->yield().value()); + } + for (auto& expr : node->args()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(expr); + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::ForStatement* node) { NOT_SUPPORT(); } @@ -916,7 +1020,17 @@ std::any PatternGraphMaker::visit(geax::frontend::DeleteStatement* node) { std::any PatternGraphMaker::visit(geax::frontend::RemoveStatement* node) { NOT_SUPPORT(); } -std::any PatternGraphMaker::visit(geax::frontend::MergeStatement* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::MergeStatement* node) { + ClauseGuard cg(node->type(), cur_types_); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->pathPattern()); + for (auto& match : node->onMatch()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(match); + } + for (auto& create : node->onCreate()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(create); + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} std::any PatternGraphMaker::visit(geax::frontend::OtherWise* node) { NOT_SUPPORT(); } @@ -932,6 +1046,33 @@ std::any PatternGraphMaker::visit(geax::frontend::KillStatement* node) { NOT_SUP std::any PatternGraphMaker::visit(geax::frontend::ManagerStatement* node) { NOT_SUPPORT(); } +std::any PatternGraphMaker::visit(geax::frontend::UnwindStatement* node) { + const std::string& variable = node->variable(); + auto list = node->list(); + ACCEPT_AND_CHECK_WITH_ERROR_MSG(list); + auto var_scope = cypher::SymbolNode::Scope::LOCAL; + if (typeid(*list) == typeid(geax::frontend::Ref)) { + geax::frontend::Ref* ref = (geax::frontend::Ref*)list; + if (pattern_graphs_[cur_pattern_graph_].symbol_table.symbols.find(ref->name()) != + pattern_graphs_[cur_pattern_graph_].symbol_table.symbols.end()) { + var_scope = cypher::SymbolNode::Scope::DERIVED_ARGUMENT; + } + } + AddSymbol(variable, SymbolNode::Type::CONSTANT, var_scope); + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + +std::any PatternGraphMaker::visit(geax::frontend::InQueryProcedureCall* node) { + curr_procedure_name_ = std::get(node->name()); + if (node->yield().has_value()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->yield().value()); + } + for (auto& expr : node->args()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(expr); + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; +} + std::any PatternGraphMaker::visit(geax::frontend::DummyNode* node) { NOT_SUPPORT(); } std::any PatternGraphMaker::reportError() { return error_msg_; } @@ -946,6 +1087,8 @@ void PatternGraphMaker::AddSymbol(const std::string& symbol_alias, cypher::Symbo void PatternGraphMaker::AddNode(Node* node) { auto& pattern_graph = pattern_graphs_[cur_pattern_graph_]; if (!pattern_graph.GetNode(node->Alias()).Empty()) { + if (node->Visited()) + pattern_graph.GetNode(node->Alias()).Visited() = true; return; } pattern_graph.AddNode(node); diff --git a/src/cypher/execution_plan/pattern_graph_maker.h b/src/cypher/execution_plan/pattern_graph_maker.h index db06b7d625..bdd1c19f12 100644 --- a/src/cypher/execution_plan/pattern_graph_maker.h +++ b/src/cypher/execution_plan/pattern_graph_maker.h @@ -25,7 +25,7 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { explicit PatternGraphMaker(std::vector& pattern_graphs) : pattern_graphs_(pattern_graphs) {} - geax::frontend::GEAXErrorCode Build(geax::frontend::AstNode* astNode); + geax::frontend::GEAXErrorCode Build(geax::frontend::AstNode* astNode, RTContext* ctx); std::string ErrorMsg() { return error_msg_; } private: @@ -194,6 +194,8 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { std::any visit(geax::frontend::ShowProcessListStatement* node) override; std::any visit(geax::frontend::KillStatement* node) override; std::any visit(geax::frontend::ManagerStatement* node) override; + std::any visit(geax::frontend::UnwindStatement* node) override; + std::any visit(geax::frontend::InQueryProcedureCall* node) override; std::any visit(geax::frontend::DummyNode* node) override; std::any reportError() override; @@ -203,13 +205,14 @@ class PatternGraphMaker : public geax::frontend::AstNodeVisitor { cypher::SymbolNode::Scope scope); void AddNode(Node* node); void AddRelationship(Relationship* rel); - std::vector& pattern_graphs_; std::vector symbols_idx_; size_t cur_pattern_graph_; std::unordered_set cur_types_; bool read_only_ = true; std::string error_msg_; + std::string curr_procedure_name_; + RTContext* ctx_; std::shared_ptr node_t_; std::shared_ptr start_t_; diff --git a/src/cypher/execution_plan/scheduler.cpp b/src/cypher/execution_plan/scheduler.cpp index d8a22ac366..87e5481b92 100644 --- a/src/cypher/execution_plan/scheduler.cpp +++ b/src/cypher/execution_plan/scheduler.cpp @@ -181,7 +181,7 @@ void Scheduler::EvalGql(RTContext *ctx, const std::string &script, ElapsedTime & LOG_DEBUG() << dumper.dump(); } cypher::ExecutionPlanV2 execution_plan_v2; - ret = execution_plan_v2.Build(node); + ret = execution_plan_v2.Build(node, ctx); elapsed.t_compile = fma_common::GetTime() - t0; if (ret != GEAXErrorCode::GEAX_SUCCEED) { LOG_DEBUG() << "build execution_plan_v2 failed: " << execution_plan_v2.ErrorMsg(); diff --git a/src/cypher/parser/cypher_base_visitor_v2.cpp b/src/cypher/parser/cypher_base_visitor_v2.cpp index a1711d3480..8a6f7ac2bf 100644 --- a/src/cypher/parser/cypher_base_visitor_v2.cpp +++ b/src/cypher/parser/cypher_base_visitor_v2.cpp @@ -73,12 +73,24 @@ void checkedAnyCast(const std::any& s, TargetType& d) { const std::unordered_map CypherBaseVisitorV2::S_AGG_LIST = { - {"sum", geax::frontend::GeneralSetFunction::kSum}, {"avg", geax::frontend::GeneralSetFunction::kAvg}, + {"count", geax::frontend::GeneralSetFunction::kCount}, {"max", geax::frontend::GeneralSetFunction::kMax}, {"min", geax::frontend::GeneralSetFunction::kMin}, - {"count", geax::frontend::GeneralSetFunction::kCount}, - {"collect", geax::frontend::GeneralSetFunction::kCollect} + {"sum", geax::frontend::GeneralSetFunction::kSum}, + {"collect", geax::frontend::GeneralSetFunction::kCollect}, + {"stdDevSamp", geax::frontend::GeneralSetFunction::kStdDevSamp}, + {"stdDevPop", geax::frontend::GeneralSetFunction::kStdDevPop}, + {"groutConcat", geax::frontend::GeneralSetFunction::kGroupConcat}, + {"stDev", geax::frontend::GeneralSetFunction::kStDev}, + {"stDevP", geax::frontend::GeneralSetFunction::kStDevP}, + {"variance", geax::frontend::GeneralSetFunction::kVariance}, + {"varianceP", geax::frontend::GeneralSetFunction::kVarianceP}}; + +const std::unordered_map + CypherBaseVisitorV2::S_BAGG_LIST = { + {"percentileCont", geax::frontend::BinarySetFunction::kPercentileCont}, + {"percentileDisc", geax::frontend::BinarySetFunction::kPercentileDisc}, }; std::string CypherBaseVisitorV2::GetFullText(antlr4::ParserRuleContext *ruleCtx) const { @@ -93,9 +105,12 @@ std::string CypherBaseVisitorV2::GetFullText(antlr4::ParserRuleContext *ruleCtx) CypherBaseVisitorV2::CypherBaseVisitorV2(geax::common::ObjectArenaAllocator &objAlloc, antlr4::tree::ParseTree *tree) - : objAlloc_(objAlloc) - , node_(ALLOC_GEAOBJECT(geax::frontend::NormalTransaction)) - , anonymous_idx_(0) { + : objAlloc_(objAlloc), + node_(ALLOC_GEAOBJECT(geax::frontend::NormalTransaction)), + anonymous_idx_(0), + visit_types_(), + path_chain_(nullptr), + filter_in_with_clause_(nullptr) { tree->accept(this); } @@ -178,11 +193,19 @@ std::any CypherBaseVisitorV2::visitOC_SinglePartQuery( auto l = ALLOC_GEAOBJECT(geax::frontend::AmbientLinearQueryStatement); co->setHead(l); SWITCH_CONTEXT_VISIT_CHILDREN(ctx, l); + if (VisitGuard::InClause(VisitType::kSinglePartQuery, visit_types_) && + filter_in_with_clause_) { + l->appendQueryStatement(filter_in_with_clause_); + } } else { VisitGuard guard(VisitType::kUpdatingClause, visit_types_); auto stmt = ALLOC_GEAOBJECT(geax::frontend::LinearDataModifyingStatement); node->setStatement(stmt); SWITCH_CONTEXT_VISIT_CHILDREN(ctx, stmt); + if (VisitGuard::InClause(VisitType::kSinglePartQuery, visit_types_) && + filter_in_with_clause_) { + stmt->appendQueryStatement(filter_in_with_clause_); + } } return 0; } @@ -232,6 +255,7 @@ std::any CypherBaseVisitorV2::visitOC_MultiPartQuery(LcypherParser::OC_MultiPart } } } + VisitGuard guard(VisitType::kSinglePartQuery, visit_types_); SWITCH_CONTEXT_VISIT(ctx->oC_SinglePartQuery(), body); return 0; } @@ -263,12 +287,13 @@ std::any CypherBaseVisitorV2::visitOC_Match(LcypherParser::OC_MatchContext *ctx) geax::frontend::LinearDataModifyingStatement *node = nullptr; checkedCast(node_, node); VisitGuard guard(VisitType::kMatchPattern, visit_types_); - visit(ctx->oC_Pattern()); + auto match = ALLOC_GEAOBJECT(geax::frontend::MatchStatement); + node->appendQueryStatement(match); + auto graph_pattern = ALLOC_GEAOBJECT(geax::frontend::GraphPattern); + match->setGraphPattern(graph_pattern); + SWITCH_CONTEXT_VISIT(ctx->oC_Pattern(), graph_pattern); if (ctx->oC_Where()) { - CYPHER_THROW_ASSERT(!node->queryStatements().empty() && node->queryStatements()[0]); - geax::frontend::MatchStatement *match = nullptr; - checkedCast(node->queryStatements()[0], match); - SWITCH_CONTEXT_VISIT(ctx->oC_Where(), match->graphPattern()); + SWITCH_CONTEXT_VISIT(ctx->oC_Where(), graph_pattern); } } @@ -276,16 +301,54 @@ std::any CypherBaseVisitorV2::visitOC_Match(LcypherParser::OC_MatchContext *ctx) } std::any CypherBaseVisitorV2::visitOC_Unwind(LcypherParser::OC_UnwindContext *ctx) { - NOT_SUPPORT_AND_THROW(); + geax::frontend::UnwindStatement *unwind = nullptr; + if (VisitGuard::InClause(VisitType::kReadingClause, visit_types_)) { + geax::frontend::AmbientLinearQueryStatement *node = nullptr; + checkedCast(node_, node); + unwind = ALLOC_GEAOBJECT(geax::frontend::UnwindStatement); + node->appendQueryStatement(unwind); + + } else if (VisitGuard::InClause(VisitType::kUpdatingClause, visit_types_)) { + geax::frontend::LinearDataModifyingStatement *node = nullptr; + checkedCast(node_, node); + unwind = ALLOC_GEAOBJECT(geax::frontend::UnwindStatement); + node->appendQueryStatement(unwind); + } + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(ctx->oC_Expression()), expr); + unwind->setList(expr); + geax::frontend::Expr *name_expr = nullptr; + checkedAnyCast(visit(ctx->oC_Variable()), name_expr); + geax::frontend::VString *vstr = nullptr; + checkedCast(name_expr, vstr); + std::string variable = vstr->val(); + unwind->setVariable(std::move(variable)); return 0; } std::any CypherBaseVisitorV2::visitOC_Merge(LcypherParser::OC_MergeContext *ctx) { - NOT_SUPPORT_AND_THROW(); + geax::frontend::LinearDataModifyingStatement *node = nullptr; + checkedCast(node_, node); + auto merge = ALLOC_GEAOBJECT(geax::frontend::MergeStatement); + node->appendModifyStatement(merge); + auto path_pattern = ALLOC_GEAOBJECT(geax::frontend::PathPattern); + merge->setPathPattern(path_pattern); + VisitGuard guard(VisitType::kMergeClause, visit_types_); + SWITCH_CONTEXT_VISIT(ctx->oC_PatternPart(), path_pattern); + for (auto action : ctx->oC_MergeAction()) { + SWITCH_CONTEXT_VISIT(action, merge); + } return 0; } std::any CypherBaseVisitorV2::visitOC_MergeAction(LcypherParser::OC_MergeActionContext *ctx) { + if (ctx->MATCH()) { + VisitGuard guard(VisitType::kMergeOnMatch, visit_types_); + return visit(ctx->oC_Set()); + } else if (ctx->CREATE()) { + VisitGuard guard(VisitType::kMergeOnCreate, visit_types_); + return visit(ctx->oC_Set()); + } NOT_SUPPORT_AND_THROW(); return 0; } @@ -305,8 +368,22 @@ std::any CypherBaseVisitorV2::visitOC_Create(LcypherParser::OC_CreateContext *ct } std::any CypherBaseVisitorV2::visitOC_Set(LcypherParser::OC_SetContext *ctx) { - if (VisitGuard::InClause(VisitType::kReadingClause, visit_types_)) { - NOT_SUPPORT_AND_THROW(); + if (VisitGuard::InClause(VisitType::kMergeClause, visit_types_)) { + geax::frontend::MergeStatement *node = nullptr; + checkedCast(node_, node); + if (VisitGuard::InClause(VisitType::kMergeOnMatch, visit_types_)) { + for (auto &item : ctx->oC_SetItem()) { + auto stmt = ALLOC_GEAOBJECT(geax::frontend::SetStatement); + node->appendOnMatch(stmt); + SWITCH_CONTEXT_VISIT(item, stmt); + } + } else if (VisitGuard::InClause(VisitType::kMergeOnCreate, visit_types_)) { + for (auto &item : ctx->oC_SetItem()) { + auto stmt = ALLOC_GEAOBJECT(geax::frontend::SetStatement); + node->appendOnCreate(stmt); + SWITCH_CONTEXT_VISIT(item, stmt); + } + } } else if (VisitGuard::InClause(VisitType::kUpdatingClause, visit_types_)) { geax::frontend::LinearDataModifyingStatement *node = nullptr; checkedCast(node_, node); @@ -315,6 +392,8 @@ std::any CypherBaseVisitorV2::visitOC_Set(LcypherParser::OC_SetContext *ctx) { node->appendModifyStatement(stmt); SWITCH_CONTEXT_VISIT(item, stmt); } + } else { + NOT_SUPPORT_AND_THROW(); } return 0; } @@ -385,7 +464,21 @@ std::any CypherBaseVisitorV2::visitOC_RemoveItem(LcypherParser::OC_RemoveItemCon } std::any CypherBaseVisitorV2::visitOC_InQueryCall(LcypherParser::OC_InQueryCallContext *ctx) { - NOT_SUPPORT_AND_THROW(); + geax::frontend::AmbientLinearQueryStatement *node = nullptr; + checkedCast(node_, node); + auto query_call = ALLOC_GEAOBJECT(geax::frontend::CallQueryStatement); + node->appendQueryStatement(query_call); + auto procedure = ALLOC_GEAOBJECT(geax::frontend::CallProcedureStatement); + query_call->setProcedureStatement(procedure); + auto inquery_query = ALLOC_GEAOBJECT(geax::frontend::InQueryProcedureCall); + procedure->setProcedureCall(inquery_query); + VisitGuard guard(VisitType::kInQueryCall, visit_types_); + SWITCH_CONTEXT_VISIT(ctx->oC_ExplicitProcedureInvocation(), inquery_query); + if (ctx->oC_YieldItems()) { + auto yield_fields = ALLOC_GEAOBJECT(geax::frontend::YieldField); + inquery_query->setYield(yield_fields); + SWITCH_CONTEXT_VISIT(ctx->oC_YieldItems(), yield_fields); + } return 0; } @@ -400,24 +493,33 @@ std::any CypherBaseVisitorV2::visitOC_StandaloneCall(LcypherParser::OC_Standalon stand->setProcedureStatement(procedure); auto named_procedure = ALLOC_GEAOBJECT(geax::frontend::NamedProcedureCall); procedure->setProcedureCall(named_procedure); + VisitGuard guard(VisitType::kStandaloneCall, visit_types_); if (ctx->oC_ExplicitProcedureInvocation()) { SWITCH_CONTEXT_VISIT(ctx->oC_ExplicitProcedureInvocation(), named_procedure); } else if (ctx->oC_ImplicitProcedureInvocation()) { SWITCH_CONTEXT_VISIT(ctx->oC_ImplicitProcedureInvocation(), named_procedure); } if (ctx->oC_YieldItems()) { - NOT_SUPPORT_AND_THROW(); + auto yield_fields = ALLOC_GEAOBJECT(geax::frontend::YieldField); + named_procedure->setYield(yield_fields); + SWITCH_CONTEXT_VISIT(ctx->oC_YieldItems(), yield_fields); } return 0; } std::any CypherBaseVisitorV2::visitOC_YieldItems(LcypherParser::OC_YieldItemsContext *ctx) { - NOT_SUPPORT_AND_THROW(); + if (ctx->oC_Where()) NOT_SUPPORT_AND_THROW(); + for (auto &item : ctx->oC_YieldItem()) { + visit(item); + } return 0; } std::any CypherBaseVisitorV2::visitOC_YieldItem(LcypherParser::OC_YieldItemContext *ctx) { - NOT_SUPPORT_AND_THROW(); + if (ctx->AS()) NOT_SUPPORT_AND_THROW(); + geax::frontend::YieldField *yield_fields = nullptr; + checkedCast(node_, yield_fields); + yield_fields->appendItem(ctx->oC_Variable()->getText(), ctx->oC_Variable()->getText()); return 0; } @@ -429,6 +531,10 @@ std::any CypherBaseVisitorV2::visitOC_With(LcypherParser::OC_WithContext *ctx) { result->setDistinct(ctx->DISTINCT() != nullptr); node->setResultStatement(result); SWITCH_CONTEXT_VISIT(ctx->oC_ReturnBody(), result); + if (ctx->oC_Where()) { + VisitGuard guard(VisitType::kWithClause, visit_types_); + SWITCH_CONTEXT_VISIT(ctx->oC_Where(), node); + } } else if (VisitGuard::InClause(VisitType::kUpdatingClause, visit_types_)) { geax::frontend::LinearDataModifyingStatement *node = nullptr; checkedCast(node_, node); @@ -436,6 +542,10 @@ std::any CypherBaseVisitorV2::visitOC_With(LcypherParser::OC_WithContext *ctx) { result->setDistinct(ctx->DISTINCT() != nullptr); node->setResultStatement(result); SWITCH_CONTEXT_VISIT(ctx->oC_ReturnBody(), result); + if (ctx->oC_Where()) { + VisitGuard guard(VisitType::kWithClause, visit_types_); + SWITCH_CONTEXT_VISIT(ctx->oC_Where(), node); + } } return 0; } @@ -531,10 +641,10 @@ std::any CypherBaseVisitorV2::visitOC_SortItem(LcypherParser::OC_SortItemContext node->appendOrderBy(order); geax::frontend::Expr *expr = nullptr; checkedAnyCast(visit(ctx->oC_Expression()), expr); - bool desc = ctx->DESCENDING() != nullptr || ctx->DESC() != nullptr || - (ctx->ASCENDING() == nullptr && ctx->ASC() == nullptr); + bool ascending = ctx->ASCENDING() != nullptr || ctx->ASC() != nullptr || + (ctx->DESCENDING() == nullptr && ctx->DESC() == nullptr); order->setField(expr); - order->setOrder(desc); + order->setOrder(!ascending); return 0; } @@ -544,11 +654,23 @@ std::any CypherBaseVisitorV2::visitOC_Hint(LcypherParser::OC_HintContext *ctx) { } std::any CypherBaseVisitorV2::visitOC_Where(LcypherParser::OC_WhereContext *ctx) { - geax::frontend::GraphPattern *node = nullptr; - checkedCast(node_, node); - geax::frontend::Expr *where = nullptr; - checkedAnyCast(visit(ctx->oC_Expression()), where); - node->setWhere(where); + if (VisitGuard::InClause(VisitType::kWithClause, visit_types_)) { + if (VisitGuard::InClause(VisitType::kReadingClause, visit_types_) || + VisitGuard::InClause(VisitType::kUpdatingClause, visit_types_)) { + filter_in_with_clause_ = ALLOC_GEAOBJECT(geax::frontend::FilterStatement); + geax::frontend::Expr *where = nullptr; + checkedAnyCast(visit(ctx->oC_Expression()), where); + filter_in_with_clause_->setPredicate(where); + } else { + NOT_SUPPORT_AND_THROW(); + } + } else { + geax::frontend::GraphPattern *node = nullptr; + checkedCast(node_, node); + geax::frontend::Expr *where = nullptr; + checkedAnyCast(visit(ctx->oC_Expression()), where); + node->setWhere(where); + } return 0; } @@ -566,15 +688,11 @@ std::any CypherBaseVisitorV2::visitOC_Pattern(LcypherParser::OC_PatternContext * visit(ctx_part); } } else if (VisitGuard::InClause(VisitType::kMatchPattern, visit_types_)) { - geax::frontend::LinearDataModifyingStatement *node = nullptr; + geax::frontend::GraphPattern *node = nullptr; checkedCast(node_, node); for (auto &ctx_part : ctx->oC_PatternPart()) { - auto match = ALLOC_GEAOBJECT(geax::frontend::MatchStatement); - node->appendQueryStatement(match); - auto graph_pattern = ALLOC_GEAOBJECT(geax::frontend::GraphPattern); - match->setGraphPattern(graph_pattern); auto pp = ALLOC_GEAOBJECT(geax::frontend::PathPattern); - graph_pattern->appendPathPattern(pp); + node->appendPathPattern(pp); SWITCH_CONTEXT_VISIT(ctx_part, pp); } } @@ -582,7 +700,8 @@ std::any CypherBaseVisitorV2::visitOC_Pattern(LcypherParser::OC_PatternContext * } std::any CypherBaseVisitorV2::visitOC_PatternPart(LcypherParser::OC_PatternPartContext *ctx) { - if (VisitGuard::InClause(VisitType::kReadingPattern, visit_types_)) { + if (VisitGuard::InClause(VisitType::kReadingPattern, visit_types_) || + VisitGuard::InClause(VisitType::kMergeClause, visit_types_)) { geax::frontend::PathPattern *node = nullptr; checkedCast(node_, node); if (ctx->oC_Variable() != nullptr) { @@ -618,6 +737,8 @@ std::any CypherBaseVisitorV2::visitOC_PatternPart(LcypherParser::OC_PatternPartC auto pc = ALLOC_GEAOBJECT(geax::frontend::PathChain); node->appendChain(pc); SWITCH_CONTEXT_VISIT(ctx->oC_AnonymousPatternPart(), pc); + } else { + NOT_SUPPORT_AND_THROW(); } return 0; } @@ -1111,7 +1232,6 @@ std::any CypherBaseVisitorV2::visitOC_StringListNullOperatorExpression( geax::frontend::IsNull *is_null = nullptr; checkedCast(is_null_expr, is_null); is_null->setExpr(expr); - return (geax::frontend::Expr *)is_null; } else if (typeid(*is_null_expr) == typeid(geax::frontend::Not)) { geax::frontend::Not *not_expr = nullptr; checkedCast(is_null_expr, not_expr); @@ -1119,10 +1239,10 @@ std::any CypherBaseVisitorV2::visitOC_StringListNullOperatorExpression( geax::frontend::IsNull *is_null = nullptr; checkedCast(inner, is_null); is_null->setExpr(expr); - return (geax::frontend::Expr *)is_null; } else { NOT_SUPPORT_AND_THROW(); } + return is_null_expr; } else if (!ctx->oC_StringOperatorExpression().empty()) { if (ctx->oC_StringOperatorExpression().size() > 1) NOT_SUPPORT_AND_THROW(); for (auto e : ctx->oC_StringOperatorExpression()) { @@ -1140,7 +1260,6 @@ std::any CypherBaseVisitorV2::visitOC_StringListNullOperatorExpression( CYPHER_THROW_ASSERT(list_op_expr->oC_PropertyOrLabelsExpression()); geax::frontend::Expr *right = nullptr; checkedAnyCast(visit(list_op_expr->oC_PropertyOrLabelsExpression()), right); - CYPHER_THROW_ASSERT(list_op_expr->oC_PropertyOrLabelsExpression()); auto bin = ALLOC_GEAOBJECT(geax::frontend::BIn); bin->setLeft(expr); bin->setRight(right); @@ -1289,6 +1408,14 @@ std::any CypherBaseVisitorV2::visitOC_Atom(LcypherParser::OC_AtomContext *ctx) { return visit(ctx->oC_PatternComprehension()); } else if (ctx->oC_Parameter()) { return visit(ctx->oC_Parameter()); + } else if (ctx->COUNT()) { + auto func = ALLOC_GEAOBJECT(geax::frontend::AggFunc); + func->setFuncName(geax::frontend::GeneralSetFunction::kCount); + func->setDistinct(false); + auto param = ALLOC_GEAOBJECT(geax::frontend::VString); + param->setVal(std::string("*")); + func->setExpr(param); + return (geax::frontend::Expr *)func; } NOT_SUPPORT_AND_THROW(); } @@ -1381,6 +1508,7 @@ std::any CypherBaseVisitorV2::visitOC_ParenthesizedExpression( std::any CypherBaseVisitorV2::visitOC_RelationshipsPattern( LcypherParser::OC_RelationshipsPatternContext *ctx) { + path_chain_ = ALLOC_GEAOBJECT(geax::frontend::PathChain); NOT_SUPPORT_AND_THROW(); return 0; } @@ -1402,7 +1530,23 @@ std::any CypherBaseVisitorV2::visitOC_FunctionInvocation( checkedAnyCast(visit(ctx->oC_FunctionName()), name); geax::frontend::Expr *res = nullptr; auto it = S_AGG_LIST.find(name); - if (it != S_AGG_LIST.end()) { + auto bit = S_BAGG_LIST.find(name); + if (name == "EXISTS") { + if (ctx->oC_Expression().size() > 1) NOT_SUPPORT_AND_THROW(); + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(ctx->oC_Expression(0)), expr); + if (typeid(*expr) == typeid(geax::frontend::Exists)) { + auto exists = ALLOC_GEAOBJECT(geax::frontend::Exists); + exists->appendPathChain(path_chain_); + res = exists; + } else { + auto func = ALLOC_GEAOBJECT(geax::frontend::Function); + func->setName(std::move(name)); + func->appendArg(expr); + res = func; + } + } else if (it != S_AGG_LIST.end()) { + CYPHER_THROW_ASSERT(ctx->oC_Expression().size() == 1); auto func = ALLOC_GEAOBJECT(geax::frontend::AggFunc); func->setFuncName(it->second); if (ctx->DISTINCT()) { @@ -1410,16 +1554,20 @@ std::any CypherBaseVisitorV2::visitOC_FunctionInvocation( } else { func->setDistinct(false); } - - for (size_t idx = 0; idx < ctx->oC_Expression().size(); ++idx) { - geax::frontend::Expr *expr = nullptr; - checkedAnyCast(visit(ctx->oC_Expression(idx)), expr); - if (idx == 0) { - func->setExpr(expr); - } else { - func->appendDistinctBy(expr); - } - } + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(ctx->oC_Expression(0)), expr); + func->setExpr(expr); + res = func; + } else if (bit != S_BAGG_LIST.end()) { + CYPHER_THROW_ASSERT(ctx->oC_Expression().size() == 2); + auto func = ALLOC_GEAOBJECT(geax::frontend::BAggFunc); + func->setFuncName(bit->second); + geax::frontend::Expr *left = nullptr, *right = nullptr; + checkedAnyCast(visit(ctx->oC_Expression(0)), left); + checkedAnyCast(visit(ctx->oC_Expression(1)), right); + bool distinct = ctx->DISTINCT() ? true : false; + func->setLExpr(distinct, left); + func->setRExpr(right); res = func; } else { auto func = ALLOC_GEAOBJECT(geax::frontend::Function); @@ -1448,16 +1596,32 @@ std::any CypherBaseVisitorV2::visitOC_FunctionName(LcypherParser::OC_FunctionNam std::any CypherBaseVisitorV2::visitOC_ExplicitProcedureInvocation( LcypherParser::OC_ExplicitProcedureInvocationContext *ctx) { - geax::frontend::NamedProcedureCall *node = nullptr; - checkedCast(node_, node); - std::string fun_name; - checkedAnyCast(visit(ctx->oC_ProcedureName()), fun_name); - geax::frontend::StringParam name = fun_name; - node->setName(std::move(name)); - for (auto oc : ctx->oC_Expression()) { - geax::frontend::Expr *expr = nullptr; - checkedAnyCast(visit(oc), expr); - node->appendArg(expr); + if (VisitGuard::InClause(VisitType::kStandaloneCall, visit_types_)) { + geax::frontend::NamedProcedureCall *node = nullptr; + checkedCast(node_, node); + std::string fun_name; + checkedAnyCast(visit(ctx->oC_ProcedureName()), fun_name); + geax::frontend::StringParam name = fun_name; + node->setName(std::move(name)); + for (auto oc : ctx->oC_Expression()) { + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(oc), expr); + node->appendArg(expr); + } + } else if (VisitGuard::InClause(VisitType::kInQueryCall, visit_types_)) { + geax::frontend::InQueryProcedureCall *node = nullptr; + checkedCast(node_, node); + std::string fun_name; + checkedAnyCast(visit(ctx->oC_ProcedureName()), fun_name); + geax::frontend::StringParam name = fun_name; + node->setName(std::move(name)); + for (auto oc : ctx->oC_Expression()) { + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(oc), expr); + node->appendArg(expr); + } + } else { + NOT_SUPPORT_AND_THROW(); } return 0; } @@ -1609,6 +1773,19 @@ std::any CypherBaseVisitorV2::visitOC_MapLiteral(LcypherParser::OC_MapLiteralCon std::string name = vstr->val(); ps->appendProperty(std::move(name), expr); } + } else if (VisitGuard::InClause(VisitType::kStandaloneCall, visit_types_) || + VisitGuard::InClause(VisitType::kInQueryCall, visit_types_)) { + auto map = ALLOC_GEAOBJECT(geax::frontend::MkMap); + if (ctx->oC_Expression().size() != ctx->oC_PropertyKeyName().size()) + NOT_SUPPORT_AND_THROW(); + for (size_t idx = 0; idx < ctx->oC_PropertyKeyName().size(); ++idx) { + geax::frontend::Expr *expr = nullptr; + checkedAnyCast(visit(ctx->oC_Expression(idx)), expr); + geax::frontend::Expr *name_expr = nullptr; + checkedAnyCast(visit(ctx->oC_PropertyKeyName(idx)), name_expr); + map->appendElem(name_expr, expr); + } + res = map; } else { NOT_SUPPORT_AND_THROW(); } @@ -1616,8 +1793,9 @@ std::any CypherBaseVisitorV2::visitOC_MapLiteral(LcypherParser::OC_MapLiteralCon } std::any CypherBaseVisitorV2::visitOC_Parameter(LcypherParser::OC_ParameterContext *ctx) { - NOT_SUPPORT_AND_THROW(); - return 0; + auto param = ALLOC_GEAOBJECT(geax::frontend::Param); + param->setName(ctx->getText()); + return (geax::frontend::Expr *)param; } std::any CypherBaseVisitorV2::visitOC_PropertyExpression( diff --git a/src/cypher/parser/cypher_base_visitor_v2.h b/src/cypher/parser/cypher_base_visitor_v2.h index 9aed9a4c04..58416b1ad5 100644 --- a/src/cypher/parser/cypher_base_visitor_v2.h +++ b/src/cypher/parser/cypher_base_visitor_v2.h @@ -36,12 +36,19 @@ namespace parser { enum class VisitType { kReadingClause, kUpdatingClause, + kMergeClause, + kMergeOnMatch, + kMergeOnCreate, kReadingPattern, kUpdatingPattern, kMatchPattern, kSetVariable, kSetNull, - kDeleteVariable + kDeleteVariable, + kWithClause, + kStandaloneCall, + kInQueryCall, + kSinglePartQuery }; class VisitGuard { @@ -71,6 +78,9 @@ class CypherBaseVisitorV2 : public LcypherVisitor { size_t anonymous_idx_; std::unordered_set visit_types_; static const std::unordered_map S_AGG_LIST; + static const std::unordered_map S_BAGG_LIST; + geax::frontend::PathChain* path_chain_; + geax::frontend::FilterStatement* filter_in_with_clause_; public: CypherBaseVisitorV2() = delete; diff --git a/src/cypher/procedure/procedure_v2.cpp b/src/cypher/procedure/procedure_v2.cpp index aa523e8471..727a23921b 100644 --- a/src/cypher/procedure/procedure_v2.cpp +++ b/src/cypher/procedure/procedure_v2.cpp @@ -209,11 +209,10 @@ void BuiltinProcedureV2::DbListLabelIndexes(RTContext *ctx, const Record *record FMA_FMT("Function requires 2 arguments, but {} are " "given. Usage: db.listLabelIndexes(label_name, label_type)", args.size())) - - CYPHER_ARG_CHECK(!args[0].IsString(), FMA_FMT("{} has to be a string ", args[0].ToString())) CYPHER_ARG_CHECK(args[0].IsString(), FMA_FMT("{} has to be a string ", args[0].ToString())) + CYPHER_ARG_CHECK(args[1].IsString(), FMA_FMT("{} has to be a string ", args[1].ToString())) auto label = args[0].constant.scalar.AsString(); - bool is_vertex = ParseIsVertex(label); + bool is_vertex = ParseIsVertex(args[1].constant.scalar.AsString()); CYPHER_DB_PROCEDURE_GRAPH_CHECK(); std::vector indexes; if (is_vertex) { @@ -336,21 +335,19 @@ void BuiltinProcedureV2::_ExtractFds(const VEC_EXPR_V2 &args, std::string &label void BuiltinProcedureV2::_ExtractAccessLevel( const VEC_EXPR_V2 &args, std::string &role, std::unordered_map &leves) { - // CYPHER_ARG_CHECK(args.size() == 2, - // "Not enough arguments. This function takes 2 or " - // "more arguments. first is a string, other arguments are map"); - // CYPHER_ARG_CHECK(args[0].IsString(), - // "wrong arguments type,has to be a string"); - // CYPHER_ARG_CHECK(args[1].type == parser::Expression::MAP, - // "wrong arguments type, has to be a map"); - // role = args[0].constant.scalar.AsString(); - // std::unordered_map temp_levels; - // for (auto &kv : args[1].Map()) { - // auto it = ValidAccessLevels.find(kv.second.constant.scalar.AsString()); - // CYPHER_ARG_CHECK(it != ValidAccessLevels.end(), "unknown access level"); - // temp_levels[kv.first] = it->second; - // } - // std::swap(leves, temp_levels); + CYPHER_ARG_CHECK(args.size() == 2, + "Not enough arguments. This function takes 2 or " + "more arguments. first is a string, other arguments are map"); + CYPHER_ARG_CHECK(args[0].IsString(), "wrong arguments type,has to be a string"); + CYPHER_ARG_CHECK(args[1].IsMap(), "wrong arguments type, has to be a map"); + role = args[0].constant.scalar.AsString(); + std::unordered_map temp_levels; + for (auto &kv : *args[1].constant.map) { + auto it = ValidAccessLevels.find(kv.second.AsString()); + CYPHER_ARG_CHECK(it != ValidAccessLevels.end(), "unknown access level"); + temp_levels[kv.first] = it->second; + } + std::swap(leves, temp_levels); } static ::std::vector<::lgraph::FieldSpec> ParseFieldSpecs(const VEC_EXPR_V2 &args, @@ -1094,36 +1091,36 @@ void BuiltinProcedureV2::DbmsGraphModGraph(RTContext *ctx, const cypher::Record const cypher::VEC_EXPR_V2 &args, const cypher::VEC_STR_V2 &yield_items, std::vector *records) { - // CYPHER_ARG_CHECK( - // args.size() == 2, - // "This function takes exactly 2 arguments. e.g. dbms.graph.modGraph(graph_name,config)"); - // CYPHER_ARG_CHECK(args[0].IsString(), "graph_name must be string"); - // /* close the previous txn first, in case of nested transaction */ - // if (ctx->txn_) ctx->txn_->Abort(); - // lgraph::DBConfig config; - // CYPHER_ARG_CHECK(args[1].type == parser::Expression::MAP, "Illega must be map"); - // lgraph::GraphManager::ModGraphActions act; - // act.mod_size = false; - // act.mod_desc = false; - // for (auto &kv : args[1].Map()) { - // if (kv.first == "max_size_GB") { - // act.mod_size = true; - // if (kv.second.type != parser::Expression::INT) - // THROW_CODE(InputError, "Invalid value for max_size_GB: must be integer"); - // act.max_size = kv.second.constant.scalar.integer() << 30; - // } else if (kv.first == "description") { - // act.mod_desc = true; - // if (kv.second.type != parser::Expression::STRING) - // THROW_CODE(InputError, "Invalid value for description: must be string"); - // act.desc = kv.second.constant.scalar.AsString(); - // } else { - // THROW_CODE(InputError, "Invalid config key: " + kv.first); - // } - // } - // bool success = ctx->galaxy_->ModGraph(ctx->user_, args[0].constant.scalar.AsString(), act); - // if (!success) { - // throw lgraph::GraphNotExistException(args[0].constant.scalar.AsString()); - // } + CYPHER_ARG_CHECK( + args.size() == 2, + "This function takes exactly 2 arguments. e.g. dbms.graph.modGraph(graph_name,config)"); + CYPHER_ARG_CHECK(args[0].IsString(), "graph_name must be string"); + /* close the previous txn first, in case of nested transaction */ + if (ctx->txn_) ctx->txn_->Abort(); + lgraph::DBConfig config; + CYPHER_ARG_CHECK(args[1].IsMap(), "Illega must be map"); + lgraph::GraphManager::ModGraphActions act; + act.mod_size = false; + act.mod_desc = false; + for (auto &kv : *args[1].constant.map) { + if (kv.first == "max_size_GB") { + act.mod_size = true; + if (!kv.second.IsInteger()) + THROW_CODE(InputError, "Invalid value for max_size_GB: must be integer"); + act.max_size = kv.second.integer() << 30; + } else if (kv.first == "description") { + act.mod_desc = true; + if (kv.second.IsString()) + THROW_CODE(InputError, "Invalid value for description: must be string"); + act.desc = kv.second.AsString(); + } else { + THROW_CODE(InputError, "Invalid config key: " + kv.first); + } + } + bool success = ctx->galaxy_->ModGraph(ctx->user_, args[0].constant.scalar.AsString(), act); + if (!success) { + throw lgraph::GraphNotExistException(args[0].constant.scalar.AsString()); + } } void BuiltinProcedureV2::DbmsGraphDeleteGraph(RTContext *ctx, const cypher::Record *record, @@ -1466,44 +1463,42 @@ void BuiltinProcedureV2::DbmsSecurityRebuildRoleAccessLevel(RTContext *ctx, cons const VEC_EXPR_V2 &args, const VEC_STR_V2 &yield_items, std::vector *records) { - // if (!ctx->galaxy_->IsAdmin(ctx->user_)) - // THROW_CODE(Unauthorized, "Admin access right required."); - // CYPHER_ARG_CHECK( - // args.size() == 2, - // "need two parameters, e.g. dbms.security.modAllRoleAccessLevel(role, access_level)") - // CYPHER_ARG_CHECK(args[0].IsString(), "role type should be string") - // CYPHER_ARG_CHECK(args[1].type == parser::Expression::MAP, - // "access_level type should be map, key and val both string") - // std::string role; - // std::unordered_map levels; - // _ExtractAccessLevel(args, role, levels); - // if (ctx->txn_) ctx->txn_->Abort(); - // bool success = ctx->galaxy_->ModAllRoleAccessLevel(role, levels); - // if (!success) { - // throw lgraph::RoleNotExistException(args[0].constant.scalar.AsString()); - // } + if (!ctx->galaxy_->IsAdmin(ctx->user_)) + THROW_CODE(Unauthorized, "Admin access right required."); + CYPHER_ARG_CHECK( + args.size() == 2, + "need two parameters, e.g. dbms.security.modAllRoleAccessLevel(role, access_level)") + CYPHER_ARG_CHECK(args[0].IsString(), "role type should be string") + CYPHER_ARG_CHECK(args[1].IsMap(), "access_level type should be map, key and val both string") + std::string role; + std::unordered_map levels; + _ExtractAccessLevel(args, role, levels); + if (ctx->txn_) ctx->txn_->Abort(); + bool success = ctx->galaxy_->ModAllRoleAccessLevel(role, levels); + if (!success) { + throw lgraph::RoleNotExistException(args[0].constant.scalar.AsString()); + } } void BuiltinProcedureV2::DbmsSecurityModRoleAccessLevel(RTContext *ctx, const Record *record, const VEC_EXPR_V2 &args, const VEC_STR_V2 &yield_items, std::vector *records) { - // if (!ctx->galaxy_->IsAdmin(ctx->user_)) - // THROW_CODE(Unauthorized, "Admin access right required."); - // CYPHER_ARG_CHECK( - // args.size() == 2, - // "need two parameters, e.g. dbms.security.modRoleAccessLevel(role, access_level)") - // CYPHER_ARG_CHECK(args[0].IsString(), "role type should be string") - // CYPHER_ARG_CHECK(args[1].type == parser::Expression::MAP, - // "access_level type should be map, key and val both string") - // std::string role; - // std::unordered_map levels; - // _ExtractAccessLevel(args, role, levels); - // if (ctx->txn_) ctx->txn_->Abort(); - // bool success = ctx->galaxy_->ModRoleAccessLevel(role, levels); - // if (!success) { - // throw lgraph::RoleNotExistException(args[0].constant.scalar.AsString()); - // } + if (!ctx->galaxy_->IsAdmin(ctx->user_)) + THROW_CODE(Unauthorized, "Admin access right required."); + CYPHER_ARG_CHECK( + args.size() == 2, + "need two parameters, e.g. dbms.security.modRoleAccessLevel(role, access_level)") + CYPHER_ARG_CHECK(args[0].IsString(), "role type should be string") + CYPHER_ARG_CHECK(args[1].IsMap(), "access_level type should be map, key and val both string") + std::string role; + std::unordered_map levels; + _ExtractAccessLevel(args, role, levels); + if (ctx->txn_) ctx->txn_->Abort(); + bool success = ctx->galaxy_->ModRoleAccessLevel(role, levels); + if (!success) { + throw lgraph::RoleNotExistException(args[0].constant.scalar.AsString()); + } } void BuiltinProcedureV2::DbmsSecurityModRoleFieldAccessLevel(RTContext *ctx, const Record *record, @@ -1682,60 +1677,56 @@ void BuiltinProcedureV2::DbmsSecurityAddUserRoles(RTContext *ctx, const Record * void BuiltinProcedureV2::DbPluginLoadPlugin(RTContext *ctx, const Record *record, const VEC_EXPR_V2 &args, const VEC_STR_V2 &yield_items, std::vector *records) { - // CYPHER_ARG_CHECK( - // args.size() == 7, - // "need seven parameters, e.g. db.plugin.loadPlugin(plugin_type," - // "plugin_name, plugin_content, code_type, plugin_description, read_only, version)") - // CYPHER_ARG_CHECK(args[0].IsString(), - // "plugin_type type should be string") - // CYPHER_ARG_CHECK(args[1].IsString(), - // "plugin_name type should be string") - // CYPHER_ARG_CHECK(args[3].IsString(), "code_type type should be string") - // CYPHER_ARG_CHECK(args[4].IsString(), - // "plugin_description type should be string") - // CYPHER_ARG_CHECK(args[5].IsBool(), "read_only type should be boolean") - // CYPHER_ARG_CHECK(args[6].IsString(), "version type should be string") - // CYPHER_DB_PROCEDURE_GRAPH_CHECK(); - // if (ctx->txn_) ctx->txn_->Abort(); - // lgraph::AccessControlledDB db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); - // auto plugin_type_it = ValidPluginType.find(args[0].constant.scalar.AsString()); - // CYPHER_ARG_CHECK(plugin_type_it != ValidPluginType.end(), - // "unknown plugin_type, one of ('CPP', 'PY')"); - // auto code_type_it = ValidPluginCodeType.find(args[3].constant.scalar.AsString()); - // CYPHER_ARG_CHECK(code_type_it != ValidPluginCodeType.end(), - // "unknown plugin_type, one of ('PY', 'SO', 'CPP', 'ZIP')"); - // bool success = false; - // fma_common::encrypt::Base64 base64; - // if (args[2].IsString()) { - // std::string content = base64.Decode(args[2].constant.scalar.AsString()); - // success = - // db.LoadPlugin(plugin_type_it->second, ctx->user_, args[1].constant.scalar.AsString(), - // std::vector{content}, std::vector{}, - // code_type_it->second, args[4].constant.scalar.AsString(), - // args[5].constant.scalar.AsBool(), args[6].constant.scalar.AsString()); - // } else if (args[2].type == parser::Expression::MAP) { - // std::vector filenames; - // std::vector codes; - // for (auto &kv : args[2].Map()) { - // if (kv.first[0] == '`' && kv.first.back() == '`') { - // filenames.push_back(kv.first.substr(1, kv.first.size() - 2)); - // } else { - // filenames.push_back(kv.first); - // } - // codes.push_back(base64.Decode(kv.second.constant.scalar.AsString())); - // } - // success = - // db.LoadPlugin(plugin_type_it->second, ctx->user_, args[1].constant.scalar.AsString(), - // codes, filenames, - // code_type_it->second, args[4].constant.scalar.AsString(), - // args[5].constant.scalar.AsBool(), args[6].constant.scalar.AsString()); - // } else { - // throw lgraph::ReminderException("plugin_content should be string or map"); - // } + CYPHER_ARG_CHECK( + args.size() == 7, + "need seven parameters, e.g. db.plugin.loadPlugin(plugin_type," + "plugin_name, plugin_content, code_type, plugin_description, read_only, version)") + CYPHER_ARG_CHECK(args[0].IsString(), "plugin_type type should be string") + CYPHER_ARG_CHECK(args[1].IsString(), "plugin_name type should be string") + CYPHER_ARG_CHECK(args[3].IsString(), "code_type type should be string") + CYPHER_ARG_CHECK(args[4].IsString(), "plugin_description type should be string") + CYPHER_ARG_CHECK(args[5].IsBool(), "read_only type should be boolean") + CYPHER_ARG_CHECK(args[6].IsString(), "version type should be string") + CYPHER_DB_PROCEDURE_GRAPH_CHECK(); + if (ctx->txn_) ctx->txn_->Abort(); + lgraph::AccessControlledDB db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + auto plugin_type_it = ValidPluginType.find(args[0].constant.scalar.AsString()); + CYPHER_ARG_CHECK(plugin_type_it != ValidPluginType.end(), + "unknown plugin_type, one of ('CPP', 'PY')"); + auto code_type_it = ValidPluginCodeType.find(args[3].constant.scalar.AsString()); + CYPHER_ARG_CHECK(code_type_it != ValidPluginCodeType.end(), + "unknown plugin_type, one of ('PY', 'SO', 'CPP', 'ZIP')"); + bool success = false; + fma_common::encrypt::Base64 base64; + if (args[2].IsString()) { + std::string content = base64.Decode(args[2].constant.scalar.AsString()); + success = + db.LoadPlugin(plugin_type_it->second, ctx->user_, args[1].constant.scalar.AsString(), + std::vector{content}, std::vector{}, + code_type_it->second, args[4].constant.scalar.AsString(), + args[5].constant.scalar.AsBool(), args[6].constant.scalar.AsString()); + } else if (args[2].IsMap()) { + std::vector filenames; + std::vector codes; + for (auto &kv : *args[2].constant.map) { + if (kv.first[0] == '`' && kv.first.back() == '`') { + filenames.push_back(kv.first.substr(1, kv.first.size() - 2)); + } else { + filenames.push_back(kv.first); + } + codes.push_back(base64.Decode(kv.second.AsString())); + } + success = db.LoadPlugin( + plugin_type_it->second, ctx->user_, args[1].constant.scalar.AsString(), codes, + filenames, code_type_it->second, args[4].constant.scalar.AsString(), + args[5].constant.scalar.AsBool(), args[6].constant.scalar.AsString()); + } else { + throw lgraph::ReminderException("plugin_content should be string or map"); + } - // if (!success) { - // throw lgraph::PluginExistException(args[1].constant.scalar.AsString()); - // } + if (!success) { + throw lgraph::PluginExistException(args[1].constant.scalar.AsString()); + } } void BuiltinProcedureV2::DbPluginDeletePlugin(RTContext *ctx, const Record *record, @@ -1914,133 +1905,127 @@ void BuiltinProcedureV2::DbImportorFullImportor(RTContext *ctx, const Record *re const VEC_EXPR_V2 &args, const VEC_STR_V2 &yield_items, std::vector *records) { - // ctx->txn_.reset(); - // ctx->ac_db_.reset(); - // CYPHER_ARG_CHECK(args.size() == 1, - // "need exactly one parameter. " - // "e.g. db.importor.fullImportor({})") - // CYPHER_ARG_CHECK(args[0].type == parser::Expression::MAP, "conf type should be map") - // if (!ctx->galaxy_->IsAdmin(ctx->user_)) - // THROW_CODE(Unauthorized, "Admin access right required."); - - // // parse parameter - // lgraph::import_v3::Importer::Config import_config_v3; - // std::map full_import_conf; - // for (auto &kv : args[0].Map()) { - // full_import_conf[kv.first] = parser::MakeFieldData(kv.second); - // } - // auto check = [&full_import_conf](const std::string& name, lgraph::FieldType type) { - // if (full_import_conf.count(name) && full_import_conf[name].GetType() != type) - // throw lgraph::CypherException("Option " + name + " is not " + to_string(type)); - // return full_import_conf.count(name); - // }; - - // if (!full_import_conf.count("config_file")) - // THROW_CODE(InputError, "Option config_file does not exist."); - // if (!check("graph_name", lgraph::FieldType::STRING)) - // full_import_conf["graph_name"] = - // lgraph::FieldData(lgraph::_detail::DEFAULT_GRAPH_DB_NAME); - // if (!check("delete_if_exists", lgraph::FieldType::BOOL)) - // full_import_conf["delete_if_exists"] = lgraph::FieldData(false); - // if (!check("username", lgraph::FieldType::STRING)) - // full_import_conf["username"] = - // lgraph::FieldData(lgraph::_detail::DEFAULT_ADMIN_NAME); - // if (!full_import_conf["delete_if_exists"].AsBool() && - // ctx->galaxy_->ListGraphsInternal().count( - // full_import_conf["graph_name"].string())) { - // throw lgraph::CypherException("DB exists and cannot be deleted."); - // } - - // std::mutex mu; - // std::condition_variable cv; - // bool ok_to_leave = false; - // std::string data_file_path, log, error; - // std::thread worker([&]() { - // std::lock_guard l(mu); - // try { - // import_config_v3.db_dir = ctx->galaxy_->GetConfig().dir + "/.import_tmp"; - // import_config_v3.graph = lgraph::_detail::DEFAULT_GRAPH_DB_NAME; - // import_config_v3.config_file = full_import_conf["config_file"].string(); - // import_config_v3.user = full_import_conf["username"].string(); - // if (check("password", lgraph::FieldType::STRING)) - // import_config_v3.password = full_import_conf["password"].string(); - // if (check("continue_on_error", lgraph::FieldType::BOOL)) - // import_config_v3.continue_on_error = - // full_import_conf["continue_on_error"].AsBool(); - // if (check("delimiter", lgraph::FieldType::STRING)) - // import_config_v3.delimiter = full_import_conf["delimiter"].string(); - // import_config_v3.delete_if_exists = true; - // if (check("parse_block_size", lgraph::FieldType::INT64)) - // import_config_v3.parse_block_size = - // full_import_conf["parse_block_size"].AsInt64(); - // if (check("parse_block_threads", lgraph::FieldType::INT64)) - // import_config_v3.parse_block_threads = - // full_import_conf["parse_block_threads"].AsInt64(); - // if (check("parse_file_threads", lgraph::FieldType::INT64)) - // import_config_v3.parse_file_threads = - // full_import_conf["parse_file_threads"].AsInt64(); - // if (check("generate_sst_threads", lgraph::FieldType::INT64)) - // import_config_v3.generate_sst_threads = - // full_import_conf["generate_sst_threads"].AsInt64(); - // if (check("read_rocksdb_threads", lgraph::FieldType::INT64)) - // import_config_v3.read_rocksdb_threads = - // full_import_conf["read_rocksdb_threads"].AsInt64(); - // if (check("vid_num_per_reading", lgraph::FieldType::INT64)) - // import_config_v3.vid_num_per_reading = - // full_import_conf["vid_num_per_reading"].AsInt64(); - // if (check("max_size_per_reading", lgraph::FieldType::INT64)) - // import_config_v3.max_size_per_reading = - // full_import_conf["max_size_per_reading"].AsInt64(); - // if (check("compact", lgraph::FieldType::BOOL)) - // import_config_v3.compact = full_import_conf["compact"].AsBool(); - // if (check("keep_vid_in_memory", lgraph::FieldType::BOOL)) - // import_config_v3.keep_vid_in_memory = - // full_import_conf["keep_vid_in_memory"].AsBool(); - // if (check("enable_fulltext_index", lgraph::FieldType::BOOL)) - // import_config_v3.enable_fulltext_index = - // full_import_conf["enable_fulltext_index"].AsBool(); - // if (check("fulltext_index_analyzer", lgraph::FieldType::STRING)) - // import_config_v3.fulltext_index_analyzer = - // full_import_conf["fulltext_index_analyzer"].string(); - // import_config_v3.import_online = true; - // lgraph::import_v3::Importer importer(import_config_v3); - // importer.DoImportOffline(); - // log = importer.OnlineFullImportLog(); - // for (const auto& entry : - // std::filesystem::directory_iterator(import_config_v3.db_dir)) { - // if (entry.is_directory() && entry.path().filename().string() != ".meta" && - // std::filesystem::exists(entry.path().string() + "/data.mdb")) { - // data_file_path = entry.path().string() + "/data.mdb"; - // break; - // } - // } - // } catch (std::exception &e) { - // error = e.what(); - // } - // ok_to_leave = true; - // cv.notify_all(); - // }); - // worker.detach(); - // std::unique_lock l(mu); - // while (!ok_to_leave) cv.wait(l); - // auto& fs = fma_common::FileSystem::GetFileSystem(ctx->galaxy_->GetConfig().dir + - // "/.import_tmp"); - // if (error.empty()) { - // lgraph::DBConfig dbConfig; - // dbConfig.dir = ctx->galaxy_->GetConfig().dir; - // dbConfig.name = full_import_conf["graph_name"].string(); - // dbConfig.create_if_not_exist = true; - // ctx->galaxy_->CreateGraph(full_import_conf["username"].string(), - // full_import_conf["graph_name"].string(), dbConfig, - // data_file_path); - // fs.RemoveDir(ctx->galaxy_->GetConfig().dir + "/.import_tmp"); - // Record r; - // r.AddConstant(lgraph::FieldData(log)); - // records->emplace_back(r.Snapshot()); - // } else { - // fs.RemoveDir(ctx->galaxy_->GetConfig().dir + "/.import_tmp"); - // throw lgraph::CypherException(error); - // } + ctx->txn_.reset(); + ctx->ac_db_.reset(); + CYPHER_ARG_CHECK(args.size() == 1, + "need exactly one parameter. " + "e.g. db.importor.fullImportor({})") + CYPHER_ARG_CHECK(args[0].IsMap(), "conf type should be map") + if (!ctx->galaxy_->IsAdmin(ctx->user_)) + THROW_CODE(Unauthorized, "Admin access right required."); + + // parse parameter + lgraph::import_v3::Importer::Config import_config_v3; + std::map full_import_conf; + for (auto &kv : *args[0].constant.map) { + full_import_conf[kv.first] = kv.second; + } + auto check = [&full_import_conf](const std::string &name, lgraph::FieldType type) { + if (full_import_conf.count(name) && full_import_conf[name].GetType() != type) + throw lgraph::CypherException("Option " + name + " is not " + to_string(type)); + return full_import_conf.count(name); + }; + + if (!full_import_conf.count("config_file")) + THROW_CODE(InputError, "Option config_file does not exist."); + if (!check("graph_name", lgraph::FieldType::STRING)) + full_import_conf["graph_name"] = lgraph::FieldData(lgraph::_detail::DEFAULT_GRAPH_DB_NAME); + if (!check("delete_if_exists", lgraph::FieldType::BOOL)) + full_import_conf["delete_if_exists"] = lgraph::FieldData(false); + if (!check("username", lgraph::FieldType::STRING)) + full_import_conf["username"] = lgraph::FieldData(lgraph::_detail::DEFAULT_ADMIN_NAME); + if (!full_import_conf["delete_if_exists"].AsBool() && + ctx->galaxy_->ListGraphsInternal().count(full_import_conf["graph_name"].string())) { + throw lgraph::CypherException("DB exists and cannot be deleted."); + } + + std::mutex mu; + std::condition_variable cv; + bool ok_to_leave = false; + std::string data_file_path, log, error; + std::thread worker([&]() { + std::lock_guard l(mu); + try { + import_config_v3.db_dir = ctx->galaxy_->GetConfig().dir + "/.import_tmp"; + import_config_v3.graph = lgraph::_detail::DEFAULT_GRAPH_DB_NAME; + import_config_v3.config_file = full_import_conf["config_file"].string(); + import_config_v3.user = full_import_conf["username"].string(); + if (check("password", lgraph::FieldType::STRING)) + import_config_v3.password = full_import_conf["password"].string(); + if (check("continue_on_error", lgraph::FieldType::BOOL)) + import_config_v3.continue_on_error = full_import_conf["continue_on_error"].AsBool(); + if (check("delimiter", lgraph::FieldType::STRING)) + import_config_v3.delimiter = full_import_conf["delimiter"].string(); + import_config_v3.delete_if_exists = true; + if (check("parse_block_size", lgraph::FieldType::INT64)) + import_config_v3.parse_block_size = full_import_conf["parse_block_size"].AsInt64(); + if (check("parse_block_threads", lgraph::FieldType::INT64)) + import_config_v3.parse_block_threads = + full_import_conf["parse_block_threads"].AsInt64(); + if (check("parse_file_threads", lgraph::FieldType::INT64)) + import_config_v3.parse_file_threads = + full_import_conf["parse_file_threads"].AsInt64(); + if (check("generate_sst_threads", lgraph::FieldType::INT64)) + import_config_v3.generate_sst_threads = + full_import_conf["generate_sst_threads"].AsInt64(); + if (check("read_rocksdb_threads", lgraph::FieldType::INT64)) + import_config_v3.read_rocksdb_threads = + full_import_conf["read_rocksdb_threads"].AsInt64(); + if (check("vid_num_per_reading", lgraph::FieldType::INT64)) + import_config_v3.vid_num_per_reading = + full_import_conf["vid_num_per_reading"].AsInt64(); + if (check("max_size_per_reading", lgraph::FieldType::INT64)) + import_config_v3.max_size_per_reading = + full_import_conf["max_size_per_reading"].AsInt64(); + if (check("compact", lgraph::FieldType::BOOL)) + import_config_v3.compact = full_import_conf["compact"].AsBool(); + if (check("keep_vid_in_memory", lgraph::FieldType::BOOL)) + import_config_v3.keep_vid_in_memory = + full_import_conf["keep_vid_in_memory"].AsBool(); + if (check("enable_fulltext_index", lgraph::FieldType::BOOL)) + import_config_v3.enable_fulltext_index = + full_import_conf["enable_fulltext_index"].AsBool(); + if (check("fulltext_index_analyzer", lgraph::FieldType::STRING)) + import_config_v3.fulltext_index_analyzer = + full_import_conf["fulltext_index_analyzer"].string(); + import_config_v3.import_online = true; + lgraph::import_v3::Importer importer(import_config_v3); + importer.DoImportOffline(); + log = importer.OnlineFullImportLog(); + for (const auto &entry : std::filesystem::directory_iterator(import_config_v3.db_dir)) { + if (entry.is_directory() && entry.path().filename().string() != ".meta" && + std::filesystem::exists(entry.path().string() + "/data.mdb")) { + data_file_path = entry.path().string() + "/data.mdb"; + break; + } + } + } catch (std::exception &e) { + error = e.what(); + } + ok_to_leave = true; + cv.notify_all(); + }); + worker.detach(); + std::unique_lock l(mu); + while (!ok_to_leave) cv.wait(l); + auto &fs = + fma_common::FileSystem::GetFileSystem(ctx->galaxy_->GetConfig().dir + "/.import_tmp"); + if (error.empty()) { + lgraph::DBConfig dbConfig; + dbConfig.dir = ctx->galaxy_->GetConfig().dir; + dbConfig.name = full_import_conf["graph_name"].string(); + dbConfig.create_if_not_exist = true; + ctx->galaxy_->CreateGraph(full_import_conf["username"].string(), + full_import_conf["graph_name"].string(), dbConfig, + data_file_path); + fs.RemoveDir(ctx->galaxy_->GetConfig().dir + "/.import_tmp"); + Record r; + r.AddConstant(lgraph::FieldData(log)); + records->emplace_back(r.Snapshot()); + } else { + fs.RemoveDir(ctx->galaxy_->GetConfig().dir + "/.import_tmp"); + throw lgraph::CypherException(error); + } } void BuiltinProcedureV2::DbImportorFullFileImportor(RTContext *ctx, const Record *record, @@ -2257,110 +2242,107 @@ void BuiltinProcedureV2::DbmsHaClusterInfo(RTContext *ctx, const Record *record, records->emplace_back(r.Snapshot()); } -// static void _FetchPath(lgraph::Transaction &txn, size_t hops, -// std::unordered_map &parent, -// std::unordered_map &child, -// lgraph::VertexId vid_from, lgraph::VertexId vid_a, lgraph::VertexId vid_b, -// lgraph::VertexId vid_to, cypher::Path &path) { -// std::vector vids; -// auto vid = vid_a; -// while (vid != vid_from) { -// vids.push_back(vid); -// vid = parent[vid]; -// } -// vids.push_back(vid); -// std::reverse(vids.begin(), vids.end()); -// vid = vid_b; -// while (vid != vid_to) { -// vids.push_back(vid); -// vid = child[vid]; -// } -// vids.push_back(vid); -// if (hops != 0 && vids.size() != hops + 1) { -// throw lgraph::ReminderException("_FetchPath failed"); -// } -// for (size_t i = 0; i < hops; i++) { -// // TODO(any): fake edges! -// path.Append(lgraph::EdgeUid(vids[i], vids[i + 1], 0, 0, 0)); -// } -// } - -// template -// static bool _Filter(lgraph::Transaction &txn, const EIT &eit, -// const EDGE_FILTER_T &edge_filter) { -// for (auto &kv1 : edge_filter) { -// auto field = txn.GetEdgeField(eit.GetUid(), kv1.first); -// for (auto &kv2 : kv1.second) { -// if ((kv2.first == "smaller_than" && field >= kv2.second) || -// (kv2.first == "greater_than" && field <= kv2.second)) { -// return false; -// } -// } -// } -// return true; -// } - -// static std::vector _GetNeighbors(lgraph::Transaction &txn, -// lgraph::VertexId vid, -// const std::string &edge_label, -// const EDGE_FILTER_T& edge_filter, -// const parser::LinkDirection& direction, -// std::optional per_node_limit) { -// auto vit = txn.GetVertexIterator(vid); -// CYPHER_THROW_ASSERT(vit.IsValid()); -// std::vector neighbors; -// if (edge_label.empty()) { -// bool out_edge_left = false, in_edge_left = false; -// neighbors = vit.ListDstVids(nullptr, nullptr, -// per_node_limit.has_value() ? per_node_limit.value() : 10000, -// &out_edge_left, nullptr); -// auto srcIds = vit.ListSrcVids(nullptr, nullptr, -// per_node_limit.has_value() ? per_node_limit.value() : 10000, -// &in_edge_left, nullptr); -// if (out_edge_left || in_edge_left) { -// LOG_WARN() << "Result trimmed down because " << vid -// << " has more than 10000 in/out neighbours"; -// } -// if (direction == parser::LinkDirection::RIGHT_TO_LEFT) { -// neighbors = srcIds; -// } else if (direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { -// neighbors.reserve(neighbors.size() + srcIds.size()); -// neighbors.insert(neighbors.end(), srcIds.begin(), srcIds.end()); -// } -// } else { -// auto edge_lid = txn.GetLabelId(false, edge_label); -// if (direction == parser::LinkDirection::LEFT_TO_RIGHT || -// direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { -// size_t count = 0; -// for (auto eit = vit.GetOutEdgeIterator(lgraph::EdgeUid( -// vid, 0, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// count += 1; -// if (per_node_limit.has_value() && count > per_node_limit.value()) { -// break; -// } -// if (!_Filter(txn, eit, edge_filter)) continue; -// neighbors.push_back(eit.GetDst()); -// } -// } -// if (direction == parser::LinkDirection::RIGHT_TO_LEFT || -// direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { -// size_t count = 0; -// for (auto eit = vit.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// count += 1; -// if (per_node_limit.has_value() && count > per_node_limit.value()) { -// break; -// } -// if (!_Filter(txn, eit, edge_filter)) continue; -// neighbors.push_back(eit.GetSrc()); -// } -// } -// } -// return neighbors; -// } +static void _FetchPath(lgraph::Transaction &txn, size_t hops, + std::unordered_map &parent, + std::unordered_map &child, + lgraph::VertexId vid_from, lgraph::VertexId vid_a, lgraph::VertexId vid_b, + lgraph::VertexId vid_to, cypher::Path &path) { + std::vector vids; + auto vid = vid_a; + while (vid != vid_from) { + vids.push_back(vid); + vid = parent[vid]; + } + vids.push_back(vid); + std::reverse(vids.begin(), vids.end()); + vid = vid_b; + while (vid != vid_to) { + vids.push_back(vid); + vid = child[vid]; + } + vids.push_back(vid); + if (hops != 0 && vids.size() != hops + 1) { + throw lgraph::ReminderException("_FetchPath failed"); + } + for (size_t i = 0; i < hops; i++) { + // TODO(any): fake edges! + path.Append(lgraph::EdgeUid(vids[i], vids[i + 1], 0, 0, 0)); + } +} + +template +static bool _Filter(lgraph::Transaction &txn, const EIT &eit, const EDGE_FILTER_T &edge_filter) { + for (auto &kv1 : edge_filter) { + auto field = txn.GetEdgeField(eit.GetUid(), kv1.first); + for (auto &kv2 : kv1.second) { + if ((kv2.first == "smaller_than" && field >= kv2.second) || + (kv2.first == "greater_than" && field <= kv2.second)) { + return false; + } + } + } + return true; +} + +static std::vector _GetNeighbors(lgraph::Transaction &txn, lgraph::VertexId vid, + const std::string &edge_label, + const EDGE_FILTER_T &edge_filter, + const parser::LinkDirection &direction, + std::optional per_node_limit) { + auto vit = txn.GetVertexIterator(vid); + CYPHER_THROW_ASSERT(vit.IsValid()); + std::vector neighbors; + if (edge_label.empty()) { + bool out_edge_left = false, in_edge_left = false; + neighbors = vit.ListDstVids(nullptr, nullptr, + per_node_limit.has_value() ? per_node_limit.value() : 10000, + &out_edge_left, nullptr); + auto srcIds = vit.ListSrcVids(nullptr, nullptr, + per_node_limit.has_value() ? per_node_limit.value() : 10000, + &in_edge_left, nullptr); + if (out_edge_left || in_edge_left) { + LOG_WARN() << "Result trimmed down because " << vid + << " has more than 10000 in/out neighbours"; + } + if (direction == parser::LinkDirection::RIGHT_TO_LEFT) { + neighbors = srcIds; + } else if (direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { + neighbors.reserve(neighbors.size() + srcIds.size()); + neighbors.insert(neighbors.end(), srcIds.begin(), srcIds.end()); + } + } else { + auto edge_lid = txn.GetLabelId(false, edge_label); + if (direction == parser::LinkDirection::LEFT_TO_RIGHT || + direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { + size_t count = 0; + for (auto eit = vit.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + count += 1; + if (per_node_limit.has_value() && count > per_node_limit.value()) { + break; + } + if (!_Filter(txn, eit, edge_filter)) continue; + neighbors.push_back(eit.GetDst()); + } + } + if (direction == parser::LinkDirection::RIGHT_TO_LEFT || + direction == parser::LinkDirection::DIR_NOT_SPECIFIED) { + size_t count = 0; + for (auto eit = vit.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + count += 1; + if (per_node_limit.has_value() && count > per_node_limit.value()) { + break; + } + if (!_Filter(txn, eit, edge_filter)) continue; + neighbors.push_back(eit.GetSrc()); + } + } + } + return neighbors; +} /* All the shortestPath procedures assume that they are being executed on undirected graphs. * Just like described in neo4j: @@ -2369,517 +2351,505 @@ void BuiltinProcedureV2::DbmsHaClusterInfo(RTContext *ctx, const Record *record, * running these algorithms on a graph where the direction is important, you can use the direction * parameter. For example, direction:"INCOMING" or direction:"OUTGOING". */ -// static void _P2PUnweightedShortestPath(lgraph::Transaction &txn, lgraph::VertexId start_vid, -// lgraph::VertexId end_vid, const std::string &edge_label, -// size_t max_hops, cypher::Path &path, -// const EDGE_FILTER_T &edge_filter, -// parser::LinkDirection &dir, -// std::optional per_node_limit) { -// path.Clear(); -// path.SetStart(start_vid); -// if (start_vid == end_vid) return; -// std::unordered_map parent{{start_vid, start_vid}}; -// std::unordered_map child{{end_vid, end_vid}}; -// std::vector forward_q{start_vid}; -// std::vector backward_q{end_vid}; -// size_t hops = 0; -// parser::LinkDirection forward_dir = dir; -// parser::LinkDirection backward_dir = -// dir == parser::LinkDirection::RIGHT_TO_LEFT ? parser::LinkDirection::LEFT_TO_RIGHT -// : dir == parser::LinkDirection::LEFT_TO_RIGHT ? parser::LinkDirection::RIGHT_TO_LEFT -// : parser::LinkDirection::DIR_NOT_SPECIFIED; -// while (hops++ < max_hops) { -// std::vector next_q; -// // decide which way to search first -// if (forward_q.size() <= backward_q.size()) { -// // search forward -// for (auto vid : forward_q) { -// auto nbrs = _GetNeighbors(txn, vid, edge_label, edge_filter, forward_dir, -// per_node_limit); -// for (auto nbr : nbrs) { -// if (child.find(nbr) != child.end()) { -// // found the path -// _FetchPath(txn, hops, parent, child, start_vid, vid, nbr, end_vid, path); -// return; -// } -// auto it = parent.find(nbr); -// if (it == parent.end()) { -// parent.emplace_hint(it, nbr, vid); -// next_q.push_back(nbr); -// } -// } -// } -// if (next_q.empty()) break; -// forward_q = std::move(next_q); -// } else { -// for (auto vid : backward_q) { -// auto nbrs = _GetNeighbors(txn, vid, edge_label, edge_filter, backward_dir, -// per_node_limit); -// for (auto nbr : nbrs) { -// if (parent.find(nbr) != parent.end()) { -// // found the path -// _FetchPath(txn, hops, parent, child, start_vid, nbr, vid, end_vid, path); -// return; -// } -// auto it = child.find(nbr); -// if (it == child.end()) { -// child.emplace_hint(it, nbr, vid); -// next_q.push_back(nbr); -// } -// } -// } -// if (next_q.empty()) break; -// backward_q = std::move(next_q); -// } -// } -// } - -// template -// static void _EmplaceForwardNeighbor( -// const EIT &eit, const int fhop, const std::unordered_map &child, -// std::unordered_map &parent, -// std::vector> &hits, -// std::vector &next_q) { -// lgraph::VertexId vid, nbr; -// bool direction; -// if (std::is_same::value) { -// vid = eit.GetSrc(); -// nbr = eit.GetDst(); -// direction = true; -// } else { -// vid = eit.GetDst(); -// nbr = eit.GetSrc(); -// direction = false; -// } -// if (child.find(nbr) != child.end()) { -// hits.emplace_back(vid, nbr, eit.GetLabelId(), eit.GetEdgeId(), direction); -// } else { -// auto it = parent.find(nbr); -// if (it == parent.end()) { -// parent.emplace_hint(it, nbr, fhop); -// next_q.emplace_back(nbr); -// } -// } -// } - -// template -// static void _EmplaceBackwardNeighbor( -// const EIT &eit, const int bhop, const std::unordered_map &parent, -// std::unordered_map &child, -// std::vector> &hits, -// std::vector &next_q) { -// lgraph::VertexId vid, nbr; -// bool direction; -// if (std::is_same::value) { -// vid = eit.GetSrc(); -// nbr = eit.GetDst(); -// direction = false; -// } else { -// vid = eit.GetDst(); -// nbr = eit.GetSrc(); -// direction = true; -// } -// if (parent.find(nbr) != parent.end()) { -// hits.emplace_back(nbr, vid, eit.GetLabelId(), eit.GetEdgeId(), direction); -// } else { -// auto it = child.find(nbr); -// if (it == child.end()) { -// child.emplace_hint(it, nbr, bhop); -// next_q.emplace_back(nbr); -// } -// } -// } - -// void _EnumeratePartialPaths(lgraph::Transaction &txn, -// const std::unordered_map &hop_info, const int64_t vid, -// const int depth, const std::string &edge_label, cypher::Path &path, -// std::vector &paths) { -// if (depth != 0) { -// if (edge_label.empty()) { -// for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// int64_t nbr = eit.GetDst(); -// auto it = hop_info.find(nbr); -// if (it != hop_info.end() && it->second == depth - 1) { -// path.Append(eit.GetUid()); -// _EnumeratePartialPaths(txn, hop_info, nbr, depth - 1, -// edge_label, path, paths); -// path.PopBack(); -// } -// } -// for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// int64_t nbr = eit.GetSrc(); -// auto it = hop_info.find(nbr); -// if (it != hop_info.end() && it->second == depth - 1) { -// path.Append(eit.GetUid()); -// _EnumeratePartialPaths(txn, hop_info, nbr, depth - 1, -// edge_label, path, paths); -// path.PopBack(); -// } -// } -// } else { -// auto edge_lid = txn.GetLabelId(false, edge_label); -// for (auto eit = txn.GetOutEdgeIterator(lgraph::EdgeUid( -// vid, 0, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// int64_t nbr = eit.GetDst(); -// auto it = hop_info.find(nbr); -// if (it != hop_info.end() && it->second == depth - 1) { -// path.Append(eit.GetUid()); -// _EnumeratePartialPaths(txn, hop_info, nbr, depth - 1, -// edge_label, path, paths); -// path.PopBack(); -// } -// } -// for (auto eit = txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// int64_t nbr = eit.GetSrc(); -// auto it = hop_info.find(nbr); -// if (it != hop_info.end() && it->second == depth - 1) { -// path.Append(eit.GetUid()); -// _EnumeratePartialPaths(txn, hop_info, nbr, depth - 1, -// edge_label, path, paths); -// path.PopBack(); -// } -// } -// } -// } else { -// paths.emplace_back(path); -// } -// } - -// static void _P2PUnweightedAllShortestPaths(lgraph::Transaction &txn, lgraph::VertexId start_vid, -// lgraph::VertexId end_vid, const std::string &edge_label, -// std::vector &paths) { -// // TODO(heng): fix PrimaryId -// if (start_vid == end_vid) { -// paths.emplace_back(cypher::Path()); -// paths.back().SetStart(start_vid); -// return; -// } -// std::unordered_map parent{{start_vid, 0}}; -// std::unordered_map child{{end_vid, 0}}; -// std::vector forward_q{start_vid}; -// std::vector backward_q{end_vid}; -// int fhop = 0; -// int bhop = 0; -// // -// std::vector< -// std::tuple> -// hits; -// for (int hop = 0; !forward_q.empty() && !backward_q.empty() && hits.empty(); hop++) { -// std::vector next_q; -// if (forward_q.size() <= backward_q.size()) { -// fhop++; -// for (auto vid : forward_q) { -// if (edge_label.empty()) { -// for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); -// } -// for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); -// } -// } else { -// auto edge_lid = txn.GetLabelId(false, edge_label); -// for (auto eit = -// txn.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); -// } -// for (auto eit = -// txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); -// } -// } -// } -// std::sort(next_q.begin(), next_q.end()); -// forward_q.swap(next_q); -// } else { -// bhop++; -// for (auto vid : backward_q) { -// if (edge_label.empty()) { -// for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); -// } -// for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { -// _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); -// } -// } else { -// auto edge_lid = txn.GetLabelId(false, edge_label); -// for (auto eit = -// txn.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); -// } -// for (auto eit = -// txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); -// eit.IsValid(); eit.Next()) { -// if (eit.GetLabelId() != edge_lid) break; -// _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); -// } -// } -// } -// std::sort(next_q.begin(), next_q.end()); -// backward_q.swap(next_q); -// } -// } -// for (auto &hit : hits) { -// std::vector fpaths; -// std::vector bpaths; -// auto fvid = std::get<0>(hit); -// auto bvid = std::get<1>(hit); -// cypher::Path path; -// path.SetStart(fvid); -// _EnumeratePartialPaths(txn, parent, fvid, parent[fvid], edge_label, path, fpaths); -// path.Clear(); -// path.SetStart(bvid); -// _EnumeratePartialPaths(txn, child, bvid, child[bvid], edge_label, path, bpaths); -// for (auto &fpath : fpaths) { -// fpath.Reverse(); -// fpath.Append(std::get<4>(hit) -// ? lgraph::EdgeUid(fvid, bvid, std::get<2>(hit), 0, std::get<3>(hit)) -// : lgraph::EdgeUid(bvid, fvid, std::get<2>(hit), 0, std::get<3>(hit))); -// for (auto &bpath : bpaths) { -// paths.emplace_back(fpath); -// for (int i = 0; i < (int)bpath.Length(); i++) -// paths.back().Append(bpath.GetNthEdge(i)); -// } -// } -// } -// } +static void _P2PUnweightedShortestPath(lgraph::Transaction &txn, lgraph::VertexId start_vid, + lgraph::VertexId end_vid, const std::string &edge_label, + size_t max_hops, cypher::Path &path, + const EDGE_FILTER_T &edge_filter, parser::LinkDirection &dir, + std::optional per_node_limit) { + path.Clear(); + path.SetStart(start_vid); + if (start_vid == end_vid) return; + std::unordered_map parent{{start_vid, start_vid}}; + std::unordered_map child{{end_vid, end_vid}}; + std::vector forward_q{start_vid}; + std::vector backward_q{end_vid}; + size_t hops = 0; + parser::LinkDirection forward_dir = dir; + parser::LinkDirection backward_dir = + dir == parser::LinkDirection::RIGHT_TO_LEFT ? parser::LinkDirection::LEFT_TO_RIGHT + : dir == parser::LinkDirection::LEFT_TO_RIGHT ? parser::LinkDirection::RIGHT_TO_LEFT + : parser::LinkDirection::DIR_NOT_SPECIFIED; + while (hops++ < max_hops) { + std::vector next_q; + // decide which way to search first + if (forward_q.size() <= backward_q.size()) { + // search forward + for (auto vid : forward_q) { + auto nbrs = + _GetNeighbors(txn, vid, edge_label, edge_filter, forward_dir, per_node_limit); + for (auto nbr : nbrs) { + if (child.find(nbr) != child.end()) { + // found the path + _FetchPath(txn, hops, parent, child, start_vid, vid, nbr, end_vid, path); + return; + } + auto it = parent.find(nbr); + if (it == parent.end()) { + parent.emplace_hint(it, nbr, vid); + next_q.push_back(nbr); + } + } + } + if (next_q.empty()) break; + forward_q = std::move(next_q); + } else { + for (auto vid : backward_q) { + auto nbrs = + _GetNeighbors(txn, vid, edge_label, edge_filter, backward_dir, per_node_limit); + for (auto nbr : nbrs) { + if (parent.find(nbr) != parent.end()) { + // found the path + _FetchPath(txn, hops, parent, child, start_vid, nbr, vid, end_vid, path); + return; + } + auto it = child.find(nbr); + if (it == child.end()) { + child.emplace_hint(it, nbr, vid); + next_q.push_back(nbr); + } + } + } + if (next_q.empty()) break; + backward_q = std::move(next_q); + } + } +} + +template +static void _EmplaceForwardNeighbor( + const EIT &eit, const int fhop, const std::unordered_map &child, + std::unordered_map &parent, + std::vector> &hits, + std::vector &next_q) { + lgraph::VertexId vid, nbr; + bool direction; + if (std::is_same::value) { + vid = eit.GetSrc(); + nbr = eit.GetDst(); + direction = true; + } else { + vid = eit.GetDst(); + nbr = eit.GetSrc(); + direction = false; + } + if (child.find(nbr) != child.end()) { + hits.emplace_back(vid, nbr, eit.GetLabelId(), eit.GetEdgeId(), direction); + } else { + auto it = parent.find(nbr); + if (it == parent.end()) { + parent.emplace_hint(it, nbr, fhop); + next_q.emplace_back(nbr); + } + } +} + +template +static void _EmplaceBackwardNeighbor( + const EIT &eit, const int bhop, const std::unordered_map &parent, + std::unordered_map &child, + std::vector> &hits, + std::vector &next_q) { + lgraph::VertexId vid, nbr; + bool direction; + if (std::is_same::value) { + vid = eit.GetSrc(); + nbr = eit.GetDst(); + direction = false; + } else { + vid = eit.GetDst(); + nbr = eit.GetSrc(); + direction = true; + } + if (parent.find(nbr) != parent.end()) { + hits.emplace_back(nbr, vid, eit.GetLabelId(), eit.GetEdgeId(), direction); + } else { + auto it = child.find(nbr); + if (it == child.end()) { + child.emplace_hint(it, nbr, bhop); + next_q.emplace_back(nbr); + } + } +} + +void _EnumeratePartialPaths_v2(lgraph::Transaction &txn, + const std::unordered_map &hop_info, const int64_t vid, + const int depth, const std::string &edge_label, cypher::Path &path, + std::vector &paths) { + if (depth != 0) { + if (edge_label.empty()) { + for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { + int64_t nbr = eit.GetDst(); + auto it = hop_info.find(nbr); + if (it != hop_info.end() && it->second == depth - 1) { + path.Append(eit.GetUid()); + _EnumeratePartialPaths_v2(txn, hop_info, nbr, depth - 1, edge_label, path, + paths); + path.PopBack(); + } + } + for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { + int64_t nbr = eit.GetSrc(); + auto it = hop_info.find(nbr); + if (it != hop_info.end() && it->second == depth - 1) { + path.Append(eit.GetUid()); + _EnumeratePartialPaths_v2(txn, hop_info, nbr, depth - 1, edge_label, path, + paths); + path.PopBack(); + } + } + } else { + auto edge_lid = txn.GetLabelId(false, edge_label); + for (auto eit = txn.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + int64_t nbr = eit.GetDst(); + auto it = hop_info.find(nbr); + if (it != hop_info.end() && it->second == depth - 1) { + path.Append(eit.GetUid()); + _EnumeratePartialPaths_v2(txn, hop_info, nbr, depth - 1, edge_label, path, + paths); + path.PopBack(); + } + } + for (auto eit = txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + int64_t nbr = eit.GetSrc(); + auto it = hop_info.find(nbr); + if (it != hop_info.end() && it->second == depth - 1) { + path.Append(eit.GetUid()); + _EnumeratePartialPaths_v2(txn, hop_info, nbr, depth - 1, edge_label, path, + paths); + path.PopBack(); + } + } + } + } else { + paths.emplace_back(path); + } +} + +static void _P2PUnweightedAllShortestPaths(lgraph::Transaction &txn, lgraph::VertexId start_vid, + lgraph::VertexId end_vid, const std::string &edge_label, + std::vector &paths) { + // TODO(heng): fix PrimaryId + if (start_vid == end_vid) { + paths.emplace_back(cypher::Path()); + paths.back().SetStart(start_vid); + return; + } + std::unordered_map parent{{start_vid, 0}}; + std::unordered_map child{{end_vid, 0}}; + std::vector forward_q{start_vid}; + std::vector backward_q{end_vid}; + int fhop = 0; + int bhop = 0; + // + std::vector< + std::tuple> + hits; + for (int hop = 0; !forward_q.empty() && !backward_q.empty() && hits.empty(); hop++) { + std::vector next_q; + if (forward_q.size() <= backward_q.size()) { + fhop++; + for (auto vid : forward_q) { + if (edge_label.empty()) { + for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { + _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); + } + for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { + _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); + } + } else { + auto edge_lid = txn.GetLabelId(false, edge_label); + for (auto eit = + txn.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); + } + for (auto eit = + txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + _EmplaceForwardNeighbor(eit, fhop, child, parent, hits, next_q); + } + } + } + std::sort(next_q.begin(), next_q.end()); + forward_q.swap(next_q); + } else { + bhop++; + for (auto vid : backward_q) { + if (edge_label.empty()) { + for (auto eit = txn.GetOutEdgeIterator(vid); eit.IsValid(); eit.Next()) { + _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); + } + for (auto eit = txn.GetInEdgeIterator(vid); eit.IsValid(); eit.Next()) { + _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); + } + } else { + auto edge_lid = txn.GetLabelId(false, edge_label); + for (auto eit = + txn.GetOutEdgeIterator(lgraph::EdgeUid(vid, 0, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); + } + for (auto eit = + txn.GetInEdgeIterator(lgraph::EdgeUid(0, vid, edge_lid, 0, 0), true); + eit.IsValid(); eit.Next()) { + if (eit.GetLabelId() != edge_lid) break; + _EmplaceBackwardNeighbor(eit, bhop, parent, child, hits, next_q); + } + } + } + std::sort(next_q.begin(), next_q.end()); + backward_q.swap(next_q); + } + } + for (auto &hit : hits) { + std::vector fpaths; + std::vector bpaths; + auto fvid = std::get<0>(hit); + auto bvid = std::get<1>(hit); + cypher::Path path; + path.SetStart(fvid); + _EnumeratePartialPaths_v2(txn, parent, fvid, parent[fvid], edge_label, path, fpaths); + path.Clear(); + path.SetStart(bvid); + _EnumeratePartialPaths_v2(txn, child, bvid, child[bvid], edge_label, path, bpaths); + for (auto &fpath : fpaths) { + fpath.Reverse(); + fpath.Append(std::get<4>(hit) + ? lgraph::EdgeUid(fvid, bvid, std::get<2>(hit), 0, std::get<3>(hit)) + : lgraph::EdgeUid(bvid, fvid, std::get<2>(hit), 0, std::get<3>(hit))); + for (auto &bpath : bpaths) { + paths.emplace_back(fpath); + for (int i = 0; i < (int)bpath.Length(); i++) + paths.back().Append(bpath.GetNthEdge(i)); + } + } + } +} void AlgoFuncV2::ShortestPath(RTContext *ctx, const Record *record, const cypher::VEC_EXPR_V2 &args, const cypher::VEC_STR_V2 &yield_items, std::vector *records) { - // CYPHER_DB_PROCEDURE_GRAPH_CHECK(); - // CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") - // CYPHER_ARG_CHECK(args[0].IsString() && - // args[1].IsString() && - // (args.size() == 2 || args[2].type == parser::Expression::MAP), - // "wrong type") - // std::string edge_label; - // EDGE_FILTER_T edge_filter; - // parser::LinkDirection direction = parser::LinkDirection::DIR_NOT_SPECIFIED; - // size_t max_hops = 20; - // if (args.size() == 3) { - // auto &map = args[2].Map(); - // auto it = map.find("relationshipQuery"); - // if (it != map.end()) { - // if (it->second.type != parser::Expression::STRING) CYPHER_TODO(); - // edge_label = it->second.constant.scalar.AsString(); - // } - // auto max = map.find("maxHops"); - // if (max != map.end()) { - // if (max->second.type != parser::Expression::INT) CYPHER_TODO(); - // max_hops = max->second.constant.scalar.integer(); - // } - // auto map_edge_filter = map.find("edgeFilter"); - // if (map_edge_filter != map.end()) { - // if (map_edge_filter->second.type != parser::Expression::MAP) CYPHER_TODO(); - // for (auto &kv : map_edge_filter->second.Map()) { - // if (kv.second.type != parser::Expression::MAP) CYPHER_TODO(); - // std::unordered_map filter_t; - // for (auto filter_pair : kv.second.Map()) { - // lgraph::FieldData field; - // if (!filter_pair.second.IsLiteral()) CYPHER_TODO(); - // field = parser::MakeFieldData(filter_pair.second); - // filter_t.emplace(filter_pair.first, field); - // } - // edge_filter.emplace(kv.first, filter_t); - // } - // } - // auto it_dir = map.find("direction"); - // if (it_dir != map.end()) { - // if (it_dir->second.type != parser::Expression::STRING) CYPHER_TODO(); - // if (it_dir->second.constant.scalar.AsString() == "PointingLeft") { - // direction = parser::LinkDirection::RIGHT_TO_LEFT; - // } else if (it_dir->second.constant.scalar.AsString() == "PointingRight") { - // direction = parser::LinkDirection::LEFT_TO_RIGHT; - // } else if (it_dir->second.constant.scalar.AsString() == "AnyDirection") { - // direction = parser::LinkDirection::DIR_NOT_SPECIFIED; - // } else { - // CYPHER_TODO(); - // } - // } - // } - // CYPHER_THROW_ASSERT(record); - // auto it1 = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); - // auto it2 = record->symbol_table->symbols.find(args[1].constant.scalar.AsString()); - // if (it1 == record->symbol_table->symbols.end() - // || it2 == record->symbol_table->symbols.end()) { - // CYPHER_TODO(); - // } - // auto start_vid = record->values[it1->second.id].node->PullVid(); - // auto end_vid = record->values[it2->second.id].node->PullVid(); - // cypher::Path path; - // auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); - // _P2PUnweightedShortestPath(*ctx->txn_->GetTxn(), start_vid, end_vid, edge_label, max_hops, - // path, edge_filter, direction, std::nullopt); - - // auto pp = global_ptable.GetProcedure("algo.shortestPath"); - // CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("nodeCount") && - // pp->ContainsYieldItem("totalCost") && pp->ContainsYieldItem("path")); - // cypher::VEC_STR_V2 titles = ProcedureTitlesV2("algo.shortestPath", yield_items); - // std::unordered_map> lmap = { - // {"nodeCount", - // [&](Record &r) { - // r.AddConstant(lgraph::FieldData( - // static_cast(path.Length() == 0 ? 0 : path.Length() + 1))); - // }}, - // {"totalCost", - // [&](Record &r) { r.AddConstant( - // lgraph::FieldData(static_cast(path.Length()))); }}, - // {"path", [&](Record &r) { r.AddConstant(lgraph::FieldData(path.ToString())); }}}; - // Record r; - // for (auto &title : titles) { - // lmap.find(title)->second(r); - // } - // records->emplace_back(r.Snapshot()); + CYPHER_DB_PROCEDURE_GRAPH_CHECK(); + CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") + CYPHER_ARG_CHECK( + args[0].IsString() && args[1].IsString() && (args.size() == 2 || args[2].IsMap()), + "wrong type") + std::string edge_label; + EDGE_FILTER_T edge_filter; + parser::LinkDirection direction = parser::LinkDirection::DIR_NOT_SPECIFIED; + size_t max_hops = 20; + if (args.size() == 3) { + auto &map = *args[2].constant.map; + auto it = map.find("relationshipQuery"); + if (it != map.end()) { + if (it->second.IsString()) CYPHER_TODO(); + edge_label = it->second.AsString(); + } + auto max = map.find("maxHops"); + if (max != map.end()) { + if (max->second.IsInteger()) CYPHER_TODO(); + max_hops = max->second.integer(); + } + auto map_edge_filter = map.find("edgeFilter"); + if (map_edge_filter != map.end()) { + CYPHER_TODO(); + // if (map_edge_filter->second.type != parser::Expression::MAP) CYPHER_TODO(); + // for (auto &kv : map_edge_filter->second.Map()) { + // if (kv.second.type != parser::Expression::MAP) CYPHER_TODO(); + // std::unordered_map filter_t; + // for (auto filter_pair : kv.second.Map()) { + // lgraph::FieldData field; + // if (!filter_pair.second.IsLiteral()) CYPHER_TODO(); + // field = parser::MakeFieldData(filter_pair.second); + // filter_t.emplace(filter_pair.first, field); + // } + // edge_filter.emplace(kv.first, filter_t); + // } + } + auto it_dir = map.find("direction"); + if (it_dir != map.end()) { + if (!it_dir->second.IsString()) CYPHER_TODO(); + if (it_dir->second.AsString() == "PointingLeft") { + direction = parser::LinkDirection::RIGHT_TO_LEFT; + } else if (it_dir->second.AsString() == "PointingRight") { + direction = parser::LinkDirection::LEFT_TO_RIGHT; + } else if (it_dir->second.AsString() == "AnyDirection") { + direction = parser::LinkDirection::DIR_NOT_SPECIFIED; + } else { + CYPHER_TODO(); + } + } + } + CYPHER_THROW_ASSERT(record); + auto it1 = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); + auto it2 = record->symbol_table->symbols.find(args[1].constant.scalar.AsString()); + if (it1 == record->symbol_table->symbols.end() || it2 == record->symbol_table->symbols.end()) { + CYPHER_TODO(); + } + auto start_vid = record->values[it1->second.id].node->PullVid(); + auto end_vid = record->values[it2->second.id].node->PullVid(); + cypher::Path path; + auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + _P2PUnweightedShortestPath(*ctx->txn_->GetTxn(), start_vid, end_vid, edge_label, max_hops, path, + edge_filter, direction, std::nullopt); + + auto pp = global_ptable.GetProcedure("algo.shortestPath"); + CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("nodeCount") && + pp->ContainsYieldItem("totalCost") && pp->ContainsYieldItem("path")); + cypher::VEC_STR_V2 titles = ProcedureTitlesV2("algo.shortestPath", yield_items); + std::unordered_map> lmap = { + {"nodeCount", + [&](Record &r) { + r.AddConstant(lgraph::FieldData( + static_cast(path.Length() == 0 ? 0 : path.Length() + 1))); + }}, + {"totalCost", + [&](Record &r) { r.AddConstant(lgraph::FieldData(static_cast(path.Length()))); }}, + {"path", [&](Record &r) { r.AddConstant(lgraph::FieldData(path.ToString())); }}}; + Record r; + for (auto &title : titles) { + lmap.find(title)->second(r); + } + records->emplace_back(r.Snapshot()); } void AlgoFuncV2::AllShortestPaths(RTContext *ctx, const Record *record, const cypher::VEC_EXPR_V2 &args, const cypher::VEC_STR_V2 &yield_items, std::vector *records) { - // CYPHER_DB_PROCEDURE_GRAPH_CHECK(); - // CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") - // CYPHER_ARG_CHECK(args[0].IsString() && - // args[1].IsString() && - // (args.size() == 2 || args[2].type == parser::Expression::MAP), - // "wrong type") - // std::string edge_label; - // if (args.size() == 3) { - // auto &map = args[2].Map(); - // auto it = map.find("relationshipQuery"); - // if (it != map.end()) { - // if (it->second.type != parser::Expression::STRING) CYPHER_TODO(); - // edge_label = it->second.constant.scalar.AsString(); - // } - // } - // CYPHER_THROW_ASSERT(record); - // auto it1 = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); - // auto it2 = record->symbol_table->symbols.find(args[1].constant.scalar.AsString()); - // if (it1 == record->symbol_table->symbols.end() || - // it2 == record->symbol_table->symbols.end()) { - // CYPHER_TODO(); - // } - // auto start_vid = record->values[it1->second.id].node->PullVid(); - // auto end_vid = record->values[it2->second.id].node->PullVid(); - // std::vector paths; - // auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); - // _P2PUnweightedAllShortestPaths(*ctx->txn_->GetTxn(), start_vid, end_vid, edge_label, paths); - - // auto pp = global_ptable.GetProcedure("algo.allShortestPaths"); - // CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("nodeIds") && - // pp->ContainsYieldItem("relationshipIds") && pp->ContainsYieldItem("cost")); - // cypher::VEC_STR_V2 titles = ProcedureTitlesV2("algo.allShortestPaths", yield_items); - // std::unordered_map> lmap = { - // {"nodeIds", - // [&](const cypher::Path &path, Record &r) { - // auto ids = cypher::FieldData::Array(0); - // for (int i = 0; i <= (int)path.Length(); i++) { - // ids.array->emplace_back( - // lgraph::FieldData(static_cast(path.ids_[i * 2]))); - // } - // r.AddConstant(ids); - // }}, - // {"relationshipIds", - // [&](const cypher::Path &path, Record &r) { - // auto ids = cypher::FieldData::Array(0); - // for (int i = 0; i < (int)path.Length(); i++) { - // ids.array->emplace_back( - // lgraph::FieldData(_detail::EdgeUid2String(path.GetNthEdge(i)))); - // } - // r.AddConstant(ids); - // }}, - // {"cost", - // [&](const cypher::Path &path, Record &r) { - // r.AddConstant(lgraph::FieldData(static_cast(path.Length()))); - // }}, - // }; - // for (auto &path : paths) { - // Record r; - // for (auto &title : titles) { - // lmap.find(title)->second(path, r); - // } - // records->emplace_back(r.Snapshot()); - // } + CYPHER_DB_PROCEDURE_GRAPH_CHECK(); + CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") + CYPHER_ARG_CHECK( + args[0].IsString() && args[1].IsString() && (args.size() == 2 || args[2].IsMap()), + "wrong type") + std::string edge_label; + if (args.size() == 3) { + auto &map = *args[2].constant.map; + auto it = map.find("relationshipQuery"); + if (it != map.end()) { + if (!it->second.IsString()) CYPHER_TODO(); + edge_label = it->second.AsString(); + } + } + CYPHER_THROW_ASSERT(record); + auto it1 = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); + auto it2 = record->symbol_table->symbols.find(args[1].constant.scalar.AsString()); + if (it1 == record->symbol_table->symbols.end() || it2 == record->symbol_table->symbols.end()) { + CYPHER_TODO(); + } + auto start_vid = record->values[it1->second.id].node->PullVid(); + auto end_vid = record->values[it2->second.id].node->PullVid(); + std::vector paths; + auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + _P2PUnweightedAllShortestPaths(*ctx->txn_->GetTxn(), start_vid, end_vid, edge_label, paths); + + auto pp = global_ptable.GetProcedure("algo.allShortestPaths"); + CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("nodeIds") && + pp->ContainsYieldItem("relationshipIds") && pp->ContainsYieldItem("cost")); + cypher::VEC_STR_V2 titles = ProcedureTitlesV2("algo.allShortestPaths", yield_items); + std::unordered_map> lmap = { + {"nodeIds", + [&](const cypher::Path &path, Record &r) { + auto ids = cypher::FieldData::Array(0); + for (int i = 0; i <= (int)path.Length(); i++) { + ids.array->emplace_back(lgraph::FieldData(static_cast(path.ids_[i * 2]))); + } + r.AddConstant(ids); + }}, + {"relationshipIds", + [&](const cypher::Path &path, Record &r) { + auto ids = cypher::FieldData::Array(0); + for (int i = 0; i < (int)path.Length(); i++) { + ids.array->emplace_back( + lgraph::FieldData(_detail::EdgeUid2String(path.GetNthEdge(i)))); + } + r.AddConstant(ids); + }}, + {"cost", + [&](const cypher::Path &path, Record &r) { + r.AddConstant(lgraph::FieldData(static_cast(path.Length()))); + }}, + }; + for (auto &path : paths) { + Record r; + for (auto &title : titles) { + lmap.find(title)->second(path, r); + } + records->emplace_back(r.Snapshot()); + } } void AlgoFuncV2::NativeExtract(RTContext *ctx, const cypher::Record *record, const cypher::VEC_EXPR_V2 &args, const cypher::VEC_STR_V2 &yield_items, struct std::vector *records) { - // CYPHER_DB_PROCEDURE_GRAPH_CHECK(); - // CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") - // CYPHER_ARG_CHECK( - // args[0].IsString() && args[1].type == parser::Expression::MAP, - // "wrong type") - // auto &config = args[1].Map(); - // auto it1 = config.find("isNode"); - // auto it2 = config.find("field"); - // if (it1 == config.end() || it1->second.type != parser::Expression::BOOL || - // it2 == config.end() || it2->second.type != parser::Expression::STRING) { - // CYPHER_TODO(); - // } - // cypher::FieldData value; - // if (it1->second.constant.scalar.AsBool()) { - // CYPHER_THROW_ASSERT(record); - // auto i = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); - // if (i == record->symbol_table->symbols.end()) CYPHER_TODO(); - // auto &vid = record->values[i->second.id]; - // auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); - // if (vid.IsArray()) { - // value = cypher::FieldData::Array(0); - // for (auto &id : *vid.constant.array) { - // value.array->emplace_back( - // ctx->txn_->GetTxn()->GetVertexField(id.AsInt64(), - // it2->second.constant.scalar.AsString())); - // } - // } else { - // CYPHER_TODO(); - // } - // } else { - // CYPHER_THROW_ASSERT(record); - // auto i = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); - // if (i == record->symbol_table->symbols.end()) CYPHER_TODO(); - // auto &eid = record->values[i->second.id]; - // if (!eid.IsString()) CYPHER_TODO(); - // auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); - // value = ctx->txn_->GetTxn()->GetEdgeField( - // _detail::ExtractEdgeUid(eid.constant.scalar.AsString()), - // it2->second.constant.scalar.AsString()); - // } - - // auto pp = global_ptable.GetProcedure("algo.native.extract"); - // CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("value")); - // cypher::VEC_STR_V2 titles; - // if (yield_items.empty()) { - // for (auto &res : pp->signature.result_list) titles.emplace_back(res.name); - // } else { - // for (auto &item : yield_items) { - // if (!pp->ContainsYieldItem(item)) { - // throw lgraph::CypherException("Unknown procedure output: " + item); - // } - // titles.emplace_back(item); - // } - // } - // std::unordered_map> lmap = { - // {"value", [&](const cypher::FieldData &d, Record &r) { r.AddConstant(d); }}, - // }; - // Record r; - // for (auto &title : titles) { - // lmap.find(title)->second(value, r); - // } - // records->emplace_back(r.Snapshot()); + CYPHER_DB_PROCEDURE_GRAPH_CHECK(); + CYPHER_ARG_CHECK(args.size() / 2 == 1, "wrong arguments number") + CYPHER_ARG_CHECK(args[0].IsString() && args[1].IsMap(), "wrong type") + auto &config = *args[1].constant.map; + auto it1 = config.find("isNode"); + auto it2 = config.find("field"); + if (it1 == config.end() || !it1->second.IsBool() || it2 == config.end() || + !it2->second.IsString()) { + CYPHER_TODO(); + } + cypher::FieldData value; + if (it1->second.AsBool()) { + CYPHER_THROW_ASSERT(record); + auto i = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); + if (i == record->symbol_table->symbols.end()) CYPHER_TODO(); + auto &vid = record->values[i->second.id]; + auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + if (vid.IsArray()) { + value = cypher::FieldData::Array(0); + for (auto &id : *vid.constant.array) { + value.array->emplace_back( + ctx->txn_->GetTxn()->GetVertexField(id.AsInt64(), it2->second.AsString())); + } + } else { + CYPHER_TODO(); + } + } else { + CYPHER_THROW_ASSERT(record); + auto i = record->symbol_table->symbols.find(args[0].constant.scalar.AsString()); + if (i == record->symbol_table->symbols.end()) CYPHER_TODO(); + auto &eid = record->values[i->second.id]; + if (!eid.IsString()) CYPHER_TODO(); + auto ac_db = ctx->galaxy_->OpenGraph(ctx->user_, ctx->graph_); + value = ctx->txn_->GetTxn()->GetEdgeField( + _detail::ExtractEdgeUid(eid.constant.scalar.AsString()), it2->second.AsString()); + } + + auto pp = global_ptable.GetProcedure("algo.native.extract"); + CYPHER_THROW_ASSERT(pp && pp->ContainsYieldItem("value")); + cypher::VEC_STR_V2 titles; + if (yield_items.empty()) { + for (auto &res : pp->signature.result_list) titles.emplace_back(res.name); + } else { + for (auto &item : yield_items) { + if (!pp->ContainsYieldItem(item)) { + throw lgraph::CypherException("Unknown procedure output: " + item); + } + titles.emplace_back(item); + } + } + std::unordered_map> lmap = + { {"value", [&](const cypher::FieldData &d, Record &r) { r.AddConstant(d); }}, + }; + Record r; + for (auto &title : titles) { + lmap.find(title)->second(value, r); + } + records->emplace_back(r.Snapshot()); } void AlgoFuncV2::PageRank(RTContext *ctx, const cypher::Record *record, diff --git a/src/cypher/procedure/utils.h b/src/cypher/procedure/utils.h index 36e036701d..c4288e8fc1 100644 --- a/src/cypher/procedure/utils.h +++ b/src/cypher/procedure/utils.h @@ -21,6 +21,7 @@ #include "lgraph/lgraph_types.h" #include "lgraph/lgraph_result.h" #include "lgraph_api/result_element.h" +#include "geax-front-end/ast/utils/AstUtil.h" namespace lgraph { @@ -132,6 +133,17 @@ class Utils { return ctx.ac_db_->CallPlugin(ctx.txn_.get(), type, "A_DUMMY_TOKEN_FOR_CPP_PLUGIN", name, input, 0, false, output); } + + static bool CallPlugin(const RTContext& ctx, lgraph::plugin::Type type, const std::string& name, + const std::vector& params, std::string& output) { + std::string input; + CYPHER_THROW_ASSERT(params.size() <= 1); + if (!params.empty()) { + input = geax::frontend::ToString(params[0]); + } + return ctx.ac_db_->CallPlugin(ctx.txn_.get(), type, "A_DUMMY_TOKEN_FOR_CPP_PLUGIN", name, + input, 0, false, output); + } }; } // namespace cypher diff --git a/src/cypher/resultset/record.h b/src/cypher/resultset/record.h index c6b4e498cd..699019b37e 100644 --- a/src/cypher/resultset/record.h +++ b/src/cypher/resultset/record.h @@ -86,6 +86,8 @@ struct Entry { bool IsArray() const { return type == CONSTANT && constant.type == cypher::FieldData::ARRAY; } + bool IsMap() const { return type == CONSTANT && constant.type == cypher::FieldData::MAP; } + bool IsScalar() const { return type == CONSTANT && constant.type == cypher::FieldData::SCALAR; } bool IsBool() const { return type == CONSTANT && constant.IsBool(); } @@ -116,11 +118,13 @@ struct Entry { return (type == rhs.type && constant == rhs.constant) || (EqualNull() && rhs.EqualNull()); case NODE: - // TODO(anyone) should be this.vid = rhs.vid? - return (type == rhs.type && node == rhs.node) || (EqualNull() && rhs.EqualNull()); + return (EqualNull() && rhs.EqualNull()) || + (type == rhs.type && node && rhs.node && node->PullVid() == rhs.node->PullVid()); case RELATIONSHIP: - return (type == rhs.type && relationship == rhs.relationship) || - (EqualNull() && rhs.EqualNull()); + return (EqualNull() && rhs.EqualNull()) || + (type == rhs.type && relationship && rhs.relationship && relationship->ItRef() && + rhs.relationship->ItRef() && + relationship->ItRef()->GetUid() == rhs.relationship->ItRef()->GetUid()); case VAR_LEN_RELP: case NODE_SNAPSHOT: case RELP_SNAPSHOT: diff --git a/src/cypher/rewriter/MultiPathPatternRewriter.h b/src/cypher/rewriter/MultiPathPatternRewriter.h new file mode 100644 index 0000000000..af6491aa75 --- /dev/null +++ b/src/cypher/rewriter/MultiPathPatternRewriter.h @@ -0,0 +1,100 @@ +/** + * Copyright 2022 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License") {} + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#pragma once + +#include +#include "geax-front-end/ast/clause/ElementFiller.h" + +#include "cypher/utils/ast_node_visitor_impl.h" +#include "cypher/parser/data_typedef.h" +#include "geax-front-end/ast/expr/Expr.h" +#include "geax-front-end/common/ObjectAllocator.h" + +namespace cypher { + +class MultiPathPatternRewriter : public AstNodeVisitorImpl { + public: + explicit MultiPathPatternRewriter(geax::common::ObjectArenaAllocator& allocator) + : allocator_(allocator) {} + std::any visit(geax::frontend::GraphPattern* node) override { + in_graph_pattern_ = true; + node_alias_.clear(); + filter_.clear(); + for (auto path_pattern : node->pathPatterns()) { + ACCEPT_AND_CHECK_WITH_ERROR_MSG(path_pattern); + } + if (node->where().has_value()) { + filter_.emplace_back(node->where().value()); + } + auto filter = MergeFilter(filter_, allocator_); + if (filter) { + node->setWhere(filter); + } + in_graph_pattern_ = false; + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + + std::any visit(geax::frontend::Node* node) override { + in_node_ = true; + ACCEPT_AND_CHECK_WITH_ERROR_MSG(node->filler()); + in_node_ = false; + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + + std::any visit(geax::frontend::ElementFiller* node) override { + if (in_graph_pattern_ && in_node_ && node->v().has_value()) { + if (node_alias_.find(node->v().value()) != node_alias_.end()) { + std::string pre_name = node->v().value(); + node->setV(parser::ANONYMOUS + std::string("MULTI_") + std::to_string(idx_++)); + std::string new_name = node->v().value(); + auto lhs = allocator_.allocate(); + lhs->setName(std::move(pre_name)); + auto rhs = allocator_.allocate(); + rhs->setName(std::move(new_name)); + auto equal = allocator_.allocate(); + equal->setLeft(lhs); + equal->setRight(rhs); + filter_.push_back(equal); + } + node_alias_.insert(node->v().value()); + } + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + + private: + static geax::frontend::Expr* MergeFilter(std::vector& filter, + geax::common::ObjectArenaAllocator& allocator) { + geax::frontend::Expr* ret = nullptr; + for (auto expr : filter) { + if (ret == nullptr) { + ret = expr; + } else { + auto and_expr = allocator.allocate(); + and_expr->setLeft(ret); + and_expr->setRight(expr); + ret = and_expr; + } + } + return ret; + } + size_t idx_ = 0; + bool in_graph_pattern_ = false; + bool in_node_ = false; + geax::common::ObjectArenaAllocator& allocator_; + std::set node_alias_; + std::vector filter_; +}; + +} // namespace cypher diff --git a/src/cypher/utils/ast_node_visitor_impl.h b/src/cypher/utils/ast_node_visitor_impl.h index d684dd09b7..c12b153301 100644 --- a/src/cypher/utils/ast_node_visitor_impl.h +++ b/src/cypher/utils/ast_node_visitor_impl.h @@ -926,6 +926,14 @@ class AstNodeVisitorImpl : public geax::frontend::AstNodeVisitor { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } + std::any visit(geax::frontend::UnwindStatement* node) override { + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + + std::any visit(geax::frontend::InQueryProcedureCall* node) override { + return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; + } + std::any visit(geax::frontend::DummyNode* node) override { return geax::frontend::GEAXErrorCode::GEAX_SUCCEED; } diff --git a/src/cypher/utils/geax_expr_util.h b/src/cypher/utils/geax_expr_util.h index 0f811d6927..b9327625ef 100644 --- a/src/cypher/utils/geax_expr_util.h +++ b/src/cypher/utils/geax_expr_util.h @@ -26,15 +26,15 @@ #include "core/data_type.h" #include "geax-front-end/ast/Ast.h" -#ifndef BINARY_EXPR_TOSTRING -#define BINARY_EXPR_TOSTRING(op) \ +#ifndef EXP_BINARY_EXPR_TOSTRING +#define EXP_BINARY_EXPR_TOSTRING(op) \ auto lef = std::any_cast(node->left()->accept(*this)); \ auto rig = std::any_cast(node->right()->accept(*this)); \ return "(" + lef + op + rig + ")"; #endif -#ifndef UNARY_EXPR_TOSTRING -#define UNARY_EXPR_TOSTRING(op) \ +#ifndef EXP_UNARY_EXPR_TOSTRING +#define EXP_UNARY_EXPR_TOSTRING(op) \ auto expr = std::any_cast(node->expr()->accept(*this)); \ return "(" + std::string(op) + expr + ")"; #endif @@ -57,27 +57,33 @@ class AstExprToString : public geax::frontend::AstExprNodeVisitorImpl { } std::any visit(geax::frontend::TupleGet* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::Not* node) override { UNARY_EXPR_TOSTRING("!"); } - std::any visit(geax::frontend::Neg* node) override { UNARY_EXPR_TOSTRING("-"); } + std::any visit(geax::frontend::Not* node) override { EXP_UNARY_EXPR_TOSTRING("!"); } + std::any visit(geax::frontend::Neg* node) override { EXP_UNARY_EXPR_TOSTRING("-"); } std::any visit(geax::frontend::Tilde* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::VSome* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::BEqual* node) override { BINARY_EXPR_TOSTRING("="); } - std::any visit(geax::frontend::BNotEqual* node) override { BINARY_EXPR_TOSTRING("!="); } - std::any visit(geax::frontend::BGreaterThan* node) override { BINARY_EXPR_TOSTRING(">"); } - std::any visit(geax::frontend::BNotSmallerThan* node) override { BINARY_EXPR_TOSTRING(">="); } - std::any visit(geax::frontend::BSmallerThan* node) override { BINARY_EXPR_TOSTRING("<"); } - std::any visit(geax::frontend::BNotGreaterThan* node) override { BINARY_EXPR_TOSTRING("<="); } + std::any visit(geax::frontend::BEqual* node) override { EXP_BINARY_EXPR_TOSTRING("="); } + std::any visit(geax::frontend::BNotEqual* node) override { EXP_BINARY_EXPR_TOSTRING("!="); } + std::any visit(geax::frontend::BGreaterThan* node) override { EXP_BINARY_EXPR_TOSTRING(">"); } + std::any visit(geax::frontend::BNotSmallerThan* node) override { + EXP_BINARY_EXPR_TOSTRING(">="); + } + std::any visit(geax::frontend::BSmallerThan* node) override { EXP_BINARY_EXPR_TOSTRING("<"); } + std::any visit(geax::frontend::BNotGreaterThan* node) override { + EXP_BINARY_EXPR_TOSTRING("<="); + } std::any visit(geax::frontend::BSafeEqual* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::BAdd* node) override { BINARY_EXPR_TOSTRING("+"); } - std::any visit(geax::frontend::BSub* node) override { BINARY_EXPR_TOSTRING("-"); } - std::any visit(geax::frontend::BDiv* node) override { BINARY_EXPR_TOSTRING("/"); } - std::any visit(geax::frontend::BMul* node) override { BINARY_EXPR_TOSTRING("*"); } - std::any visit(geax::frontend::BMod* node) override { BINARY_EXPR_TOSTRING("%"); } - std::any visit(geax::frontend::BSquare* node) override { BINARY_EXPR_TOSTRING(" ^ "); } - std::any visit(geax::frontend::BAnd* node) override { BINARY_EXPR_TOSTRING(" and "); } - std::any visit(geax::frontend::BOr* node) override { BINARY_EXPR_TOSTRING(" or "); } - std::any visit(geax::frontend::BXor* node) override { BINARY_EXPR_TOSTRING(" xor "); } - std::any visit(geax::frontend::IsLabeled* node) override { UNARY_EXPR_TOSTRING(" isLabel "); } + std::any visit(geax::frontend::BAdd* node) override { EXP_BINARY_EXPR_TOSTRING("+"); } + std::any visit(geax::frontend::BSub* node) override { EXP_BINARY_EXPR_TOSTRING("-"); } + std::any visit(geax::frontend::BDiv* node) override { EXP_BINARY_EXPR_TOSTRING("/"); } + std::any visit(geax::frontend::BMul* node) override { EXP_BINARY_EXPR_TOSTRING("*"); } + std::any visit(geax::frontend::BMod* node) override { EXP_BINARY_EXPR_TOSTRING("%"); } + std::any visit(geax::frontend::BSquare* node) override { EXP_BINARY_EXPR_TOSTRING(" ^ "); } + std::any visit(geax::frontend::BAnd* node) override { EXP_BINARY_EXPR_TOSTRING(" and "); } + std::any visit(geax::frontend::BOr* node) override { EXP_BINARY_EXPR_TOSTRING(" or "); } + std::any visit(geax::frontend::BXor* node) override { EXP_BINARY_EXPR_TOSTRING(" xor "); } + std::any visit(geax::frontend::IsLabeled* node) override { + EXP_UNARY_EXPR_TOSTRING(" isLabel "); + } std::any visit(geax::frontend::BBitAnd* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::BBitOr* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::BBitXor* node) override { NOT_SUPPORT_AND_THROW(); } @@ -86,7 +92,12 @@ class AstExprToString : public geax::frontend::AstExprNodeVisitorImpl { std::any visit(geax::frontend::BConcat* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::BIndex* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::BLike* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::BIn* node) override { NOT_SUPPORT_AND_THROW(); } + std::any visit(geax::frontend::BIn* node) override { + std::string str = std::any_cast(node->left()->accept(*this)); + str += " In "; + str += std::any_cast(node->right()->accept(*this)); + return str; + } std::any visit(geax::frontend::If* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::Function* node) override { @@ -109,7 +120,22 @@ class AstExprToString : public geax::frontend::AstExprNodeVisitorImpl { std::any visit(geax::frontend::BAggFunc* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::MultiCount* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::Windowing* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::MkList* node) override { NOT_SUPPORT_AND_THROW(); } + std::any visit(geax::frontend::MkList* node) override { + std::string res("{"); + const auto& exprs = node->elems(); + for (size_t idx = 0; idx < exprs.size(); ++idx) { + std::string temp; + if (idx != exprs.size() - 1) { + checkedAnyCast(exprs[idx]->accept(*this), temp); + res += temp; + res += ", "; + } + checkedAnyCast(exprs[idx]->accept(*this), temp); + res += temp; + res += "}"; + } + return res; + } std::any visit(geax::frontend::MkMap* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::MkRecord* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::MkSet* node) override { NOT_SUPPORT_AND_THROW(); } @@ -122,14 +148,14 @@ class AstExprToString : public geax::frontend::AstExprNodeVisitorImpl { std::any visit(geax::frontend::VDatetime* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::VDuration* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::VTime* node) override { NOT_SUPPORT_AND_THROW(); } - std::any visit(geax::frontend::VNull* node) override { NOT_SUPPORT_AND_THROW(); } + std::any visit(geax::frontend::VNull* node) override { return std::string("__nul__"); } std::any visit(geax::frontend::VNone* node) override { NOT_SUPPORT_AND_THROW(); } std::any visit(geax::frontend::Ref* node) override { return node->name(); } - std::any visit(geax::frontend::Param* node) override { NOT_SUPPORT_AND_THROW(); } + std::any visit(geax::frontend::Param* node) override { return node->name(); } std::any visit(geax::frontend::IsNull* node) override { - std::string str = std::any_cast(node->expr()->accept(*this)); - str += " IS NULL"; - return str; + std::string str = std::any_cast(node->expr()->accept(*this)); + str += "IS NULL"; + return str; } std::any reportError() override { return error_msg_; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 912cab0678..64e380bd2f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -33,6 +33,7 @@ add_executable(unit_test test_cypher.cpp test_cypher_v2.cpp test_cypher_plan.cpp + test_cypher_field_data.cpp test_data_type.cpp test_db_management_client.cpp test_dense_string.cpp diff --git a/test/resource/unit_test/add/cypher/add.result b/test/resource/unit_test/add/cypher/add.result index 73145ba31b..f821aafad8 100644 --- a/test/resource/unit_test/add/cypher/add.result +++ b/test/resource/unit_test/add/cypher/add.result @@ -15,7 +15,7 @@ MATCH (a:Person {name:'Passerby A'}), (sy:City {name:'Sanya'}) WITH a,sy CREATE CREATE (passerC:Person {name:'Passerby C'}); [{"":"created 1 vertices, created 0 edges."}] MATCH (p:Person {name:'Passerby C'}) RETURN exists(p.birthyear) /* false */; -[{"{EXISTS(p.birthyear)}":false}] +[{"exists(p.birthyear)":false}] WITH 'Passerby D' AS x, 2020 AS y CREATE (:Person {name:x, birthyear:y}); [{"":"created 1 vertices, created 0 edges."}] MATCH (a {name:'Passerby A'}) CREATE (:Person {name:'Passerby E', birthyear:a.birthyear}); diff --git a/test/resource/unit_test/add/cypher/add.test b/test/resource/unit_test/add/cypher/add.test index f2c7f0ca1d..ad5aaa9a71 100644 --- a/test/resource/unit_test/add/cypher/add.test +++ b/test/resource/unit_test/add/cypher/add.test @@ -1,10 +1,10 @@ CREATE (passerA:Person {name:'Passerby A', birthyear:1983}) CREATE (passerB:Person {name:'Passerby B', birthyear:1984}) CREATE (passerA)-[:MARRIED]->(passerB), (passerB)-[:MARRIED]->(passerA); -#MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); +MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); MATCH (a:Person {name:'Lindsay Lohan'})-[r]->(b:Film {title:'The Parent Trap'}) RETURN r; -#MATCH (a:Film),(b:City) CREATE (a)-[:BORN_IN]->(b) /* 15 edges */; +MATCH (a:Film),(b:City) CREATE (a)-[:BORN_IN]->(b) /* 15 edges */; CREATE (sy:City {name:'Sanya'}) RETURN sy,sy.name; -#MATCH (a:Person {name:'Passerby A'}), (sy:City {name:'Sanya'}) CREATE (a)-[r:BORN_IN]->(sy) RETURN a.name,r,sy.name; -#MATCH (a:Person {name:'Passerby A'}), (sy:City {name:'Sanya'}) WITH a,sy CREATE (a)-[r:BORN_IN]->(sy); +MATCH (a:Person {name:'Passerby A'}), (sy:City {name:'Sanya'}) CREATE (a)-[r:BORN_IN]->(sy) RETURN a.name,r,sy.name; +MATCH (a:Person {name:'Passerby A'}), (sy:City {name:'Sanya'}) WITH a,sy CREATE (a)-[r:BORN_IN]->(sy); CREATE (passerC:Person {name:'Passerby C'}); MATCH (p:Person {name:'Passerby C'}) RETURN exists(p.birthyear) /* false */; WITH 'Passerby D' AS x, 2020 AS y CREATE (:Person {name:x, birthyear:y}); diff --git a/test/resource/unit_test/aggregate/cypher/aggregate.result b/test/resource/unit_test/aggregate/cypher/aggregate.result index e404d33485..944c2d4b2e 100644 --- a/test/resource/unit_test/aggregate/cypher/aggregate.result +++ b/test/resource/unit_test/aggregate/cypher/aggregate.result @@ -9,15 +9,15 @@ MATCH (n:Person) RETURN avg(n.age) /* 30.0 */; MATCH (n { name: 'A' })-->(x) RETURN count(x) /* 3 */; [{"count(x)":3}] MATCH (n:Person) RETURN count(n.age) /* 3 */; -[{"count(n.age)":5}] +[{"count(n.age)":3}] MATCH (n:Person) RETURN max(n.age) /* 44 */; [{"max(n.age)":44.0}] MATCH (n:Person) RETURN min(n.age) /* 13 */; [{"min(n.age)":13.0}] MATCH (n:Person) RETURN percentileCont(n.age, 0.4) /* 29 */; -[{"percentileCont(n.age,0.400000)":29.0}] +[{"percentileCont(n.age, 0.4)":29.0}] MATCH (n:Person) RETURN percentileDisc(n.age, 0.5) /* 33 */; -[{"percentileDisc(n.age,0.500000)":33.0}] +[{"percentileDisc(n.age, 0.5)":33.0}] MATCH (n:Person) RETURN stDev(n.age) /* 15.716234 */; [{"stDev(n.age)":15.716233645501712}] MATCH (n:Person) RETURN stDevP(n.age) /* 12.832251 */; @@ -29,11 +29,11 @@ MATCH (n:Person) RETURN varianceP(n.age); MATCH (n:Person) RETURN collect(n.age) /* 13,33,44 */; [{"collect(n.age)":[13,33,44]}] MATCH (n:Person) RETURN collect([n.name,n.age]) /* [[A, 13], [B, 33], [C, 44], [D, null], [E, null]] */; -[{"collect(expr::prop = {n.name},expr::prop = {n.age})":"[[A,13],[B,33],[C,44],[D,NUL],[E,NUL]]"}] +[{"collect([n.name,n.age])":"[[A,13],[B,33],[C,44],[D,NUL],[E,NUL]]"}] MATCH (n {name: 'A'})-[]->(x) RETURN label(n), n.age, count(*) /* Person,13,3.000000 */; [{"count(*)":3,"label(n)":"Person","n.age":13}] MATCH (n {name: 'A'})-[]->(x) RETURN label(n), n, count(*) /* Person,V[0],3.000000 */; -[{"count(*)":3,"label(n)":"Person","n":{"identity":0}}] +[{"count(*)":3,"label(n)":"Person","n":{"identity":0,"label":"Person","properties":{"age":13,"eyes":null,"name":"A"}}}] MATCH (n {name: 'A'})-[r]->() RETURN type(r), count(*) /* KNOWS,3.00000 */; [{"count(*)":3,"type(r)":"KNOWS"}] MATCH (n:Person) WHERE n.age = 13 OR n.age > 40 RETURN count(n) AS nCount; diff --git a/test/resource/unit_test/create_yago/cypher/create_yago.result b/test/resource/unit_test/create_yago/cypher/create_yago.result index c174abff31..a056eb8c9a 100644 --- a/test/resource/unit_test/create_yago/cypher/create_yago.result +++ b/test/resource/unit_test/create_yago/cypher/create_yago.result @@ -18,4 +18,3 @@ CALL db.createEdgeLabel('ACTED_IN', '[]', 'charactername', 'STRING', false); [] CREATE (rachel:Person:Actor {name: 'Rachel Kempson', birthyear: 1910}) CREATE (michael:Person:Actor {name: 'Michael Redgrave', birthyear: 1908}) CREATE (vanessa:Person:Actor {name: 'Vanessa Redgrave', birthyear: 1937}) CREATE (corin:Person:Actor {name: 'Corin Redgrave', birthyear: 1939}) CREATE (liam:Person:Actor {name: 'Liam Neeson', birthyear: 1952}) CREATE (natasha:Person:Actor {name: 'Natasha Richardson', birthyear: 1963}) CREATE (richard:Person:Actor {name: 'Richard Harris', birthyear: 1930}) CREATE (dennis:Person:Actor {name: 'Dennis Quaid', birthyear: 1954}) CREATE (lindsay:Person:Actor {name: 'Lindsay Lohan', birthyear: 1986}) CREATE (jemma:Person:Actor {name: 'Jemma Redgrave', birthyear: 1965}) CREATE (roy:Person:Actor {name: 'Roy Redgrave', birthyear: 1873}) CREATE (john:Person {name: 'John Williams', birthyear: 1932}) CREATE (christopher:Person {name: 'Christopher Nolan', birthyear: 1970}) CREATE (newyork:City {name: 'New York'}) CREATE (london:City {name: 'London'}) CREATE (houston:City {name: 'Houston'}) CREATE (mrchips:Film {title: 'Goodbye, Mr. Chips'}) CREATE (batmanbegins:Film {title: 'Batman Begins'}) CREATE (harrypotter:Film {title: 'Harry Potter and the Sorcerer\'s Stone'}) CREATE (parent:Film {title: 'The Parent Trap'}) CREATE (camelot:Film {title: 'Camelot'}) CREATE (rachel)-[:HAS_CHILD]->(vanessa), (rachel)-[:HAS_CHILD]->(corin), (michael)-[:HAS_CHILD]->(vanessa), (michael)-[:HAS_CHILD]->(corin), (corin)-[:HAS_CHILD]->(jemma), (vanessa)-[:HAS_CHILD]->(natasha), (roy)-[:HAS_CHILD]->(michael), (rachel)-[:MARRIED]->(michael), (michael)-[:MARRIED]->(rachel), (natasha)-[:MARRIED]->(liam), (liam)-[:MARRIED]->(natasha), (vanessa)-[:BORN_IN]->(london), (natasha)-[:BORN_IN]->(london), (christopher)-[:BORN_IN]->(london), (dennis)-[:BORN_IN]->(houston), (lindsay)-[:BORN_IN]->(newyork), (john)-[:BORN_IN]->(newyork), (christopher)-[:DIRECTED]->(batmanbegins), (john)-[:WROTE_MUSIC_FOR]->(harrypotter), (john)-[:WROTE_MUSIC_FOR]->(mrchips), (michael)-[:ACTED_IN {charactername: 'The Headmaster'}]->(mrchips), (vanessa)-[:ACTED_IN {charactername: 'Guenevere'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'King Arthur'}]->(camelot), (richard)-[:ACTED_IN {charactername: 'Albus Dumbledore'}]->(harrypotter), (natasha)-[:ACTED_IN {charactername: 'Liz James'}]->(parent), (dennis)-[:ACTED_IN {charactername: 'Nick Parker'}]->(parent),(lindsay)-[:ACTED_IN {charactername: 'Halle/Annie'}]->(parent), (liam)-[:ACTED_IN {charactername: 'Henri Ducard'}]->(batmanbegins) ; [{"":"created 21 vertices, created 28 edges."}] - diff --git a/test/resource/unit_test/delete/cypher/delete.result b/test/resource/unit_test/delete/cypher/delete.result index 456d251578..3f7f0e8290 100644 --- a/test/resource/unit_test/delete/cypher/delete.result +++ b/test/resource/unit_test/delete/cypher/delete.result @@ -12,11 +12,12 @@ MATCH (n {name:'B'})-[r:KNOWS]->() DELETE r; [{"":"deleted 0 vertices, deleted 2 edges."}] MATCH (n:Person {name:'F'})-[r:KNOWS]->(m:Person {name:'I'}) DELETE r; [{"":"deleted 0 vertices, deleted 1 edges."}] -#MATCH (n:Person {name:'A'}),(m:Person {name:'C'}) WITH n,m MATCH (n)-[r]->(m) DELETE r; +MATCH (n:Person {name:'A'}),(m:Person {name:'C'}) WITH n,m MATCH (n)-[r]->(m) DELETE r; +[{"":"deleted 0 vertices, deleted 1 edges."}] match (n)-[r]->(m) return r,properties(r) /*debug*/; -[{"properties(r)":{"_EID_":"0_1_0_0_0","_LABEL_":"KNOWS","weight":10},"r":{"dst":1,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":10},"src":0,"temporal_id":0}},{"properties(r)":{"_EID_":"0_2_0_0_0","_LABEL_":"KNOWS","weight":15},"r":{"dst":2,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":15},"src":0,"temporal_id":0}},{"properties(r)":{"_EID_":"2_4_0_0_0","_LABEL_":"KNOWS","weight":12},"r":{"dst":4,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":12},"src":2,"temporal_id":0}},{"properties(r)":{"_EID_":"5_6_0_0_0","_LABEL_":"KNOWS","weight":0},"r":{"dst":6,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":0},"src":5,"temporal_id":0}},{"properties(r)":{"_EID_":"5_7_0_0_0","_LABEL_":"KNOWS","weight":0},"r":{"dst":7,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":0},"src":5,"temporal_id":0}}] +[{"properties(r)":{"_EID_":"0_1_0_0_0","_LABEL_":"KNOWS","weight":10},"r":{"dst":1,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":10},"src":0,"temporal_id":0}},{"properties(r)":{"_EID_":"2_4_0_0_0","_LABEL_":"KNOWS","weight":12},"r":{"dst":4,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":12},"src":2,"temporal_id":0}},{"properties(r)":{"_EID_":"5_6_0_0_0","_LABEL_":"KNOWS","weight":0},"r":{"dst":6,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":0},"src":5,"temporal_id":0}},{"properties(r)":{"_EID_":"5_7_0_0_0","_LABEL_":"KNOWS","weight":0},"r":{"dst":7,"forward":false,"identity":0,"label":"KNOWS","label_id":0,"properties":{"weight":0},"src":5,"temporal_id":0}}] MATCH (n:Person {name:'A'}) DELETE n; -[{"":"deleted 1 vertices, deleted 2 edges."}] +[{"":"deleted 1 vertices, deleted 1 edges."}] MATCH (n:Person {name:'B'}) WITH n DELETE n; [{"":"deleted 1 vertices, deleted 0 edges."}] match (n) return n,properties(n) /*debug*/; diff --git a/test/resource/unit_test/delete/cypher/delete.test b/test/resource/unit_test/delete/cypher/delete.test index 9cc177e99c..61396cc228 100644 --- a/test/resource/unit_test/delete/cypher/delete.test +++ b/test/resource/unit_test/delete/cypher/delete.test @@ -5,7 +5,7 @@ match (n)-[r]->(m) return r,properties(r) /*debug*/; MATCH (n {name:'D'}) DELETE n; MATCH (n {name:'B'})-[r:KNOWS]->() DELETE r; MATCH (n:Person {name:'F'})-[r:KNOWS]->(m:Person {name:'I'}) DELETE r; -#MATCH (n:Person {name:'A'}),(m:Person {name:'C'}) WITH n,m MATCH (n)-[r]->(m) DELETE r; +MATCH (n:Person {name:'A'}),(m:Person {name:'C'}) WITH n,m MATCH (n)-[r]->(m) DELETE r; match (n)-[r]->(m) return r,properties(r) /*debug*/; MATCH (n:Person {name:'A'}) DELETE n; MATCH (n:Person {name:'B'}) WITH n DELETE n; diff --git a/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.result b/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.result index 5f6c58cee1..2a2691c0ab 100644 --- a/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.result +++ b/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.result @@ -8,28 +8,26 @@ MATCH (n:Person {name:'Liam Neeson'}),(m:Person {name:'Richard Harris'}) SET n.b [{"m.birthyear":1930,"n.birthyear":2152}] MATCH (n:Person {name:'Liam Neeson'}),(m:Person {name:'Richard Harris'}) SET n.birthyear=2252,m.birthyear=2230 RETURN n.birthyear,m.birthyear /*2252,2230*/; [{"m.birthyear":2230,"n.birthyear":2252}] -MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[:HAS_CHILD]->(z4); -[{"":"created 2 vertices, created 1 edges."}] +#MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[:HAS_CHILD]->(z4); MATCH (z3:Person {name:'zhang3'})-[r]->(z4:Person {name:'zhang4'}) RETURN r; -[{"r":{"dst":22,"forward":false,"identity":0,"label":"HAS_CHILD","label_id":0,"src":21,"temporal_id":0}}] -MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[r:HAS_CHILD]->(z4) RETURN z3,z4,r; -[{"r":{"dst":22,"forward":false,"identity":1,"label":"HAS_CHILD","label_id":0,"src":21,"temporal_id":0},"z3":{"identity":21,"label":"Person","properties":{"birthyear":2022,"name":"zhang3"}},"z4":{"identity":22,"label":"Person","properties":{"birthyear":2022,"name":"zhang4"}}}] +[] +#MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[r:HAS_CHILD]->(z4) RETURN z3,z4,r; MATCH (z3:Person {name:'zhang3'})-[r]->(z4:Person {name:'zhang4'}) RETURN r; -[{"r":{"dst":22,"forward":false,"identity":0,"label":"HAS_CHILD","label_id":0,"src":21,"temporal_id":0}},{"r":{"dst":22,"forward":false,"identity":1,"label":"HAS_CHILD","label_id":0,"src":21,"temporal_id":0}}] +[] MATCH (m:City) RETURN collect(m.name) + [1,2]; [{"collect(m.name) + [1,2]":"[Houston,London,New York,1,2]"}] WITH [1,2] AS nn MATCH (m:City) RETURN collect(m.name) + nn; -[{"collect(expr::prop = {m.name}),nn,\"+\"":"[New York,London,Houston,1,2]"}] +[{"collect(m.name) + nn":"[New York,London,Houston,1,2]"}] MATCH (n:City) WITH collect(n.name) AS nn MATCH (m:City) RETURN collect(m.name) + nn; -[{"collect(expr::prop = {m.name}),nn,\"+\"":"[New York,London,Houston,Houston,London,New York]"}] +[{"collect(m.name) + nn":"[New York,London,Houston,Houston,London,New York]"}] MATCH (n:Person) RETURN -n.birthyear LIMIT 3; -[{"0,expr::prop = {n.birthyear},\"-\"":-1970},{"0,expr::prop = {n.birthyear},\"-\"":-1939},{"0,expr::prop = {n.birthyear},\"-\"":-1954}] +[{"-n.birthyear":-1970},{"-n.birthyear":-1939},{"-n.birthyear":-1954}] MATCH (n:Person) RETURN -sum(n.birthyear) /*-27241*/; -[{"0,sum(expr::prop = {n.birthyear}),\"-\"":-29863.0}] +[{"-sum(n.birthyear)":-25819.0}] MATCH (n) -[r:HAS_CHILD * 2 ]->(m) RETURN n,m ; [{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}}] MATCH (n) -[r:HAS_CHILD * .. ]->(m) RETURN n,m ; -[{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}}},{"m":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":22,"label":"Person","properties":{"birthyear":2022,"name":"zhang4"}},"n":{"identity":21,"label":"Person","properties":{"birthyear":2022,"name":"zhang3"}}},{"m":{"identity":22,"label":"Person","properties":{"birthyear":2022,"name":"zhang4"}},"n":{"identity":21,"label":"Person","properties":{"birthyear":2022,"name":"zhang3"}}}] +[{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}}},{"m":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"m":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}},"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}}] WITH '1' as s UNWIND ['a','b'] as k RETURN s,k; [{"k":"a","s":"1"},{"k":"b","s":"1"}] WITH '1' as s UNWIND ['a','b']+s as k RETURN s,k; @@ -60,14 +58,12 @@ RETURN 2 AS b UNION RETURN 1 AS a; [CypherException] CypherException: All sub queries in an UNION must have the same column names. RETURN 2 AS b UNION RETURN 1 AS a, 3 AS c; [CypherException] CypherException: All sub queries in an UNION must have the same column names. -DELETE []; -[InputError] Type mismatch: expected Node, Path or Relationship +#DELETE []; DELETE [x in [1, 2, 3] | x]; [InputError] Type mismatch: expected Node, Path or Relationship -DELETE TRUE; -[InputError] Type mismatch: expected Node, Path or Relationship +#DELETE TRUE; MERGE (n:null {id: 2909}) RETURN n; -[CypherException] CypherException: cannot match node with given label and properties +[CypherException] No such vertex label: null MATCH (n:Person {name:'Liam Neeson'}), (m:Person {name:'Liam Neeson'}), (o:Person {name:'Liam Neeson'}) WHERE custom.myadd('asd')='1' RETURN 1; [InputError] Plugin [_fma_myadd] does not exist. MATCH (movie)<-[r]-(n) WITH n,n MATCH (n1) RETURN n1 LIMIT 1; diff --git a/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.test b/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.test index 6f3e264d8b..acbb2180c5 100644 --- a/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.test +++ b/test/resource/unit_test/fix_crash_issues/cypher/fix_crash_issues.test @@ -3,9 +3,9 @@ MATCH (n:Person {name:'Liam Neeson'})-[e]->(m) WITH e AS aa RETURN aa; MATCH (n:Person {name:'Liam Neeson'}) SET n.birthyear=2052 RETURN n.birthyear; MATCH (n:Person {name:'Liam Neeson'}),(m:Person {name:'Richard Harris'}) SET n.birthyear=2152 RETURN n.birthyear,m.birthyear /*2152,1930*/; MATCH (n:Person {name:'Liam Neeson'}),(m:Person {name:'Richard Harris'}) SET n.birthyear=2252,m.birthyear=2230 RETURN n.birthyear,m.birthyear /*2252,2230*/; -MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[:HAS_CHILD]->(z4); +#MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[:HAS_CHILD]->(z4); MATCH (z3:Person {name:'zhang3'})-[r]->(z4:Person {name:'zhang4'}) RETURN r; -MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[r:HAS_CHILD]->(z4) RETURN z3,z4,r; +#MERGE (z3:Person {name:'zhang3'}) ON CREATE SET z3.birthyear=2021 ON MATCH SET z3.birthyear=2022 WITH z3 MERGE (z4:Person {name:'zhang4'}) ON CREATE SET z4.birthyear=2021 ON MATCH SET z4.birthyear=2022 WITH z3,z4 CREATE (z3)-[r:HAS_CHILD]->(z4) RETURN z3,z4,r; MATCH (z3:Person {name:'zhang3'})-[r]->(z4:Person {name:'zhang4'}) RETURN r; MATCH (m:City) RETURN collect(m.name) + [1,2]; WITH [1,2] AS nn MATCH (m:City) RETURN collect(m.name) + nn; @@ -29,9 +29,9 @@ RETURN * UNION RETURN *; RETURN * UNION RETURN 1 AS a; RETURN 2 AS b UNION RETURN 1 AS a; RETURN 2 AS b UNION RETURN 1 AS a, 3 AS c; -DELETE []; +#DELETE []; DELETE [x in [1, 2, 3] | x]; -DELETE TRUE; +#DELETE TRUE; MERGE (n:null {id: 2909}) RETURN n; MATCH (n:Person {name:'Liam Neeson'}), (m:Person {name:'Liam Neeson'}), (o:Person {name:'Liam Neeson'}) WHERE custom.myadd('asd')='1' RETURN 1; MATCH (movie)<-[r]-(n) WITH n,n MATCH (n1) RETURN n1 LIMIT 1; diff --git a/test/resource/unit_test/func_filter/cypher/func_filter.result b/test/resource/unit_test/func_filter/cypher/func_filter.result index 29979b9922..5c6cf87dc0 100644 --- a/test/resource/unit_test/func_filter/cypher/func_filter.result +++ b/test/resource/unit_test/func_filter/cypher/func_filter.result @@ -4,6 +4,7 @@ MATCH (n) WHERE id(n) <> 6 RETURN n; [{"n":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"n":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}},{"n":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}},{"n":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}}},{"n":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}},{"n":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}}},{"n":{"identity":7,"label":"Person","properties":{"birthyear":1954,"name":"Dennis Quaid"}}},{"n":{"identity":8,"label":"Person","properties":{"birthyear":1986,"name":"Lindsay Lohan"}}},{"n":{"identity":9,"label":"Person","properties":{"birthyear":1965,"name":"Jemma Redgrave"}}},{"n":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}}},{"n":{"identity":11,"label":"Person","properties":{"birthyear":1932,"name":"John Williams"}}},{"n":{"identity":12,"label":"Person","properties":{"birthyear":1970,"name":"Christopher Nolan"}}},{"n":{"identity":13,"label":"City","properties":{"name":"New York"}}},{"n":{"identity":14,"label":"City","properties":{"name":"London"}}},{"n":{"identity":15,"label":"City","properties":{"name":"Houston"}}},{"n":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}}},{"n":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}}},{"n":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}}},{"n":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}}},{"n":{"identity":20,"label":"Film","properties":{"title":"Camelot"}}}] MATCH ()-[r]->() WHERE type(r) = 'ACTED_IN' RETURN r,type(r); [{"r":{"dst":16,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"The Headmaster"},"src":1,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Guenevere"},"src":2,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":17,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Henri Ducard"},"src":4,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":19,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Liz James"},"src":5,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":18,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Albus Dumbledore"},"src":6,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"King Arthur"},"src":6,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":19,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Nick Parker"},"src":7,"temporal_id":0},"type(r)":"ACTED_IN"},{"r":{"dst":19,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Halle/Annie"},"src":8,"temporal_id":0},"type(r)":"ACTED_IN"}] -#MATCH ()-[r]->() WHERE type(4) = 'ACTED_IN' RETURN r,type(r) /* invalid argument */; +MATCH ()-[r]->() WHERE type(4) = 'ACTED_IN' RETURN r,type(r) /* invalid argument */; +[CypherException] CypherException: Invalid argument of Type MATCH (a)-->(b)-->(c)<--(d) WHERE id(b) <> id(d) AND id(a) > id(d) AND id(b) < id(c) RETURN a,b,c,d; [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"b":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"c":{"identity":14,"label":"City","properties":{"name":"London"}},"d":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}},{"a":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"b":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"c":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"d":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}},{"a":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}},"b":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"c":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"d":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}},{"a":{"identity":10,"label":"Person","properties":{"birthyear":1873,"name":"Roy Redgrave"}},"b":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"c":{"identity":3,"label":"Person","properties":{"birthyear":1939,"name":"Corin Redgrave"}},"d":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}}}] diff --git a/test/resource/unit_test/func_filter/cypher/func_filter.test b/test/resource/unit_test/func_filter/cypher/func_filter.test index 6c75d196dd..86c80aff68 100644 --- a/test/resource/unit_test/func_filter/cypher/func_filter.test +++ b/test/resource/unit_test/func_filter/cypher/func_filter.test @@ -1,5 +1,5 @@ MATCH (n) WHERE id(n) = 6 RETURN n.name; MATCH (n) WHERE id(n) <> 6 RETURN n; MATCH ()-[r]->() WHERE type(r) = 'ACTED_IN' RETURN r,type(r); -#MATCH ()-[r]->() WHERE type(4) = 'ACTED_IN' RETURN r,type(r) /* invalid argument */; +MATCH ()-[r]->() WHERE type(4) = 'ACTED_IN' RETURN r,type(r) /* invalid argument */; MATCH (a)-->(b)-->(c)<--(d) WHERE id(b) <> id(d) AND id(a) > id(d) AND id(b) < id(c) RETURN a,b,c,d; \ No newline at end of file diff --git a/test/resource/unit_test/merge/cypher/merge.result b/test/resource/unit_test/merge/cypher/merge.result index 3a8fc589de..b3319bb83e 100644 --- a/test/resource/unit_test/merge/cypher/merge.result +++ b/test/resource/unit_test/merge/cypher/merge.result @@ -24,54 +24,54 @@ CREATE (n:City {name: 'Shanghai', area:6340.5, population:2423.78}) ; [{"":"created 1 vertices, created 0 edges."}] MERGE (n:Person {name:'Liubei'}) RETURN n.birthyear, n.gender ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":161,"n.gender":1}] MERGE (n:Person {name:'Zhugeliang'}) ON CREATE SET n.gender=1,n.birthyear=181 RETURN n.name ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.name":"Zhugeliang"}] MERGE (n:Person {name:'Liubei'}) ON MATCH SET n.birthyear=2010 RETURN n.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":2010}] MERGE(n:Person {name:'Liubei'}) ON CREATE SET n.gender=1 ON MATCH SET n.birthyear=2020 RETURN n.name, n.gender,n.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":2020,"n.gender":1,"n.name":"Liubei"}] MERGE(n:Person {name:'Huatuo'}) ON CREATE SET n.gender=1 ON MATCH SET n.birthyear=2020 RETURN n.name, n.gender,n.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":null,"n.gender":1,"n.name":"Huatuo"}] MERGE(n:Person {name:'Liubei'}) ON MATCH SET n.gender=0,n.birthyear=2050 RETURN n.name, n.gender,n.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":2050,"n.gender":0,"n.name":"Liubei"}] MATCH(n:Person {name:'Caocao'}), (m:Person {name:'Sunquan'}) MERGE (n)-[r:Knows{intimacy:0.6}]->(m) RETURN r.intimacy ; -[] +[{"r.intimacy":0.6}] MATCH(n:Person {name:'Caocao'}), (m:Person {name:'Sunquan'}) MERGE (n)-[r:Knows]->(m) RETURN r.intimacy ; -[] +[{"r.intimacy":0.6}] MATCH (n:Person),(m:City) WHERE n.name='Caocao' AND m.name='Beijing' MERGE (n)-[r:Livein]->(m) RETURN r ; -[InputError] Vertex label "Person" does not exist. +[{"r":{"dst":5,"forward":false,"identity":0,"label":"Livein","label_id":1,"src":1,"temporal_id":0}}] MATCH (n:Person {name:'Caocao'}) MERGE (n)-[r:Knows]->(m:Person {name:'Sunquan'})RETURN r ; -[] +[{"r":{"dst":2,"forward":false,"identity":0,"label":"Knows","label_id":0,"properties":{"intimacy":0.6},"src":1,"temporal_id":0}}] MATCH (n:Person),(m:City) WHERE n.birthyear >= 160 AND m.name = 'Beijing' MERGE (n)-[r:Livein]->(m) RETURN r ; -[InputError] Vertex label "Person" does not exist. +[{"r":{"dst":5,"forward":false,"identity":0,"label":"Livein","label_id":1,"src":3,"temporal_id":0}},{"r":{"dst":5,"forward":false,"identity":0,"label":"Livein","label_id":1,"src":0,"temporal_id":0}},{"r":{"dst":5,"forward":false,"identity":0,"label":"Livein","label_id":1,"src":2,"temporal_id":0}},{"r":{"dst":5,"forward":false,"identity":0,"label":"Livein","label_id":1,"src":7,"temporal_id":0}}] MERGE (n:Person {name:'Caocao'})-[r:Knows]->(m:Person {name:'Caogai'})RETURN r ; -[CypherException] CypherException: cannot match node with given label and properties +[{"r":{"dst":9,"forward":false,"identity":0,"label":"Knows","label_id":0,"properties":{"intimacy":null},"src":1,"temporal_id":0}}] MERGE (n:Person {name:'Huatuo'}) RETURN n.name ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.name":"Huatuo"}] MERGE (n:Person {name:'Xunyu'}) RETURN n.name ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.name":"Xunyu"}] MERGE (n:Person {name:'Liubei'}) RETURN n.birthyear, n.gender ; -[CypherException] CypherException: cannot match node with given label and properties +[{"n.birthyear":2050,"n.gender":0}] MERGE (node1: Person {name: 'lisi'}) ON CREATE SET node1.birthyear = 1903 WITH node1 MATCH (node1) WHERE node1.birthyear < 1904 SET node1.birthyear = 1904 RETURN id(node1), node1.name, node1.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"id(node1)":11,"node1.birthyear":1904,"node1.name":"lisi"}] MERGE (n: Person {name: 'wangwu'}) ON CREATE SET n.birthyear = 1903 ON CREATE SET n.name = 'wangwu2' WITH n MATCH (n) WHERE n.birthyear < 2002 SET n += {birthyear: 2002, name: 'wangwu2'} RETURN id(n), n.name, n.birthyear ; -[CypherException] CypherException: cannot match node with given label and properties +[{"id(n)":12,"n.birthyear":2002,"n.name":"wangwu2"}] MERGE (a:Person {name: 'zhangsan'}) SET a.birthyear = 2020 RETURN a.birthyear; -[CypherException] CypherException: cannot match node with given label and properties +[{"a.birthyear":2020}] MERGE (a:Person {name: 'zhangsan'}) DELETE a; -[CypherException] CypherException: cannot match node with given label and properties +[{"":"deleted 1 vertices, deleted 0 edges."}] MERGE (a:Person {name: 'zhangsan'}) CREATE (b:Person {name : 'xiaoming'})RETURN b; -[CypherException] CypherException: cannot match node with given label and properties +[{"b":{"identity":15,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"xiaoming"}}}] MERGE (n:Person {name:'zhangsan'}) MERGE (m:Person {name:'lisi'}) RETURN n,m; -[CypherException] CypherException: cannot match node with given label and properties +[{"m":{"identity":11,"label":"Person","properties":{"birthyear":1904,"gender":null,"name":"lisi"}},"n":{"identity":14,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"zhangsan"}}}] MERGE (n:Person {name:'zhangsan'}) MERGE (m:Person {name:'lisi'}) RETURN n,m; -[CypherException] CypherException: cannot match node with given label and properties +[{"m":{"identity":11,"label":"Person","properties":{"birthyear":1904,"gender":null,"name":"lisi"}},"n":{"identity":14,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"zhangsan"}}}] MERGE (n:Person {name:'zhangsan'}) MERGE (m:Person {name:'lisi'}) CREATE (n)-[r:Knows]->(m) RETURN n, r, m; -[CypherException] CypherException: cannot match node with given label and properties +[{"m":{"identity":11,"label":"Person","properties":{"birthyear":1904,"gender":null,"name":"lisi"}},"n":{"identity":14,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"zhangsan"}},"r":{"dst":11,"forward":false,"identity":0,"label":"Knows","label_id":0,"properties":{"intimacy":null},"src":14,"temporal_id":0}}] MERGE (n:Person {name:'zhangsan'}) MERGE (m:Person {name:'lisi'}) MERGE (n)-[r:Knows]->(m) return n, r, m; -[CypherException] CypherException: cannot match node with given label and properties +[{"m":{"identity":11,"label":"Person","properties":{"birthyear":1904,"gender":null,"name":"lisi"}},"n":{"identity":14,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"zhangsan"}},"r":{"dst":11,"forward":false,"identity":0,"label":"Knows","label_id":0,"properties":{"intimacy":null},"src":14,"temporal_id":0}}] MATCH (a:Person {name:'zhangsan'}) SET a.birthyear = 2023 CREATE (b:Person {name:'wangwu'}) RETURN b; -[] +[{"b":{"identity":16,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"wangwu"}}}] MATCH (a:Person {name:'zhangsan'}) SET a.birthyear = 2023 MERGE (b:Person {name:'wangwu'}) RETURN b; -[] +[{"b":{"identity":16,"label":"Person","properties":{"birthyear":null,"gender":null,"name":"wangwu"}}}] diff --git a/test/resource/unit_test/optional_match/cypher/optional_match.result b/test/resource/unit_test/optional_match/cypher/optional_match.result index f47a59dd28..027ab3b78b 100644 --- a/test/resource/unit_test/optional_match/cypher/optional_match.result +++ b/test/resource/unit_test/optional_match/cypher/optional_match.result @@ -4,7 +4,7 @@ MATCH (n:Person {name:'NoOne'}) RETURN n; OPTIONAL MATCH (n:Person {name:'NoOne'}) RETURN n; [{"n":"__null__"}] OPTIONAL MATCH (n:City {name:'London'})-[r]->(m) RETURN n.name, r, m; -[{"m":"__null__","n.name":null,"r":{"dst":0,"forward":false,"identity":0,"label":"HAS_CHILD","label_id":0,"src":14,"temporal_id":0}}] +[{"m":"__null__","n.name":null,"r":"__null__"}] OPTIONAL MATCH (n:City {name:'London'})-[r]-(m) RETURN n.name, r, m; [{"m":{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}},"n.name":"London","r":{"dst":14,"forward":false,"identity":0,"label":"BORN_IN","label_id":2,"properties":{"reg_time":"2023-05-01 10:00:00","weight":20.21},"src":2,"temporal_id":0}},{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n.name":"London","r":{"dst":14,"forward":false,"identity":0,"label":"BORN_IN","label_id":2,"properties":{"reg_time":"2023-05-01 11:00:00","weight":20.18},"src":5,"temporal_id":0}},{"m":{"identity":12,"label":"Person","properties":{"birthyear":1970,"name":"Christopher Nolan"}},"n.name":"London","r":{"dst":14,"forward":false,"identity":0,"label":"BORN_IN","label_id":2,"properties":{"reg_time":"2023-05-01 12:00:00","weight":19.93},"src":12,"temporal_id":0}}] MATCH (n:City {name:'London'}) WITH n.name AS city_name OPTIONAL MATCH (n:Person {name:'NoOne'}) RETURN n.name, city_name; @@ -13,7 +13,9 @@ MATCH (n:City) WITH n MATCH (n)-->(m) RETURN n,m; [] MATCH (n:City) WITH n OPTIONAL MATCH (n)-->(m) RETURN n,m; [{"m":"__null__","n":{"identity":15,"label":"City","properties":{"name":"Houston"}}},{"m":"__null__","n":{"identity":14,"label":"City","properties":{"name":"London"}}},{"m":"__null__","n":{"identity":13,"label":"City","properties":{"name":"New York"}}}] - -# MATCH (n:City {name:'London'}) OPTIONAL MATCH (n)-[r]->(m) RETURN n.name, r, m ; -# MATCH (n:City {name:'London'}) OPTIONAL MATCH (m:NoLabel) RETURN n.name, m ; -# MATCH (n:Person {name:'Vanessa Redgrave'}) OPTIONAL MATCH (n)-[:ACTED_IN]->(m)-[:NoType]->(l) RETURN n, m, l; +MATCH (n:City {name:'London'}) OPTIONAL MATCH (n)-[r]->(m) RETURN n.name, r, m ; +[CypherException] CypherException: Function not implemented yet: AddClause at src/cypher/parser/clause.h:300 +MATCH (n:City {name:'London'}) OPTIONAL MATCH (m:NoLabel) RETURN n.name, m ; +[CypherException] CypherException: Function not implemented yet: AddClause at src/cypher/parser/clause.h:300 +MATCH (n:Person {name:'Vanessa Redgrave'}) OPTIONAL MATCH (n)-[:ACTED_IN]->(m)-[:NoType]->(l) RETURN n, m, l; +[CypherException] CypherException: Function not implemented yet: AddClause at src/cypher/parser/clause.h:300 diff --git a/test/resource/unit_test/optional_match/cypher/optional_match.test b/test/resource/unit_test/optional_match/cypher/optional_match.test index 579443673a..36853659dd 100644 --- a/test/resource/unit_test/optional_match/cypher/optional_match.test +++ b/test/resource/unit_test/optional_match/cypher/optional_match.test @@ -6,7 +6,6 @@ OPTIONAL MATCH (n:City {name:'London'})-[r]-(m) RETURN n.name, r, m; MATCH (n:City {name:'London'}) WITH n.name AS city_name OPTIONAL MATCH (n:Person {name:'NoOne'}) RETURN n.name, city_name; MATCH (n:City) WITH n MATCH (n)-->(m) RETURN n,m; MATCH (n:City) WITH n OPTIONAL MATCH (n)-->(m) RETURN n,m; - -# MATCH (n:City {name:'London'}) OPTIONAL MATCH (n)-[r]->(m) RETURN n.name, r, m ; -# MATCH (n:City {name:'London'}) OPTIONAL MATCH (m:NoLabel) RETURN n.name, m ; -# MATCH (n:Person {name:'Vanessa Redgrave'}) OPTIONAL MATCH (n)-[:ACTED_IN]->(m)-[:NoType]->(l) RETURN n, m, l; \ No newline at end of file +MATCH (n:City {name:'London'}) OPTIONAL MATCH (n)-[r]->(m) RETURN n.name, r, m ; +MATCH (n:City {name:'London'}) OPTIONAL MATCH (m:NoLabel) RETURN n.name, m ; +MATCH (n:Person {name:'Vanessa Redgrave'}) OPTIONAL MATCH (n)-[:ACTED_IN]->(m)-[:NoType]->(l) RETURN n, m, l; diff --git a/test/resource/unit_test/orderby/cypher/orderby.result b/test/resource/unit_test/orderby/cypher/orderby.result index 712476855c..e214ff9915 100644 --- a/test/resource/unit_test/orderby/cypher/orderby.result +++ b/test/resource/unit_test/orderby/cypher/orderby.result @@ -1,6 +1,7 @@ -#MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); +MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); +[{"":"created 0 vertices, created 1 edges."}] match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(v2) as cnt; -[{"cnt":3,"v1.title":"The Parent Trap"},{"cnt":1,"v1.title":"Harry Potter and the Sorcerer's Stone"},{"cnt":1,"v1.title":"Goodbye, Mr. Chips"},{"cnt":2,"v1.title":"Batman Begins"},{"cnt":2,"v1.title":"Camelot"}] +[{"cnt":4,"v1.title":"The Parent Trap"},{"cnt":1,"v1.title":"Harry Potter and the Sorcerer's Stone"},{"cnt":1,"v1.title":"Goodbye, Mr. Chips"},{"cnt":2,"v1.title":"Batman Begins"},{"cnt":2,"v1.title":"Camelot"}] match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt; [{"cnt":3,"v1.title":"The Parent Trap"},{"cnt":1,"v1.title":"Harry Potter and the Sorcerer's Stone"},{"cnt":1,"v1.title":"Goodbye, Mr. Chips"},{"cnt":2,"v1.title":"Batman Begins"},{"cnt":2,"v1.title":"Camelot"}] match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt order by cnt; @@ -14,7 +15,7 @@ match (v1:Film) return distinct v1.title order by v1.title; match (v1:Film) return distinct v1.title order by v1.title limit 3 /* Batman, Camelot, Goodbye */; [{"v1.title":"Batman Begins"},{"v1.title":"Camelot"},{"v1.title":"Goodbye, Mr. Chips"}] match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt order by cnt desc limit 3 /* NOTE: unstable heap sort */; -[{"cnt":3,"v1.title":"The Parent Trap"},{"cnt":2,"v1.title":"Camelot"},{"cnt":2,"v1.title":"Batman Begins"}] +[{"cnt":3,"v1.title":"The Parent Trap"},{"cnt":2,"v1.title":"Batman Begins"},{"cnt":2,"v1.title":"Camelot"}] match (:Person {name:'Vanessa Redgrave'})<-[:HAS_CHILD]-(p)-[:ACTED_IN*0..]->(m) return p.name,m order by p.name; [{"m":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"p.name":"Michael Redgrave"},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"p.name":"Michael Redgrave"},{"m":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},"p.name":"Rachel Kempson"}] MATCH (n) RETURN n,n.name AS name ORDER BY name; @@ -32,4 +33,4 @@ MATCH (n:Person {name:'Vanessa Redgrave'})-[*2]-(m:Person) RETURN DISTINCT m.nam MATCH (n:Person {name:'Vanessa Redgrave'})-[*2]-(m:Person) RETURN DISTINCT m.name ORDER BY m.name SKIP 2 LIMIT 3; [{"m.name":"Liam Neeson"},{"m.name":"Michael Redgrave"},{"m.name":"Natasha Richardson"}] MATCH (v1:Film)<-[r:ACTED_IN|DIRECTED]-(v2:Person) RETURN v1.title AS title, r ORDER BY title LIMIT 5; -[{"r":{"dst":17,"forward":false,"identity":0,"label":"DIRECTED","label_id":3,"src":12,"temporal_id":0},"title":"Batman Begins"},{"r":{"dst":17,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Henri Ducard"},"src":4,"temporal_id":0},"title":"Batman Begins"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"King Arthur"},"src":6,"temporal_id":0},"title":"Camelot"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Guenevere"},"src":2,"temporal_id":0},"title":"Camelot"},{"r":{"dst":16,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"The Headmaster"},"src":1,"temporal_id":0},"title":"Goodbye, Mr. Chips"}] +[{"r":{"dst":17,"forward":false,"identity":0,"label":"DIRECTED","label_id":3,"src":12,"temporal_id":0},"title":"Batman Begins"},{"r":{"dst":17,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Henri Ducard"},"src":4,"temporal_id":0},"title":"Batman Begins"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Guenevere"},"src":2,"temporal_id":0},"title":"Camelot"},{"r":{"dst":20,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"King Arthur"},"src":6,"temporal_id":0},"title":"Camelot"},{"r":{"dst":16,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"The Headmaster"},"src":1,"temporal_id":0},"title":"Goodbye, Mr. Chips"}] diff --git a/test/resource/unit_test/orderby/cypher/orderby.test b/test/resource/unit_test/orderby/cypher/orderby.test index b2a3ae6aab..d3aa3a404d 100644 --- a/test/resource/unit_test/orderby/cypher/orderby.test +++ b/test/resource/unit_test/orderby/cypher/orderby.test @@ -1,4 +1,4 @@ -#MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); +MATCH (a:Person {name:'Lindsay Lohan'}), (b:Film {title:'The Parent Trap'}) CREATE (a)-[r:DIRECTED]->(b); match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(v2) as cnt; match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt; match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt order by cnt; diff --git a/test/resource/unit_test/parameter/cypher/parameter.result b/test/resource/unit_test/parameter/cypher/parameter.result new file mode 100644 index 0000000000..e1174801fc --- /dev/null +++ b/test/resource/unit_test/parameter/cypher/parameter.result @@ -0,0 +1,4 @@ +MATCH (n:Person) WHERE n.name = $name RETURN n; +[{"n":{"identity":8,"label":"Person","properties":{"birthyear":1986,"name":"Lindsay Lohan"}}}] +MATCH (n:Person {name:$name}) RETURN n; +[{"n":{"identity":8,"label":"Person","properties":{"birthyear":1986,"name":"Lindsay Lohan"}}}] diff --git a/test/resource/unit_test/parameter/cypher/paratemer.test b/test/resource/unit_test/parameter/cypher/parameter.test similarity index 100% rename from test/resource/unit_test/parameter/cypher/paratemer.test rename to test/resource/unit_test/parameter/cypher/parameter.test diff --git a/test/resource/unit_test/procedure/cypher/procedure.result b/test/resource/unit_test/procedure/cypher/procedure.result index dc983e40ca..f3f560301e 100644 --- a/test/resource/unit_test/procedure/cypher/procedure.result +++ b/test/resource/unit_test/procedure/cypher/procedure.result @@ -5,10 +5,9 @@ -- loadProcedure peek_some_node_salt ../../test/test_procedures/peek_some_node_salt.cpp read_only=true -- loadProcedure custom_path_process ../../test/test_procedures/v2_path_process.cpp read_only=true -- loadProcedure custom_algo ../../test/test_procedures/v2_algo.cpp read_only=true - -CALL db.createVertexLabel('Director', 'name', 'name', STRING, false, 'age', INT16, true); +CALL db.createVertexLabel('Director', 'name', 'name', 'STRING', false, 'age', 'INT16', true); [] -CALL db.createVertexLabel('P2', 'flag1', 'flag1', BOOL, false, 'flag2', Bool, true); +CALL db.createVertexLabel('P2', 'flag1', 'flag1', 'BOOL', false, 'flag2', 'Bool', true); [] CALL db.createEdgeLabel('LIKE', '[]'); [] @@ -23,11 +22,11 @@ CALL db.edgeLabels; CALL db.indexes; [{"field":"birthyear","label":"Person","label_type":"vertex","pair_unique":false,"unique":false},{"field":"name","label":"Person","label_type":"vertex","pair_unique":false,"unique":true},{"field":"name","label":"City","label_type":"vertex","pair_unique":false,"unique":true},{"field":"title","label":"Film","label_type":"vertex","pair_unique":false,"unique":true},{"field":"name","label":"Director","label_type":"vertex","pair_unique":false,"unique":true},{"field":"flag1","label":"P2","label_type":"vertex","pair_unique":false,"unique":true}] CALL dbms.procedures; -[{"name":"db.subgraph","read_only":true,"signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"name":"db.vertexLabels","read_only":true,"signature":"db.vertexLabels() :: (label::STRING)"},{"name":"db.edgeLabels","read_only":true,"signature":"db.edgeLabels() :: (label::STRING)"},{"name":"db.indexes","read_only":true,"signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.listLabelIndexes","read_only":true,"signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.propertyKeys","read_only":true,"signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"name":"db.warmup","read_only":true,"signature":"db.warmup() :: (time_used::STRING)"},{"name":"db.createVertexLabelByJson","read_only":false,"signature":"db.createVertexLabelByJson(json_data::STRING) :: (::NUL)"},{"name":"db.createEdgeLabelByJson","read_only":false,"signature":"db.createEdgeLabelByJson(json_data::STRING) :: (::NUL)"},{"name":"db.createVertexLabel","read_only":false,"signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.createLabel","read_only":false,"signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"name":"db.getLabelSchema","read_only":true,"signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"name":"db.getVertexSchema","read_only":true,"signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"name":"db.getEdgeSchema","read_only":true,"signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"name":"db.deleteLabel","read_only":false,"signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"name":"db.alterLabelDelFields","read_only":false,"signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelAddFields","read_only":false,"signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"name":"db.upsertVertex","read_only":false,"signature":"db.upsertVertex(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertVertexByJson","read_only":false,"signature":"db.upsertVertexByJson(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertEdge","read_only":false,"signature":"db.upsertEdge(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertEdgeByJson","read_only":false,"signature":"db.upsertEdgeByJson(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.alterLabelModFields","read_only":false,"signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"name":"db.createEdgeLabel","read_only":false,"signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.addIndex","read_only":false,"signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"name":"db.addEdgeIndex","read_only":false,"signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"name":"db.addFullTextIndex","read_only":false,"signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteFullTextIndex","read_only":false,"signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.rebuildFullTextIndex","read_only":false,"signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"name":"db.fullTextIndexes","read_only":true,"signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"name":"db.addEdgeConstraints","read_only":false,"signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"name":"db.clearEdgeConstraints","read_only":false,"signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"name":"dbms.procedures","read_only":true,"signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"name":"dbms.meta.countDetail","read_only":true,"signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"name":"dbms.meta.count","read_only":true,"signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"name":"dbms.meta.refreshCount","read_only":false,"signature":"dbms.meta.refreshCount() :: (::NUL)"},{"name":"dbms.security.changePassword","read_only":false,"signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.changeUserPassword","read_only":false,"signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.createUser","read_only":false,"signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUser","read_only":false,"signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"name":"dbms.security.setUserMemoryLimit","read_only":false,"signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"name":"dbms.security.listUsers","read_only":true,"signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"name":"dbms.security.showCurrentUser","read_only":true,"signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"name":"dbms.security.listAllowedHosts","read_only":true,"signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"name":"dbms.security.deleteAllowedHosts","read_only":false,"signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"name":"dbms.security.addAllowedHosts","read_only":false,"signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"name":"dbms.graph.createGraph","read_only":false,"signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"name":"dbms.graph.deleteGraph","read_only":false,"signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"name":"dbms.graph.modGraph","read_only":false,"signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"name":"dbms.graph.listGraphs","read_only":true,"signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.listUserGraphs","read_only":true,"signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphInfo","read_only":true,"signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphSchema","read_only":true,"signature":"dbms.graph.getGraphSchema() :: (schema::STRING)"},{"name":"dbms.system.info","read_only":true,"signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"name":"dbms.config.list","read_only":true,"signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"name":"dbms.config.update","read_only":false,"signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"name":"dbms.takeSnapshot","read_only":false,"signature":"dbms.takeSnapshot() :: (path::STRING)"},{"name":"dbms.listBackupFiles","read_only":true,"signature":"dbms.listBackupFiles() :: (file::STRING)"},{"name":"algo.shortestPath","read_only":true,"signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"name":"algo.allShortestPaths","read_only":true,"signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"name":"algo.native.extract","read_only":true,"signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"name":"algo.pagerank","read_only":true,"signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"name":"algo.jaccard","read_only":true,"signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"name":"spatial.distance","read_only":true,"signature":"spatial.distance(Spatial1::STRING,Spatial2::STRING) :: (distance::DOUBLE)"},{"name":"dbms.security.listRoles","read_only":true,"signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"name":"dbms.security.createRole","read_only":false,"signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"name":"dbms.security.deleteRole","read_only":false,"signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"name":"dbms.security.getUserInfo","read_only":true,"signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getUserMemoryUsage","read_only":true,"signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"name":"dbms.security.getUserPermissions","read_only":true,"signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getRoleInfo","read_only":true,"signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"name":"dbms.security.disableRole","read_only":false,"signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.modRoleDesc","read_only":false,"signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.rebuildRoleAccessLevel","read_only":false,"signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleAccessLevel","read_only":false,"signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleFieldAccessLevel","read_only":false,"signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"name":"dbms.security.disableUser","read_only":false,"signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.setCurrentDesc","read_only":false,"signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"name":"dbms.security.setUserDesc","read_only":false,"signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUserRoles","read_only":false,"signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.rebuildUserRoles","read_only":false,"signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.addUserRoles","read_only":false,"signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"db.plugin.loadPlugin","read_only":false,"signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"name":"db.plugin.deletePlugin","read_only":false,"signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"name":"db.plugin.getPluginInfo","read_only":true,"signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listPlugin","read_only":false,"signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listUserPlugins","read_only":true,"signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"name":"db.plugin.callPlugin","read_only":false,"signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"name":"db.importor.dataImportor","read_only":false,"signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"name":"db.importor.fullImportor","read_only":false,"signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"name":"db.importor.fullFileImportor","read_only":false,"signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"name":"db.importor.schemaImportor","read_only":false,"signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"name":"db.deleteIndex","read_only":false,"signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteEdgeIndex","read_only":false,"signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.flushDB","read_only":false,"signature":"db.flushDB() :: (::NUL)"},{"name":"db.dropDB","read_only":false,"signature":"db.dropDB() :: (::NUL)"},{"name":"dbms.task.listTasks","read_only":true,"signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"name":"dbms.task.terminateTask","read_only":false,"signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"name":"db.monitor.tuGraphInfo","read_only":false,"signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"name":"db.monitor.serverInfo","read_only":false,"signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"name":"dbms.ha.clusterInfo","read_only":true,"signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] +[{"name":"db.subgraph","read_only":true,"signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"name":"db.vertexLabels","read_only":true,"signature":"db.vertexLabels() :: (label::STRING)"},{"name":"db.edgeLabels","read_only":true,"signature":"db.edgeLabels() :: (label::STRING)"},{"name":"db.indexes","read_only":true,"signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.listLabelIndexes","read_only":true,"signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.propertyKeys","read_only":true,"signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"name":"db.warmup","read_only":true,"signature":"db.warmup() :: (time_used::STRING)"},{"name":"db.createVertexLabel","read_only":false,"signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.createLabel","read_only":false,"signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"name":"db.getLabelSchema","read_only":true,"signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"name":"db.getVertexSchema","read_only":true,"signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"name":"db.getEdgeSchema","read_only":true,"signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"name":"db.deleteLabel","read_only":false,"signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"name":"db.alterLabelDelFields","read_only":false,"signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelAddFields","read_only":false,"signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelModFields","read_only":false,"signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"name":"db.createEdgeLabel","read_only":false,"signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.addIndex","read_only":false,"signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"name":"db.addEdgeIndex","read_only":false,"signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"name":"db.addFullTextIndex","read_only":false,"signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteFullTextIndex","read_only":false,"signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.rebuildFullTextIndex","read_only":false,"signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"name":"db.fullTextIndexes","read_only":true,"signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"name":"db.addEdgeConstraints","read_only":false,"signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"name":"db.clearEdgeConstraints","read_only":false,"signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"name":"dbms.procedures","read_only":true,"signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"name":"dbms.meta.countDetail","read_only":true,"signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"name":"dbms.meta.count","read_only":true,"signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"name":"dbms.meta.refreshCount","read_only":false,"signature":"dbms.meta.refreshCount() :: (::NUL)"},{"name":"dbms.security.changePassword","read_only":false,"signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.changeUserPassword","read_only":false,"signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.createUser","read_only":false,"signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUser","read_only":false,"signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"name":"dbms.security.setUserMemoryLimit","read_only":false,"signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"name":"dbms.security.listUsers","read_only":true,"signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"name":"dbms.security.showCurrentUser","read_only":true,"signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"name":"dbms.security.listAllowedHosts","read_only":true,"signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"name":"dbms.security.deleteAllowedHosts","read_only":false,"signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"name":"dbms.security.addAllowedHosts","read_only":false,"signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"name":"dbms.graph.createGraph","read_only":false,"signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"name":"dbms.graph.deleteGraph","read_only":false,"signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"name":"dbms.graph.modGraph","read_only":false,"signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"name":"dbms.graph.listGraphs","read_only":true,"signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.listUserGraphs","read_only":true,"signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphInfo","read_only":true,"signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.system.info","read_only":true,"signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"name":"dbms.config.list","read_only":true,"signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"name":"dbms.config.update","read_only":false,"signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"name":"dbms.takeSnapshot","read_only":false,"signature":"dbms.takeSnapshot() :: (path::STRING)"},{"name":"dbms.listBackupFiles","read_only":true,"signature":"dbms.listBackupFiles() :: (file::STRING)"},{"name":"algo.shortestPath","read_only":true,"signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"name":"algo.allShortestPaths","read_only":true,"signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"name":"algo.native.extract","read_only":true,"signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"name":"algo.pagerank","read_only":true,"signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"name":"algo.jaccard","read_only":true,"signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"name":"dbms.security.listRoles","read_only":true,"signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"name":"dbms.security.createRole","read_only":false,"signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"name":"dbms.security.deleteRole","read_only":false,"signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"name":"dbms.security.getUserInfo","read_only":true,"signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getUserMemoryUsage","read_only":true,"signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"name":"dbms.security.getUserPermissions","read_only":true,"signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getRoleInfo","read_only":true,"signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"name":"dbms.security.disableRole","read_only":false,"signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.modRoleDesc","read_only":false,"signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.rebuildRoleAccessLevel","read_only":false,"signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleAccessLevel","read_only":false,"signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleFieldAccessLevel","read_only":false,"signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"name":"dbms.security.disableUser","read_only":false,"signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.setCurrentDesc","read_only":false,"signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"name":"dbms.security.setUserDesc","read_only":false,"signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUserRoles","read_only":false,"signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.rebuildUserRoles","read_only":false,"signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.addUserRoles","read_only":false,"signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"db.plugin.loadPlugin","read_only":false,"signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"name":"db.plugin.deletePlugin","read_only":false,"signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"name":"db.plugin.getPluginInfo","read_only":true,"signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listPlugin","read_only":false,"signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listUserPlugins","read_only":true,"signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"name":"db.plugin.callPlugin","read_only":false,"signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"name":"db.importor.dataImportor","read_only":false,"signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"name":"db.importor.fullImportor","read_only":false,"signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"name":"db.importor.fullFileImportor","read_only":false,"signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"name":"db.importor.schemaImportor","read_only":false,"signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"name":"db.deleteIndex","read_only":false,"signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteEdgeIndex","read_only":false,"signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.flushDB","read_only":false,"signature":"db.flushDB() :: (::NUL)"},{"name":"db.dropDB","read_only":false,"signature":"db.dropDB() :: (::NUL)"},{"name":"dbms.task.listTasks","read_only":true,"signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"name":"dbms.task.terminateTask","read_only":false,"signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"name":"db.monitor.tuGraphInfo","read_only":false,"signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"name":"db.monitor.serverInfo","read_only":false,"signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"name":"dbms.ha.clusterInfo","read_only":true,"signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] CALL dbms.procedures YIELD signature; -[{"signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"signature":"db.vertexLabels() :: (label::STRING)"},{"signature":"db.edgeLabels() :: (label::STRING)"},{"signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"signature":"db.warmup() :: (time_used::STRING)"},{"signature":"db.createVertexLabelByJson(json_data::STRING) :: (::NUL)"},{"signature":"db.createEdgeLabelByJson(json_data::STRING) :: (::NUL)"},{"signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"signature":"db.upsertVertex(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"signature":"db.upsertVertexByJson(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"signature":"db.upsertEdge(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"signature":"db.upsertEdgeByJson(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"signature":"dbms.meta.refreshCount() :: (::NUL)"},{"signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.graph.getGraphSchema() :: (schema::STRING)"},{"signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"signature":"dbms.takeSnapshot() :: (path::STRING)"},{"signature":"dbms.listBackupFiles() :: (file::STRING)"},{"signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"signature":"spatial.distance(Spatial1::STRING,Spatial2::STRING) :: (distance::DOUBLE)"},{"signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.flushDB() :: (::NUL)"},{"signature":"db.dropDB() :: (::NUL)"},{"signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] +[{"signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"signature":"db.vertexLabels() :: (label::STRING)"},{"signature":"db.edgeLabels() :: (label::STRING)"},{"signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"signature":"db.warmup() :: (time_used::STRING)"},{"signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"signature":"dbms.meta.refreshCount() :: (::NUL)"},{"signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"signature":"dbms.takeSnapshot() :: (path::STRING)"},{"signature":"dbms.listBackupFiles() :: (file::STRING)"},{"signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"signature":"db.flushDB() :: (::NUL)"},{"signature":"db.dropDB() :: (::NUL)"},{"signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] CALL dbms.procedures YIELD signature, name; -[{"name":"db.subgraph","signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"name":"db.vertexLabels","signature":"db.vertexLabels() :: (label::STRING)"},{"name":"db.edgeLabels","signature":"db.edgeLabels() :: (label::STRING)"},{"name":"db.indexes","signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.listLabelIndexes","signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.propertyKeys","signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"name":"db.warmup","signature":"db.warmup() :: (time_used::STRING)"},{"name":"db.createVertexLabelByJson","signature":"db.createVertexLabelByJson(json_data::STRING) :: (::NUL)"},{"name":"db.createEdgeLabelByJson","signature":"db.createEdgeLabelByJson(json_data::STRING) :: (::NUL)"},{"name":"db.createVertexLabel","signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.createLabel","signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"name":"db.getLabelSchema","signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"name":"db.getVertexSchema","signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"name":"db.getEdgeSchema","signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"name":"db.deleteLabel","signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"name":"db.alterLabelDelFields","signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelAddFields","signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"name":"db.upsertVertex","signature":"db.upsertVertex(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertVertexByJson","signature":"db.upsertVertexByJson(label_name::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertEdge","signature":"db.upsertEdge(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.upsertEdgeByJson","signature":"db.upsertEdgeByJson(label_name::STRING,start_spec::STRING,end_spec::STRING,list_data::STRING) :: (total::INTEGER,data_error::INTEGER,index_conflict::INTEGER,insert::INTEGER,update::INTEGER)"},{"name":"db.alterLabelModFields","signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"name":"db.createEdgeLabel","signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.addIndex","signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"name":"db.addEdgeIndex","signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"name":"db.addFullTextIndex","signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteFullTextIndex","signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.rebuildFullTextIndex","signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"name":"db.fullTextIndexes","signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"name":"db.addEdgeConstraints","signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"name":"db.clearEdgeConstraints","signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"name":"dbms.procedures","signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"name":"dbms.meta.countDetail","signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"name":"dbms.meta.count","signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"name":"dbms.meta.refreshCount","signature":"dbms.meta.refreshCount() :: (::NUL)"},{"name":"dbms.security.changePassword","signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.changeUserPassword","signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.createUser","signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUser","signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"name":"dbms.security.setUserMemoryLimit","signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"name":"dbms.security.listUsers","signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"name":"dbms.security.showCurrentUser","signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"name":"dbms.security.listAllowedHosts","signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"name":"dbms.security.deleteAllowedHosts","signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"name":"dbms.security.addAllowedHosts","signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"name":"dbms.graph.createGraph","signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"name":"dbms.graph.deleteGraph","signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"name":"dbms.graph.modGraph","signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"name":"dbms.graph.listGraphs","signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.listUserGraphs","signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphInfo","signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphSchema","signature":"dbms.graph.getGraphSchema() :: (schema::STRING)"},{"name":"dbms.system.info","signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"name":"dbms.config.list","signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"name":"dbms.config.update","signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"name":"dbms.takeSnapshot","signature":"dbms.takeSnapshot() :: (path::STRING)"},{"name":"dbms.listBackupFiles","signature":"dbms.listBackupFiles() :: (file::STRING)"},{"name":"algo.shortestPath","signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"name":"algo.allShortestPaths","signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"name":"algo.native.extract","signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"name":"algo.pagerank","signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"name":"algo.jaccard","signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"name":"spatial.distance","signature":"spatial.distance(Spatial1::STRING,Spatial2::STRING) :: (distance::DOUBLE)"},{"name":"dbms.security.listRoles","signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"name":"dbms.security.createRole","signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"name":"dbms.security.deleteRole","signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"name":"dbms.security.getUserInfo","signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getUserMemoryUsage","signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"name":"dbms.security.getUserPermissions","signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getRoleInfo","signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"name":"dbms.security.disableRole","signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.modRoleDesc","signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.rebuildRoleAccessLevel","signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleAccessLevel","signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleFieldAccessLevel","signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"name":"dbms.security.disableUser","signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.setCurrentDesc","signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"name":"dbms.security.setUserDesc","signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUserRoles","signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.rebuildUserRoles","signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.addUserRoles","signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"db.plugin.loadPlugin","signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"name":"db.plugin.deletePlugin","signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"name":"db.plugin.getPluginInfo","signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listPlugin","signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listUserPlugins","signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"name":"db.plugin.callPlugin","signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"name":"db.importor.dataImportor","signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"name":"db.importor.fullImportor","signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"name":"db.importor.fullFileImportor","signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"name":"db.importor.schemaImportor","signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"name":"db.deleteIndex","signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteEdgeIndex","signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.flushDB","signature":"db.flushDB() :: (::NUL)"},{"name":"db.dropDB","signature":"db.dropDB() :: (::NUL)"},{"name":"dbms.task.listTasks","signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"name":"dbms.task.terminateTask","signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"name":"db.monitor.tuGraphInfo","signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"name":"db.monitor.serverInfo","signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"name":"dbms.ha.clusterInfo","signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] +[{"name":"db.subgraph","signature":"db.subgraph(vids::LIST) :: (subgraph::STRING)"},{"name":"db.vertexLabels","signature":"db.vertexLabels() :: (label::STRING)"},{"name":"db.edgeLabels","signature":"db.edgeLabels() :: (label::STRING)"},{"name":"db.indexes","signature":"db.indexes() :: (label::STRING,field::STRING,label_type::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.listLabelIndexes","signature":"db.listLabelIndexes(label_name::STRING,label_type::LIST) :: (label::STRING,field::STRING,unique::BOOLEAN,pair_unique::BOOLEAN)"},{"name":"db.propertyKeys","signature":"db.propertyKeys() :: (propertyKey::STRING)"},{"name":"db.warmup","signature":"db.warmup() :: (time_used::STRING)"},{"name":"db.createVertexLabel","signature":"db.createVertexLabel(label_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.createLabel","signature":"db.createLabel(label_type::STRING,label_name::STRING,extra::STRING,field_specs::LIST) :: ()"},{"name":"db.getLabelSchema","signature":"db.getLabelSchema(label_type::STRING,label_name::STRING) :: (name::STRING,type::STRING,optional::BOOLEAN)"},{"name":"db.getVertexSchema","signature":"db.getVertexSchema(label::STRING) :: (schema::MAP)"},{"name":"db.getEdgeSchema","signature":"db.getEdgeSchema(label::STRING) :: (schema::MAP)"},{"name":"db.deleteLabel","signature":"db.deleteLabel(label_type::STRING,label_name::STRING) :: (::NUL)"},{"name":"db.alterLabelDelFields","signature":"db.alterLabelDelFields(label_type::STRING,label_name::STRING,del_fields::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelAddFields","signature":"db.alterLabelAddFields(label_type::STRING,label_name::STRING,add_field_spec_values::LIST) :: (record_affected::INTEGER)"},{"name":"db.alterLabelModFields","signature":"db.alterLabelModFields(label_type::STRING,label_name::STRING,mod_field_specs::LIST) :: (record_affected::INTEGER)"},{"name":"db.createEdgeLabel","signature":"db.createEdgeLabel(type_name::STRING,field_specs::LIST) :: (::NUL)"},{"name":"db.addIndex","signature":"db.addIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN) :: (::NUL)"},{"name":"db.addEdgeIndex","signature":"db.addEdgeIndex(label_name::STRING,field_name::STRING,unique::BOOLEAN,) :: (::NUL)"},{"name":"db.addFullTextIndex","signature":"db.addFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteFullTextIndex","signature":"db.deleteFullTextIndex(is_vertex::BOOLEAN,label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.rebuildFullTextIndex","signature":"db.rebuildFullTextIndex(vertex_labels::STRING,edge_labels::STRING) :: (::NUL)"},{"name":"db.fullTextIndexes","signature":"db.fullTextIndexes() :: (is_vertex::BOOLEAN,label::STRING,field::STRING)"},{"name":"db.addEdgeConstraints","signature":"db.addEdgeConstraints(label_name::STRING,constraints::STRING) :: (::NUL)"},{"name":"db.clearEdgeConstraints","signature":"db.clearEdgeConstraints(label_name::STRING) :: (::NUL)"},{"name":"dbms.procedures","signature":"dbms.procedures() :: (name::STRING,signature::STRING,read_only::BOOLEAN)"},{"name":"dbms.meta.countDetail","signature":"dbms.meta.countDetail() :: (is_vertex::BOOLEAN,label::STRING,count::INTEGER)"},{"name":"dbms.meta.count","signature":"dbms.meta.count() :: (type::STRING,number::INTEGER)"},{"name":"dbms.meta.refreshCount","signature":"dbms.meta.refreshCount() :: (::NUL)"},{"name":"dbms.security.changePassword","signature":"dbms.security.changePassword(current_password::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.changeUserPassword","signature":"dbms.security.changeUserPassword(user_name::STRING,new_password::STRING) :: (::NUL)"},{"name":"dbms.security.createUser","signature":"dbms.security.createUser(user_name::STRING,password::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUser","signature":"dbms.security.deleteUser(user_name::STRING) :: (::NUL)"},{"name":"dbms.security.setUserMemoryLimit","signature":"dbms.security.setUserMemoryLimit(user_name::STRING,MemoryLimit::INTEGER) :: (::NUL)"},{"name":"dbms.security.listUsers","signature":"dbms.security.listUsers() :: (user_name::STRING,user_info::MAP)"},{"name":"dbms.security.showCurrentUser","signature":"dbms.security.showCurrentUser() :: (current_user::STRING)"},{"name":"dbms.security.listAllowedHosts","signature":"dbms.security.listAllowedHosts() :: (host::STRING)"},{"name":"dbms.security.deleteAllowedHosts","signature":"dbms.security.deleteAllowedHosts(hosts::LIST) :: (record_affected::INTEGER)"},{"name":"dbms.security.addAllowedHosts","signature":"dbms.security.addAllowedHosts(hosts::LIST) :: (num_added::INTEGER)"},{"name":"dbms.graph.createGraph","signature":"dbms.graph.createGraph(graph_name::STRING,description::STRING,max_size_GB::INTEGER) :: (::NUL)"},{"name":"dbms.graph.deleteGraph","signature":"dbms.graph.deleteGraph(graph_name::STRING) :: (::NUL)"},{"name":"dbms.graph.modGraph","signature":"dbms.graph.modGraph(graph_name::STRING,config::MAP) :: (::NUL)"},{"name":"dbms.graph.listGraphs","signature":"dbms.graph.listGraphs() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.listUserGraphs","signature":"dbms.graph.listUserGraphs(user_name::STRING) :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.graph.getGraphInfo","signature":"dbms.graph.getGraphInfo() :: (graph_name::STRING,configuration::MAP)"},{"name":"dbms.system.info","signature":"dbms.system.info() :: (name::STRING,value::ANY)"},{"name":"dbms.config.list","signature":"dbms.config.list() :: (name::STRING,value::ANY)"},{"name":"dbms.config.update","signature":"dbms.config.update(updates::MAP) :: (::NUL)"},{"name":"dbms.takeSnapshot","signature":"dbms.takeSnapshot() :: (path::STRING)"},{"name":"dbms.listBackupFiles","signature":"dbms.listBackupFiles() :: (file::STRING)"},{"name":"algo.shortestPath","signature":"algo.shortestPath(startNode::NODE,endNode::NODE,config::MAP) :: (nodeCount::INTEGER,totalCost::FLOAT,path::STRING)"},{"name":"algo.allShortestPaths","signature":"algo.allShortestPaths(startNode::NODE,endNode::NODE,config::MAP) :: (nodeIds::LIST,relationshipIds::LIST,cost::LIST)"},{"name":"algo.native.extract","signature":"algo.native.extract(id::ANY,config::MAP) :: (value::ANY)"},{"name":"algo.pagerank","signature":"algo.pagerank(num_iterations::INTEGER) :: (node::NODE,pr::FLOAT)"},{"name":"algo.jaccard","signature":"algo.jaccard(lhs::ANY,) :: (similarity::FLOAT)"},{"name":"dbms.security.listRoles","signature":"dbms.security.listRoles() :: (role_name::STRING,role_info::MAP)"},{"name":"dbms.security.createRole","signature":"dbms.security.createRole(role_name::STRING,desc::STRING) :: (::NUL)"},{"name":"dbms.security.deleteRole","signature":"dbms.security.deleteRole(role_name::STRING) :: (::NUL)"},{"name":"dbms.security.getUserInfo","signature":"dbms.security.getUserInfo(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getUserMemoryUsage","signature":"dbms.security.getUserMemoryUsage(user::STRING) :: (memory_usage::INTEGER)"},{"name":"dbms.security.getUserPermissions","signature":"dbms.security.getUserPermissions(user::STRING) :: (user_info::MAP)"},{"name":"dbms.security.getRoleInfo","signature":"dbms.security.getRoleInfo(role::STRING) :: (role_info::MAP)"},{"name":"dbms.security.disableRole","signature":"dbms.security.disableRole(role::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.modRoleDesc","signature":"dbms.security.modRoleDesc(role::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.rebuildRoleAccessLevel","signature":"dbms.security.rebuildRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleAccessLevel","signature":"dbms.security.modRoleAccessLevel(role::STRING,access_level::MAP) :: (::NUL)"},{"name":"dbms.security.modRoleFieldAccessLevel","signature":"dbms.security.modRoleFieldAccessLevel(role::STRING,) :: (::NUL)"},{"name":"dbms.security.disableUser","signature":"dbms.security.disableUser(user::STRING,disable::BOOLEAN) :: (::NUL)"},{"name":"dbms.security.setCurrentDesc","signature":"dbms.security.setCurrentDesc(description::STRING) :: (::NUL)"},{"name":"dbms.security.setUserDesc","signature":"dbms.security.setUserDesc(user::STRING,description::STRING) :: (::NUL)"},{"name":"dbms.security.deleteUserRoles","signature":"dbms.security.deleteUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.rebuildUserRoles","signature":"dbms.security.rebuildUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"dbms.security.addUserRoles","signature":"dbms.security.addUserRoles(user::STRING,roles::LIST) :: (::NUL)"},{"name":"db.plugin.loadPlugin","signature":"db.plugin.loadPlugin(plugin_type::STRING,plugin_name::STRING,plugin_content::ANY,code_type::STRING,plugin_description::STRING,read_only::BOOLEAN,version::STRING) :: (::NUL)"},{"name":"db.plugin.deletePlugin","signature":"db.plugin.deletePlugin(plugin_type::STRING,plugin_name::STRING) :: (::NUL)"},{"name":"db.plugin.getPluginInfo","signature":"db.plugin.getPluginInfo(plugin_type::STRING,plugin_name::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listPlugin","signature":"db.plugin.listPlugin(plugin_type::STRING,plugin_version::STRING) :: (plugin_description::MAP)"},{"name":"db.plugin.listUserPlugins","signature":"db.plugin.listUserPlugins() :: (graph::STRING,plugins::MAP)"},{"name":"db.plugin.callPlugin","signature":"db.plugin.callPlugin(plugin_type::STRING,plugin_name::STRING,param::STRING,timeout::DOUBLE,in_process::BOOLEAN) :: (result::STRING)"},{"name":"db.importor.dataImportor","signature":"db.importor.dataImportor(description::STRING,content::STRING,continue_on_error::BOOLEAN,thread_nums::INTEGER,delimiter::STRING) :: (::NUL)"},{"name":"db.importor.fullImportor","signature":"db.importor.fullImportor(conf::MAP) :: (result::STRING)"},{"name":"db.importor.fullFileImportor","signature":"db.importor.fullFileImportor(graph_name::STRING,path::STRING) :: (::NUL)"},{"name":"db.importor.schemaImportor","signature":"db.importor.schemaImportor(description::STRING) :: (::NUL)"},{"name":"db.deleteIndex","signature":"db.deleteIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.deleteEdgeIndex","signature":"db.deleteEdgeIndex(label_name::STRING,field_name::STRING) :: (::NUL)"},{"name":"db.flushDB","signature":"db.flushDB() :: (::NUL)"},{"name":"db.dropDB","signature":"db.dropDB() :: (::NUL)"},{"name":"dbms.task.listTasks","signature":"dbms.task.listTasks() :: (tasks_info::MAP)"},{"name":"dbms.task.terminateTask","signature":"dbms.task.terminateTask(task_id::STRING) :: (::NUL)"},{"name":"db.monitor.tuGraphInfo","signature":"db.monitor.tuGraphInfo() :: (request::STRING)"},{"name":"db.monitor.serverInfo","signature":"db.monitor.serverInfo() :: (cpu::STRING,memory::STRING,disk_rate::STRING,disk_storage::STRING)"},{"name":"dbms.ha.clusterInfo","signature":"dbms.ha.clusterInfo() :: (cluster_info::LIST,is_master::BOOLEAN)"}] CALL dbms.graph.createGraph('demo1'); [] CALL dbms.graph.listGraphs(); @@ -59,7 +58,7 @@ CALL dbms.graph.createGraph('demo2'); CALL dbms.security.createUser('guest2','123'); [] CALL dbms.system.info(); -[{"name":"lgraph_version","value":"4.2.0"},{"name":"up_time","value":0.0},{"name":"git_branch","value":"\"cypher_join_ast\""},{"name":"git_commit","value":"\"df86ed6\""},{"name":"web_commit","value":"\"8253763\""},{"name":"cpp_id","value":"\"GNU\""},{"name":"cpp_version","value":"\"8.2.0\""},{"name":"python_version","value":"\"3.6.8\""}] +[{"name":"lgraph_version","value":"4.3.0"},{"name":"up_time","value":0.0},{"name":"git_branch","value":"\"ddl_cypher\""},{"name":"git_commit","value":"\"313c6ce\""},{"name":"web_commit","value":"\"\""},{"name":"cpp_id","value":"\"GNU\""},{"name":"cpp_version","value":"\"8.4.0\""},{"name":"python_version","value":"\"3.6.9\""}] CALL dbms.config.list(); [{"name":"bind_host","value":"0.0.0.0"},{"name":"disable_auth","value":false},{"name":"durable","value":false},{"name":"enable_audit_log","value":false},{"name":"enable_backup_log","value":false},{"name":"enable_fulltext_index","value":false},{"name":"enable_ha","value":false},{"name":"enable_ip_check","value":false},{"name":"enable_rpc","value":false},{"name":"enable_ssl","value":false},{"name":"optimistic_txn","value":false},{"name":"port","value":7071},{"name":"subprocess_max_idle_seconds","value":600},{"name":"thread_limit","value":0},{"name":"verbose","value":1}] CALL dbms.security.listAllowedHosts(); @@ -155,9 +154,9 @@ CALL dbms.task.listTasks(); CALL plugin.cpp.scan_graph({scan_edges:true,times:2}); [{"plugin.cpp.scan_graph":{"num_edges":56,"num_vertices":42}}] CALL plugin.cpp.standard({}); -[{"bool_var":true,"double_var":123.23300170898438,"edge_num":{"in_num_edges":1,"out_num_edges":3},"edge_num_sum":4,"float_var":123.233,"in_edge":{"dst":20,"forward":true,"identity":0,"label_id":5,"properties":{"charactername":"Guenevere"},"src":2,"temporal_id":0},"list_var":["1"],"node":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},"out_edge":{"dst":14,"forward":true,"identity":0,"label_id":2,"properties":{"reg_time":"2023-05-01 12:00:00","weight":19.93},"src":12,"temporal_id":0},"path":[{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},{"dst":1,"forward":true,"identity":1,"label_id":1,"src":0,"temporal_id":0},{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}}]}] +[{"bool_var":true,"double_var":123.23300170898438,"edge_num":{"in_num_edges":1,"out_num_edges":3},"edge_num_sum":4,"float_var":123.233,"in_edge":{"dst":20,"forward":false,"identity":0,"label_id":5,"properties":{"charactername":"Guenevere"},"src":2,"temporal_id":0},"list_var":["1"],"node":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},"out_edge":{"dst":14,"forward":false,"identity":0,"label_id":2,"properties":{"reg_time":"2023-05-01 12:00:00","weight":19.93},"src":12,"temporal_id":0},"path":[{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},{"dst":2,"forward":false,"identity":0,"label_id":0,"src":0,"temporal_id":0},{"identity":2,"label":"Person","properties":{"birthyear":1937,"name":"Vanessa Redgrave"}}]}] CALL dbms.procedures() YIELD name RETURN name,1; -[{"1":1,"name":"db.subgraph"},{"1":1,"name":"db.vertexLabels"},{"1":1,"name":"db.edgeLabels"},{"1":1,"name":"db.indexes"},{"1":1,"name":"db.listLabelIndexes"},{"1":1,"name":"db.propertyKeys"},{"1":1,"name":"db.warmup"},{"1":1,"name":"db.createVertexLabelByJson"},{"1":1,"name":"db.createEdgeLabelByJson"},{"1":1,"name":"db.createVertexLabel"},{"1":1,"name":"db.createLabel"},{"1":1,"name":"db.getLabelSchema"},{"1":1,"name":"db.getVertexSchema"},{"1":1,"name":"db.getEdgeSchema"},{"1":1,"name":"db.deleteLabel"},{"1":1,"name":"db.alterLabelDelFields"},{"1":1,"name":"db.alterLabelAddFields"},{"1":1,"name":"db.upsertVertex"},{"1":1,"name":"db.upsertVertexByJson"},{"1":1,"name":"db.upsertEdge"},{"1":1,"name":"db.upsertEdgeByJson"},{"1":1,"name":"db.alterLabelModFields"},{"1":1,"name":"db.createEdgeLabel"},{"1":1,"name":"db.addIndex"},{"1":1,"name":"db.addEdgeIndex"},{"1":1,"name":"db.addFullTextIndex"},{"1":1,"name":"db.deleteFullTextIndex"},{"1":1,"name":"db.rebuildFullTextIndex"},{"1":1,"name":"db.fullTextIndexes"},{"1":1,"name":"db.addEdgeConstraints"},{"1":1,"name":"db.clearEdgeConstraints"},{"1":1,"name":"dbms.procedures"},{"1":1,"name":"dbms.meta.countDetail"},{"1":1,"name":"dbms.meta.count"},{"1":1,"name":"dbms.meta.refreshCount"},{"1":1,"name":"dbms.security.changePassword"},{"1":1,"name":"dbms.security.changeUserPassword"},{"1":1,"name":"dbms.security.createUser"},{"1":1,"name":"dbms.security.deleteUser"},{"1":1,"name":"dbms.security.setUserMemoryLimit"},{"1":1,"name":"dbms.security.listUsers"},{"1":1,"name":"dbms.security.showCurrentUser"},{"1":1,"name":"dbms.security.listAllowedHosts"},{"1":1,"name":"dbms.security.deleteAllowedHosts"},{"1":1,"name":"dbms.security.addAllowedHosts"},{"1":1,"name":"dbms.graph.createGraph"},{"1":1,"name":"dbms.graph.deleteGraph"},{"1":1,"name":"dbms.graph.modGraph"},{"1":1,"name":"dbms.graph.listGraphs"},{"1":1,"name":"dbms.graph.listUserGraphs"},{"1":1,"name":"dbms.graph.getGraphInfo"},{"1":1,"name":"dbms.graph.getGraphSchema"},{"1":1,"name":"dbms.system.info"},{"1":1,"name":"dbms.config.list"},{"1":1,"name":"dbms.config.update"},{"1":1,"name":"dbms.takeSnapshot"},{"1":1,"name":"dbms.listBackupFiles"},{"1":1,"name":"algo.shortestPath"},{"1":1,"name":"algo.allShortestPaths"},{"1":1,"name":"algo.native.extract"},{"1":1,"name":"algo.pagerank"},{"1":1,"name":"algo.jaccard"},{"1":1,"name":"spatial.distance"},{"1":1,"name":"dbms.security.listRoles"},{"1":1,"name":"dbms.security.createRole"},{"1":1,"name":"dbms.security.deleteRole"},{"1":1,"name":"dbms.security.getUserInfo"},{"1":1,"name":"dbms.security.getUserMemoryUsage"},{"1":1,"name":"dbms.security.getUserPermissions"},{"1":1,"name":"dbms.security.getRoleInfo"},{"1":1,"name":"dbms.security.disableRole"},{"1":1,"name":"dbms.security.modRoleDesc"},{"1":1,"name":"dbms.security.rebuildRoleAccessLevel"},{"1":1,"name":"dbms.security.modRoleAccessLevel"},{"1":1,"name":"dbms.security.modRoleFieldAccessLevel"},{"1":1,"name":"dbms.security.disableUser"},{"1":1,"name":"dbms.security.setCurrentDesc"},{"1":1,"name":"dbms.security.setUserDesc"},{"1":1,"name":"dbms.security.deleteUserRoles"},{"1":1,"name":"dbms.security.rebuildUserRoles"},{"1":1,"name":"dbms.security.addUserRoles"},{"1":1,"name":"db.plugin.loadPlugin"},{"1":1,"name":"db.plugin.deletePlugin"},{"1":1,"name":"db.plugin.getPluginInfo"},{"1":1,"name":"db.plugin.listPlugin"},{"1":1,"name":"db.plugin.listUserPlugins"},{"1":1,"name":"db.plugin.callPlugin"},{"1":1,"name":"db.importor.dataImportor"},{"1":1,"name":"db.importor.fullImportor"},{"1":1,"name":"db.importor.fullFileImportor"},{"1":1,"name":"db.importor.schemaImportor"},{"1":1,"name":"db.deleteIndex"},{"1":1,"name":"db.deleteEdgeIndex"},{"1":1,"name":"db.flushDB"},{"1":1,"name":"db.dropDB"},{"1":1,"name":"dbms.task.listTasks"},{"1":1,"name":"dbms.task.terminateTask"},{"1":1,"name":"db.monitor.tuGraphInfo"},{"1":1,"name":"db.monitor.serverInfo"},{"1":1,"name":"dbms.ha.clusterInfo"}] +[{"1":1,"name":"db.subgraph"},{"1":1,"name":"db.vertexLabels"},{"1":1,"name":"db.edgeLabels"},{"1":1,"name":"db.indexes"},{"1":1,"name":"db.listLabelIndexes"},{"1":1,"name":"db.propertyKeys"},{"1":1,"name":"db.warmup"},{"1":1,"name":"db.createVertexLabel"},{"1":1,"name":"db.createLabel"},{"1":1,"name":"db.getLabelSchema"},{"1":1,"name":"db.getVertexSchema"},{"1":1,"name":"db.getEdgeSchema"},{"1":1,"name":"db.deleteLabel"},{"1":1,"name":"db.alterLabelDelFields"},{"1":1,"name":"db.alterLabelAddFields"},{"1":1,"name":"db.alterLabelModFields"},{"1":1,"name":"db.createEdgeLabel"},{"1":1,"name":"db.addIndex"},{"1":1,"name":"db.addEdgeIndex"},{"1":1,"name":"db.addFullTextIndex"},{"1":1,"name":"db.deleteFullTextIndex"},{"1":1,"name":"db.rebuildFullTextIndex"},{"1":1,"name":"db.fullTextIndexes"},{"1":1,"name":"db.addEdgeConstraints"},{"1":1,"name":"db.clearEdgeConstraints"},{"1":1,"name":"dbms.procedures"},{"1":1,"name":"dbms.meta.countDetail"},{"1":1,"name":"dbms.meta.count"},{"1":1,"name":"dbms.meta.refreshCount"},{"1":1,"name":"dbms.security.changePassword"},{"1":1,"name":"dbms.security.changeUserPassword"},{"1":1,"name":"dbms.security.createUser"},{"1":1,"name":"dbms.security.deleteUser"},{"1":1,"name":"dbms.security.setUserMemoryLimit"},{"1":1,"name":"dbms.security.listUsers"},{"1":1,"name":"dbms.security.showCurrentUser"},{"1":1,"name":"dbms.security.listAllowedHosts"},{"1":1,"name":"dbms.security.deleteAllowedHosts"},{"1":1,"name":"dbms.security.addAllowedHosts"},{"1":1,"name":"dbms.graph.createGraph"},{"1":1,"name":"dbms.graph.deleteGraph"},{"1":1,"name":"dbms.graph.modGraph"},{"1":1,"name":"dbms.graph.listGraphs"},{"1":1,"name":"dbms.graph.listUserGraphs"},{"1":1,"name":"dbms.graph.getGraphInfo"},{"1":1,"name":"dbms.system.info"},{"1":1,"name":"dbms.config.list"},{"1":1,"name":"dbms.config.update"},{"1":1,"name":"dbms.takeSnapshot"},{"1":1,"name":"dbms.listBackupFiles"},{"1":1,"name":"algo.shortestPath"},{"1":1,"name":"algo.allShortestPaths"},{"1":1,"name":"algo.native.extract"},{"1":1,"name":"algo.pagerank"},{"1":1,"name":"algo.jaccard"},{"1":1,"name":"dbms.security.listRoles"},{"1":1,"name":"dbms.security.createRole"},{"1":1,"name":"dbms.security.deleteRole"},{"1":1,"name":"dbms.security.getUserInfo"},{"1":1,"name":"dbms.security.getUserMemoryUsage"},{"1":1,"name":"dbms.security.getUserPermissions"},{"1":1,"name":"dbms.security.getRoleInfo"},{"1":1,"name":"dbms.security.disableRole"},{"1":1,"name":"dbms.security.modRoleDesc"},{"1":1,"name":"dbms.security.rebuildRoleAccessLevel"},{"1":1,"name":"dbms.security.modRoleAccessLevel"},{"1":1,"name":"dbms.security.modRoleFieldAccessLevel"},{"1":1,"name":"dbms.security.disableUser"},{"1":1,"name":"dbms.security.setCurrentDesc"},{"1":1,"name":"dbms.security.setUserDesc"},{"1":1,"name":"dbms.security.deleteUserRoles"},{"1":1,"name":"dbms.security.rebuildUserRoles"},{"1":1,"name":"dbms.security.addUserRoles"},{"1":1,"name":"db.plugin.loadPlugin"},{"1":1,"name":"db.plugin.deletePlugin"},{"1":1,"name":"db.plugin.getPluginInfo"},{"1":1,"name":"db.plugin.listPlugin"},{"1":1,"name":"db.plugin.listUserPlugins"},{"1":1,"name":"db.plugin.callPlugin"},{"1":1,"name":"db.importor.dataImportor"},{"1":1,"name":"db.importor.fullImportor"},{"1":1,"name":"db.importor.fullFileImportor"},{"1":1,"name":"db.importor.schemaImportor"},{"1":1,"name":"db.deleteIndex"},{"1":1,"name":"db.deleteEdgeIndex"},{"1":1,"name":"db.flushDB"},{"1":1,"name":"db.dropDB"},{"1":1,"name":"dbms.task.listTasks"},{"1":1,"name":"dbms.task.terminateTask"},{"1":1,"name":"db.monitor.tuGraphInfo"},{"1":1,"name":"db.monitor.serverInfo"},{"1":1,"name":"dbms.ha.clusterInfo"}] MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2) YIELD nodeCount,totalCost RETURN nodeCount,totalCost /* 2,1.0 */; [{"nodeCount":2,"totalCost":1.0}] MATCH (n1 {name:'Michael Redgrave'}),(n2 {name:'Rachel Kempson'}) CALL algo.shortestPath(n1,n2) YIELD path RETURN path /* [V[vid0],E[vid0_vid1_type_eid],V[vid1]] */; diff --git a/test/resource/unit_test/procedure/cypher/procedure.test b/test/resource/unit_test/procedure/cypher/procedure.test index 7e67f8a2fa..1d57cafaf1 100644 --- a/test/resource/unit_test/procedure/cypher/procedure.test +++ b/test/resource/unit_test/procedure/cypher/procedure.test @@ -5,7 +5,6 @@ -- loadProcedure peek_some_node_salt ../../test/test_procedures/peek_some_node_salt.cpp read_only=true -- loadProcedure custom_path_process ../../test/test_procedures/v2_path_process.cpp read_only=true -- loadProcedure custom_algo ../../test/test_procedures/v2_algo.cpp read_only=true - CALL db.createVertexLabel('Director', 'name', 'name', 'STRING', false, 'age', 'INT16', true); CALL db.createVertexLabel('P2', 'flag1', 'flag1', 'BOOL', false, 'flag2', 'Bool', true); CALL db.createEdgeLabel('LIKE', '[]'); diff --git a/test/resource/unit_test/profile/cypher/profile.test b/test/resource/unit_test/profile/cypher/profile.test index 70c9f85bc2..f5d36a835b 100644 --- a/test/resource/unit_test/profile/cypher/profile.test +++ b/test/resource/unit_test/profile/cypher/profile.test @@ -1,2 +1,2 @@ MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; -PROFILE MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; \ No newline at end of file +#PROFILE MATCH (n:Person) WHERE n.birthyear > 1960 RETURN n LIMIT 5; diff --git a/test/resource/unit_test/set/cypher/set.result b/test/resource/unit_test/set/cypher/set.result index 67386ad002..60ac5d55b2 100644 --- a/test/resource/unit_test/set/cypher/set.result +++ b/test/resource/unit_test/set/cypher/set.result @@ -6,7 +6,8 @@ CREATE (a:Person {name:'A', age:13, date:DATE('2023-07-23')}) CREATE (b:Person { [{"":"created 9 vertices, created 8 edges."}] MATCH (n:Person {name:'E'}) SET n.name='X'; [{"":"set 1 properties."}] -#MATCH (n:Person {name:'A'}), (m:Person {name:'B'}) SET n.age=50 SET m.age=51; +MATCH (n:Person {name:'A'}), (m:Person {name:'B'}) SET n.age=50 SET m.age=51; +[{"":"set 2 properties."}] MATCH (n:Person {name:'A'})-[e:KNOWS]->(m:Person) SET n.age=50 SET e.weight=50; [{"":"set 6 properties."}] MATCH (n:Person {name:'B'})<-[]-(m:Person) SET m.age = 34; @@ -16,13 +17,13 @@ MATCH (n:Person {name:'B'})<-[]-(m:Person) SET m.age = id(n); MATCH (n:Person {name:'B'})<-[]-(m:Person) SET m = {age: 33}; [{"":"set 1 properties."}] match (n) return n,properties(n) /*debug*/; -[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":33,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":33,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":null,"date":null,"eyes":null,"name":"X"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":NUL,\"date\":NUL,\"name\":\"X\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] +[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":51,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":51,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":null,"date":null,"eyes":null,"name":"X"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":NUL,\"date\":NUL,\"name\":\"X\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] match (n) return n,properties(n) /*debug*/; -[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":33,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":33,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":null,"date":null,"eyes":null,"name":"X"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":NUL,\"date\":NUL,\"name\":\"X\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] +[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":51,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":51,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":null,"date":null,"eyes":null,"name":"X"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":NUL,\"date\":NUL,\"name\":\"X\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] MATCH (n:Person {name:'X'}) SET n += {name:'Y', age:19}; [{"":"set 2 properties."}] match (n) return n,properties(n) /*debug*/; -[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":33,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":33,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":19,"date":null,"eyes":null,"name":"Y"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":19,\"date\":NUL,\"name\":\"Y\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] +[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":51,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":51,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":4,"label":"Person","properties":{"age":19,"date":null,"eyes":null,"name":"Y"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":4,\"age\":19,\"date\":NUL,\"name\":\"Y\",\"eyes\":NUL}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] MATCH (n {name:'A'})-[r:KNOWS]->(m {name:'B'}) SET r.weight=11; [{"":"set 1 properties."}] MATCH (n)-[r:KNOWS]->(m) WHERE r.weight=15 SET r += {weight:16}; @@ -36,4 +37,4 @@ match (n)-[r]->(m) return r,properties(r) /*debug*/; MATCH (n:Person {name:'Y'}) SET n=NULL; [{"":"set 1 properties."}] match (n) return n,properties(n) /*debug*/; -[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":33,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":33,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] +[{"n":{"identity":0,"label":"Person","properties":{"age":33,"date":"2023-07-23","eyes":null,"name":"A"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":0,\"age\":33,\"date\":2023-07-23,\"name\":\"A\",\"eyes\":NUL}"},{"n":{"identity":1,"label":"Person","properties":{"age":51,"date":null,"eyes":"blue","name":"B"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":1,\"age\":51,\"date\":NUL,\"name\":\"B\",\"eyes\":\"blue\"}"},{"n":{"identity":2,"label":"Person","properties":{"age":44,"date":null,"eyes":"blue","name":"C"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":2,\"age\":44,\"date\":NUL,\"name\":\"C\",\"eyes\":\"blue\"}"},{"n":{"identity":3,"label":"Person","properties":{"age":null,"date":null,"eyes":"brown","name":"D"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":3,\"age\":NUL,\"date\":NUL,\"name\":\"D\",\"eyes\":\"brown\"}"},{"n":{"identity":5,"label":"Person","properties":{"age":1,"date":null,"eyes":null,"name":"F"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":5,\"age\":1,\"date\":NUL,\"name\":\"F\",\"eyes\":NUL}"},{"n":{"identity":6,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"G"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":6,\"age\":2,\"date\":NUL,\"name\":\"G\",\"eyes\":NUL}"},{"n":{"identity":7,"label":"Person","properties":{"age":2,"date":null,"eyes":null,"name":"H"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":7,\"age\":2,\"date\":NUL,\"name\":\"H\",\"eyes\":NUL}"},{"n":{"identity":8,"label":"Person","properties":{"age":3,"date":null,"eyes":null,"name":"I"}},"properties(n)":"{\"_LABEL_\":\"Person\",\"_VID_\":8,\"age\":3,\"date\":NUL,\"name\":\"I\",\"eyes\":NUL}"}] diff --git a/test/resource/unit_test/set/cypher/set.test b/test/resource/unit_test/set/cypher/set.test index 9588fef2e4..c59f5db985 100644 --- a/test/resource/unit_test/set/cypher/set.test +++ b/test/resource/unit_test/set/cypher/set.test @@ -2,7 +2,7 @@ CALL db.createVertexLabel('Person', 'name', 'name', 'STRING', false, 'age', 'INT CALL db.createEdgeLabel('KNOWS', '[]', 'weight', 'INT16', true); CREATE (a:Person {name:'A', age:13, date:DATE('2023-07-23')}) CREATE (b:Person {name:'B', age:33, eyes:'blue'}) CREATE (c:Person {name:'C', age:44, eyes:'blue'}) CREATE (d:Person {name:'D', eyes:'brown'}) CREATE (e:Person {name:'E'}) CREATE (f:Person {name:'F', age:1}) CREATE (g:Person {name:'G', age:2}) CREATE (h:Person {name:'H', age:2}) CREATE (i1:Person {name:'I', age:3}) CREATE (a)-[:KNOWS {weight:10}]->(b), (a)-[:KNOWS {weight:15}]->(c), (a)-[:KNOWS {weight:40}]->(d), (b)-[:KNOWS {weight:20}]->(e), (c)-[:KNOWS {weight:12}]->(e),(f)-[:KNOWS {weight:0}]->(g), (f)-[:KNOWS {weight:0}]->(h),(f)-[:KNOWS {weight:0}]->(i1) ; MATCH (n:Person {name:'E'}) SET n.name='X'; -#MATCH (n:Person {name:'A'}), (m:Person {name:'B'}) SET n.age=50 SET m.age=51; +MATCH (n:Person {name:'A'}), (m:Person {name:'B'}) SET n.age=50 SET m.age=51; MATCH (n:Person {name:'A'})-[e:KNOWS]->(m:Person) SET n.age=50 SET e.weight=50; MATCH (n:Person {name:'B'})<-[]-(m:Person) SET m.age = 34; MATCH (n:Person {name:'B'})<-[]-(m:Person) SET m.age = id(n); diff --git a/test/resource/unit_test/topn/cypher/topn.result b/test/resource/unit_test/topn/cypher/topn.result index 4f5f326fbb..6a3baa0185 100644 --- a/test/resource/unit_test/topn/cypher/topn.result +++ b/test/resource/unit_test/topn/cypher/topn.result @@ -5,7 +5,7 @@ match (v1:Film) return v1.title order by v1.title limit 3; match (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,v2.name as cnt order by cnt desc limit 3; [{"cnt":"Vanessa Redgrave","v1.title":"Camelot"},{"cnt":"Richard Harris","v1.title":"Camelot"},{"cnt":"Richard Harris","v1.title":"Harry Potter and the Sorcerer's Stone"}] match (:Person {name:'Vanessa Redgrave'})<-[:HAS_CHILD]-(p)-[:ACTED_IN*0..]->(m) return p.name,m order by p.name limit 3; -[{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"p.name":"Michael Redgrave"},{"m":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"p.name":"Michael Redgrave"},{"m":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},"p.name":"Rachel Kempson"}] +[{"m":{"identity":1,"label":"Person","properties":{"birthyear":1908,"name":"Michael Redgrave"}},"p.name":"Michael Redgrave"},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"p.name":"Michael Redgrave"},{"m":{"identity":0,"label":"Person","properties":{"birthyear":1910,"name":"Rachel Kempson"}},"p.name":"Rachel Kempson"}] MATCH (n) RETURN n.name AS name ORDER BY name LIMIT 10; [{"name":null},{"name":null},{"name":null},{"name":null},{"name":null},{"name":"Christopher Nolan"},{"name":"Corin Redgrave"},{"name":"Dennis Quaid"},{"name":"Houston"},{"name":"Jemma Redgrave"}] MATCH (n:Person {name:'Vanessa Redgrave'})-[*2]-(m:Person) RETURN m.birthyear, m.name ORDER BY m.name LIMIT 5; diff --git a/test/resource/unit_test/undefined_var/cypher/undefined_var.result b/test/resource/unit_test/undefined_var/cypher/undefined_var.result index 28811c5b54..5bad2bc967 100644 --- a/test/resource/unit_test/undefined_var/cypher/undefined_var.result +++ b/test/resource/unit_test/undefined_var/cypher/undefined_var.result @@ -3,15 +3,15 @@ MATCH (n:Person) RETURN q; MATCH (n:Person) RETURN n.name + q.name; [CypherException] CypherException: Unknown variable: q MATCH (n:Person) WITH q as m RETURN m; -[InputError] Variable `q` not defined +[CypherException] CypherException: Unknown variable: q MATCH (n:Person) WITH k RETURN n; -[InputError] Variable `k` not defined +[CypherException] CypherException: Unknown variable: k UNWIND k as n RETURN n; -[InputError] Variable `k` not defined +[CypherException] CypherException: Unknown variable: k MATCH (n:Person) WHERE n.name = k RETURN n; [CypherException] CypherException: Unknown variable: k MATCH (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct v2) as cnt order by number; -[InputError] Variable `number` not defined +[CypherException] CypherException: Unknown variable: k WITH [1,2,3] AS k RETURN [x in k | x + y] AS result; [InputError] Variable `y` not defined REMOVE a.name; @@ -29,4 +29,4 @@ MATCH(n:Person {name: 'D'}) DELETE k; MATCH(n)-[r:REL]-(m) DELETE k; [InputError] Variable `k` not defined MATCH (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) WITH v1, count(*) AS edge_num WHERE non_edge > 1 RETURN v1,edge_num; -[InputError] Variable `non_edge` not defined +[CypherException] CypherException: Unknown variable: non_edge diff --git a/test/resource/unit_test/undefined_var/cypher/undefined_var.test b/test/resource/unit_test/undefined_var/cypher/undefined_var.test index d24fba38c5..37af12e075 100644 --- a/test/resource/unit_test/undefined_var/cypher/undefined_var.test +++ b/test/resource/unit_test/undefined_var/cypher/undefined_var.test @@ -8,8 +8,8 @@ MATCH (v1:Film)<-[:ACTED_IN|DIRECTED]-(v2:Person) return v1.title,count(distinct WITH [1,2,3] AS k RETURN [x in k | x + y] AS result; REMOVE a.name; SET a :MyLabel; -MATCH (n:Person {name:'D'}),(m:Person {name:'X'}) SET z=m; -MATCH (n:Person {name:'D'}),(m:Person {name:'X'}) SET n=z; +#MATCH (n:Person {name:'D'}),(m:Person {name:'X'}) SET z=m; +#MATCH (n:Person {name:'D'}),(m:Person {name:'X'}) SET n=z; MATCH (n:Person {name:'X'}) SET z.name = "Y"; MATCH(n:Person {name: 'D'}) DELETE k; MATCH(n)-[r:REL]-(m) DELETE k; diff --git a/test/resource/unit_test/unwind/cypher/unwind.result b/test/resource/unit_test/unwind/cypher/unwind.result index 8e5cb15232..503a41686d 100644 --- a/test/resource/unit_test/unwind/cypher/unwind.result +++ b/test/resource/unit_test/unwind/cypher/unwind.result @@ -13,7 +13,7 @@ UNWIND NULL AS x RETURN x, 'some_literal'; UNWIND [1,2] AS x MATCH (n {name:'Houston'}) RETURN x,n; [{"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2}] UNWIND [1,2] AS x MATCH (n {name:'Houston'}),(m:Film) RETURN x,n,m; -[{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2}] +[{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2},{"m":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":1},{"m":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":2}] UNWIND ['Paris','Houston'] AS x MATCH (n {name:x}),(m:Film) RETURN x,n,m; [{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":"Houston"},{"m":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":"Houston"},{"m":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":"Houston"},{"m":{"identity":18,"label":"Film","properties":{"title":"Harry Potter and the Sorcerer's Stone"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":"Houston"},{"m":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}},"n":{"identity":15,"label":"City","properties":{"name":"Houston"}},"x":"Houston"}] MATCH (c {name:'Houston'}) WITH c MATCH (c)<-[r]-(p) RETURN p; @@ -22,17 +22,13 @@ MATCH (c {name:'Houston'}) WITH c MATCH (p)-[r]->(c) RETURN p; [{"p":{"identity":7,"label":"Person","properties":{"birthyear":1954,"name":"Dennis Quaid"}}}] MATCH (a {name:'Liam Neeson'}) WITH a,'London' AS cid MATCH (c {name:cid}) RETURN a,c; [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"c":{"identity":14,"label":"City","properties":{"name":"London"}}}] -MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,count(c); +MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,count(c); [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"count(c)":2}] -MATCH (a {name:'Liam Neeson'}),(b {name:'Dennis Quaid'}) WITH a,b,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,b,count(c); +MATCH (a {name:'Liam Neeson'}),(b {name:'Dennis Quaid'}) WITH a,b,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,b,count(c); [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"b":{"identity":7,"label":"Person","properties":{"birthyear":1954,"name":"Dennis Quaid"}},"count(c)":2}] -MATCH (a {name:'Dennis Quaid'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid})<-[]-(a) RETURN a,count(c); +MATCH (a {name:'Dennis Quaid'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid})<-[]-(a) RETURN a,count(c); [{"a":{"identity":7,"label":"Person","properties":{"birthyear":1954,"name":"Dennis Quaid"}},"count(c)":1}] -MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid})<-[]-()-[:MARRIED]->(a) RETURN a,count(c); +MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid})<-[]-()-[:MARRIED]->(a) RETURN a,count(c); [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"count(c)":1}] MATCH (c {name:'Houston'}) WITH c MATCH (p:Person {name:'Liam Neeson'}) CREATE (c)-[:HAS_CHILD]->(p); [{"":"created 0 vertices, created 1 edges."}] @@ -40,16 +36,11 @@ MATCH (c {name:'Houston'}) WITH c UNWIND $personIds AS pId MATCH (q:Person {name [{"":"created 0 vertices, created 3 edges."}] MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer1', birthyear:2002})-[r:BORN_IN]->(c) RETURN p,r,c; [{"c":{"identity":15,"label":"City","properties":{"name":"Houston"}},"p":{"identity":21,"label":"Person","properties":{"birthyear":2002,"name":"passer1"}},"r":{"dst":15,"forward":false,"identity":0,"label":"BORN_IN","label_id":2,"properties":{"reg_time":null,"weight":null},"src":21,"temporal_id":0}}] -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer2', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q); +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer2', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q); [{"":"created 1 vertices, created 4 edges."}] -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer3', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p -UNWIND ['Liam Neeson'] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,7 */; +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer3', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p UNWIND ['Liam Neeson'] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,7 */; [{"":"created 1 vertices, created 7 edges."}] -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer4', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p -UNWIND [] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,4 */; +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer4', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p UNWIND [] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,4 */; [{"":"created 1 vertices, created 4 edges."}] WITH [1, 1, 2, 2] AS coll UNWIND coll AS x WITH x RETURN collect(x); [{"collect(x)":[1,1,2,2]}] diff --git a/test/resource/unit_test/unwind/cypher/unwind.test b/test/resource/unit_test/unwind/cypher/unwind.test index 0c508b58d1..bd750834a2 100644 --- a/test/resource/unit_test/unwind/cypher/unwind.test +++ b/test/resource/unit_test/unwind/cypher/unwind.test @@ -10,25 +10,16 @@ UNWIND ['Paris','Houston'] AS x MATCH (n {name:x}),(m:Film) RETURN x,n,m; MATCH (c {name:'Houston'}) WITH c MATCH (c)<-[r]-(p) RETURN p; MATCH (c {name:'Houston'}) WITH c MATCH (p)-[r]->(c) RETURN p; MATCH (a {name:'Liam Neeson'}) WITH a,'London' AS cid MATCH (c {name:cid}) RETURN a,c; -MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,count(c); -MATCH (a {name:'Liam Neeson'}),(b {name:'Dennis Quaid'}) WITH a,b,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,b,count(c); -MATCH (a {name:'Dennis Quaid'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid})<-[]-(a) RETURN a,count(c); -MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids -UNWIND cids AS cid MATCH (c {name:cid})<-[]-()-[:MARRIED]->(a) RETURN a,count(c); +MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,count(c); +MATCH (a {name:'Liam Neeson'}),(b {name:'Dennis Quaid'}) WITH a,b,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid}) RETURN a,b,count(c); +MATCH (a {name:'Dennis Quaid'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid})<-[]-(a) RETURN a,count(c); +MATCH (a {name:'Liam Neeson'}) WITH a,['London','Houston'] AS cids UNWIND cids AS cid MATCH (c {name:cid})<-[]-()-[:MARRIED]->(a) RETURN a,count(c); MATCH (c {name:'Houston'}) WITH c MATCH (p:Person {name:'Liam Neeson'}) CREATE (c)-[:HAS_CHILD]->(p); MATCH (c {name:'Houston'}) WITH c UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (c)-[:HAS_CHILD]->(q); MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer1', birthyear:2002})-[r:BORN_IN]->(c) RETURN p,r,c; -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer2', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q); -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer3', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p -UNWIND ['Liam Neeson'] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,7 */; -MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer4', birthyear:2002})-[r:BORN_IN]->(c) WITH p -UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p -UNWIND [] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,4 */; +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer2', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q); +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer3', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p UNWIND ['Liam Neeson'] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,7 */; +MATCH (c {name:'Houston'}) CREATE (p:Person {name:'passer4', birthyear:2002})-[r:BORN_IN]->(c) WITH p UNWIND $personIds AS pId MATCH (q:Person {name:pId}) CREATE (p)-[:HAS_CHILD]->(q) WITH p UNWIND [] AS sId MATCH (s:Person {name:sId}) CREATE (p)-[:DIRECTED]->(s) /* 1,4 */; WITH [1, 1, 2, 2] AS coll UNWIND coll AS x WITH x RETURN collect(x); WITH [1, 1, 2, 2] AS coll UNWIND coll AS x WITH x RETURN collect(DISTINCT x); CREATE (:City {name:'Shanghai'}), (:City {name:'Zhongshan'}), (:Person {name:'Zhongshan'}); diff --git a/test/resource/unit_test/with/cypher/with.result b/test/resource/unit_test/with/cypher/with.result index 13f5283907..50bd7bbde7 100644 --- a/test/resource/unit_test/with/cypher/with.result +++ b/test/resource/unit_test/with/cypher/with.result @@ -8,7 +8,8 @@ match (n {name:'Liam Neeson'}) with n return n; [{"n":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}}] match (n {name:'Liam Neeson'}) with n match (n)-->(m) return n,m; [{"m":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"n":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}},{"m":{"identity":17,"label":"Film","properties":{"title":"Batman Begins"}},"n":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}}] -#match (n {name:'London'}) with n optional match (n)-->(m) return n,m; +match (n {name:'London'}) with n optional match (n)-->(m) return n,m; +[{"m":"__null__","n":{"identity":14,"label":"City","properties":{"name":"London"}}}] match (a {name:'Liam Neeson'})-[r]->(b) with b match (b)-[]->(c) return c; [{"c":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}},{"c":{"identity":14,"label":"City","properties":{"name":"London"}}},{"c":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}}}] match (a {name:'Liam Neeson'}),(b {name:'London'}) with a, b match (c:Film) return a,b,c; @@ -23,11 +24,12 @@ MATCH (a {name:'Liam Neeson'})-[r]->(b) WITH a,count(b) AS out_num MATCH (a)<-[] [{"in_num":1,"out_num":2}] match (a {name:'Liam Neeson'})-[r]->(b) with a,b match (b)-[]->(c) return a,b,c; [{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"b":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"c":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}}},{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"b":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"c":{"identity":14,"label":"City","properties":{"name":"London"}}},{"a":{"identity":4,"label":"Person","properties":{"birthyear":1952,"name":"Liam Neeson"}},"b":{"identity":5,"label":"Person","properties":{"birthyear":1963,"name":"Natasha Richardson"}},"c":{"identity":19,"label":"Film","properties":{"title":"The Parent Trap"}}}] -#match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}),(n)-[r]->(m) return r,type(r); +match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}),(n)-[r]->(m) return r,type(r); +[{"r":{"dst":5,"forward":false,"identity":0,"label":"MARRIED","label_id":1,"src":4,"temporal_id":0},"type(r)":"MARRIED"}] match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}) with n,m match (n)-[r]->(m) return r,type(r); [{"r":{"dst":5,"forward":false,"identity":0,"label":"MARRIED","label_id":1,"src":4,"temporal_id":0},"type(r)":"MARRIED"}] match (n {name:'Liam Neeson'}),(m {name:'Liam Neeson'}) with n,m optional match (n)-[r]->(m) return r,type(r); -[{"r":{"dst":17,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Henri Ducard"},"src":4,"temporal_id":0},"type(r)":null}] +[{"r":"__null__","type(r)":null}] match (n {name:'Liam Neeson'})-[r]->(m) with r return r,type(r); [{"r":{"dst":5,"forward":false,"identity":0,"label":"MARRIED","label_id":1,"src":4,"temporal_id":0},"type(r)":"MARRIED"},{"r":{"dst":17,"forward":false,"identity":0,"label":"ACTED_IN","label_id":5,"properties":{"charactername":"Henri Ducard"},"src":4,"temporal_id":0},"type(r)":"ACTED_IN"}] match (n:City) with count (n) as num_city match (n:Film) return count(n) as num_film, num_city; @@ -74,13 +76,19 @@ WITH 1 AS a MATCH (n:City) RETURN DISTINCT a,n; [{"a":1,"n":{"identity":13,"label":"City","properties":{"name":"New York"}}},{"a":1,"n":{"identity":14,"label":"City","properties":{"name":"London"}}},{"a":1,"n":{"identity":15,"label":"City","properties":{"name":"Houston"}}}] MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; [{"film":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"m.name":"Vanessa Redgrave"},{"film":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"m.name":"Michael Redgrave"}] -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film)<-[:ACTED_IN]-(coactor) RETURN m.name,film,coactor; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film RETURN m.name,film; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film OPTIONAL MATCH (film)<-[:WROTE_MUSIC_FOR]-(musician) RETURN m.name,film,musician; +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; +[{"film":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"m.name":"Vanessa Redgrave"},{"film":"__null__","m.name":"Corin Redgrave"},{"film":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"m.name":"Michael Redgrave"}] +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film)<-[:ACTED_IN]-(coactor) RETURN m.name,film,coactor; +[{"coactor":{"identity":6,"label":"Person","properties":{"birthyear":1930,"name":"Richard Harris"}},"film":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"m.name":"Vanessa Redgrave"},{"coactor":"__null__","film":"__null__","m.name":"Corin Redgrave"},{"coactor":"__null__","film":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"m.name":"Michael Redgrave"}] +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film RETURN m.name,film; +[{"film":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"m.name":"Vanessa Redgrave"},{"film":"__null__","m.name":"Corin Redgrave"},{"film":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"m.name":"Michael Redgrave"}] +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film OPTIONAL MATCH (film)<-[:WROTE_MUSIC_FOR]-(musician) RETURN m.name,film,musician; +[{"film":{"identity":20,"label":"Film","properties":{"title":"Camelot"}},"m.name":"Vanessa Redgrave","musician":"__null__"},{"film":"__null__","m.name":"Corin Redgrave","musician":"__null__"},{"film":{"identity":16,"label":"Film","properties":{"title":"Goodbye, Mr. Chips"}},"m.name":"Michael Redgrave","musician":{"identity":11,"label":"Person","properties":{"birthyear":1932,"name":"John Williams"}}}] match (n:Person) where n.name='Michael Redgrave' with n.birthyear as nb match (p)-[:HAS_CHILD]->(c) where p.birthyear=nb return c.name; [{"c.name":"Vanessa Redgrave"},{"c.name":"Corin Redgrave"}] -#match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; -#match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with n, collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; +match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; +[{"p.name":"Michael Redgrave"},{"p.name":"Roy Redgrave"}] +match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with n, collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; +[{"p.name":"Roy Redgrave"},{"p.name":"Michael Redgrave"}] match (c:Person)-[:HAS_CHILD]->(f:Person) where c.name='Roy Redgrave' with c, f match (m:Person)-[:ACTED_IN]->(film:Film)<-[:WROTE_MUSIC_FOR]-(p:Person) where m.name=f.name return c.name, p.name; [{"c.name":"Roy Redgrave","p.name":"John Williams"}] diff --git a/test/resource/unit_test/with/cypher/with.test b/test/resource/unit_test/with/cypher/with.test index aaafee9b11..592224b5b6 100644 --- a/test/resource/unit_test/with/cypher/with.test +++ b/test/resource/unit_test/with/cypher/with.test @@ -3,7 +3,7 @@ match (n {name:'Liam Neeson'}) with n as n1 match (n {name:'Dennis Quaid'}) with match (n {name:'Liam Neeson'}) with n as n1 match (n {name:'Dennis Quaid'}) with n as n2, n1.name as n1name match (n {name:'John Williams'}) return n,n2,n1name; match (n {name:'Liam Neeson'}) with n return n; match (n {name:'Liam Neeson'}) with n match (n)-->(m) return n,m; -#match (n {name:'London'}) with n optional match (n)-->(m) return n,m; +match (n {name:'London'}) with n optional match (n)-->(m) return n,m; match (a {name:'Liam Neeson'})-[r]->(b) with b match (b)-[]->(c) return c; match (a {name:'Liam Neeson'}),(b {name:'London'}) with a, b match (c:Film) return a,b,c; match (n {name:'Liam Neeson'}) with n match (n) return n.name; @@ -11,7 +11,7 @@ match (a {name:'Liam Neeson'}), (b {name:'London'}) with a, b match (a), (b) ret MATCH (a {name:'Liam Neeson'})-[r]->(b) RETURN a,count(b) AS out_num; MATCH (a {name:'Liam Neeson'})-[r]->(b) WITH a,count(b) AS out_num MATCH (a)<-[]-(c) RETURN count(c) AS in_num,out_num; match (a {name:'Liam Neeson'})-[r]->(b) with a,b match (b)-[]->(c) return a,b,c; -#match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}),(n)-[r]->(m) return r,type(r); +match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}),(n)-[r]->(m) return r,type(r); match (n {name:'Liam Neeson'}),(m {name:'Natasha Richardson'}) with n,m match (n)-[r]->(m) return r,type(r); match (n {name:'Liam Neeson'}),(m {name:'Liam Neeson'}) with n,m optional match (n)-[r]->(m) return r,type(r); match (n {name:'Liam Neeson'})-[r]->(m) with r return r,type(r); @@ -37,11 +37,11 @@ WITH 'Vanessa Redgrave' AS varName MATCH (n {name:varName}) RETURN n; MATCH (n {birthyear:1952}) WITH n,n.name AS varName MATCH (m {name:varName}) RETURN n,m; WITH 1 AS a MATCH (n:City) RETURN DISTINCT a,n; MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film)<-[:ACTED_IN]-(coactor) RETURN m.name,film,coactor; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film RETURN m.name,film; -#MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film OPTIONAL MATCH (film)<-[:WROTE_MUSIC_FOR]-(musician) RETURN m.name,film,musician; +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) RETURN m.name,film; +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film)<-[:ACTED_IN]-(coactor) RETURN m.name,film,coactor; +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film RETURN m.name,film; +MATCH (n {name:'Rachel Kempson'})-[]->(m:Person) WITH m OPTIONAL MATCH (m)-[:ACTED_IN]->(film) WITH m,film OPTIONAL MATCH (film)<-[:WROTE_MUSIC_FOR]-(musician) RETURN m.name,film,musician; match (n:Person) where n.name='Michael Redgrave' with n.birthyear as nb match (p)-[:HAS_CHILD]->(c) where p.birthyear=nb return c.name; -#match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; -#match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with n, collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; +match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; +match (n:Person) where n.name='Roy Redgrave' or n.name='Michael Redgrave' with n, collect(id(n)) as cn match (p:Person) where id(p) in cn return p.name; match (c:Person)-[:HAS_CHILD]->(f:Person) where c.name='Roy Redgrave' with c, f match (m:Person)-[:ACTED_IN]->(film:Film)<-[:WROTE_MUSIC_FOR]-(p:Person) where m.name=f.name return c.name, p.name; \ No newline at end of file diff --git a/test/test_cypher_field_data.cpp b/test/test_cypher_field_data.cpp new file mode 100644 index 0000000000..7302b8d75d --- /dev/null +++ b/test/test_cypher_field_data.cpp @@ -0,0 +1,91 @@ +/** + * Copyright 2022 AntGroup CO., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + */ + +#include "core/data_type.h" +#include "gtest/gtest.h" +#include "./ut_utils.h" +#include "cypher/cypher_types.h" + +std::vector<::lgraph::FieldData> g_list = { + lgraph::FieldData(1), lgraph::FieldData(2), lgraph::FieldData(3), + lgraph::FieldData(4), lgraph::FieldData(5), +}; + +std::unordered_map g_map = {{"a", lgraph::FieldData(1)}, + {"b", lgraph::FieldData(2)}, + {"c", lgraph::FieldData(3)}, + {"d", lgraph::FieldData(4)}, + {"e", lgraph::FieldData(5)}}; + +class TestCypherFieldData : public TuGraphTest {}; + +TEST_F(TestCypherFieldData, DataType) { + cypher::FieldData default_data; + UT_EXPECT_TRUE(default_data.type == cypher::FieldData::FieldType::SCALAR); + + cypher::FieldData sdata(lgraph::FieldData("test")); + UT_EXPECT_TRUE(sdata.type == cypher::FieldData::FieldType::SCALAR); + + cypher::FieldData ldata(g_list); + UT_EXPECT_TRUE(ldata.type == cypher::FieldData::FieldType::ARRAY); + + cypher::FieldData ldata_move(std::vector<::lgraph::FieldData>{ + lgraph::FieldData("A"), + lgraph::FieldData("B"), + lgraph::FieldData("C"), + lgraph::FieldData("D"), + lgraph::FieldData("E"), + }); + UT_EXPECT_TRUE(ldata_move.type == cypher::FieldData::FieldType::ARRAY); + + cypher::FieldData mdata(g_map); + UT_EXPECT_TRUE(mdata.type == cypher::FieldData::FieldType::MAP); + + cypher::FieldData mdata_move( + std::unordered_map{{"Z", lgraph::FieldData(9)}, + {"Y", lgraph::FieldData(8)}, + {"X", lgraph::FieldData(7)}, + {"W", lgraph::FieldData(6)}, + {"V", lgraph::FieldData(5)}}); + UT_EXPECT_TRUE(mdata_move.type == cypher::FieldData::FieldType::MAP); + + { + cypher::FieldData sdata_copy(sdata); + UT_EXPECT_TRUE(sdata_copy.type == cypher::FieldData::FieldType::SCALAR); + UT_EXPECT_TRUE(sdata_copy == sdata); + cypher::FieldData sdata_move(std::move(sdata_copy)); + UT_EXPECT_TRUE(sdata_move.type == cypher::FieldData::FieldType::SCALAR); + UT_EXPECT_TRUE(sdata_move == sdata); + } + + { + cypher::FieldData ldata_copy(ldata); + UT_EXPECT_TRUE(ldata_copy.type == cypher::FieldData::FieldType::ARRAY); + UT_EXPECT_ANY_THROW(ldata_copy == ldata); + cypher::FieldData ldata_move(std::move(ldata_copy)); + UT_EXPECT_TRUE(ldata_move.type == cypher::FieldData::FieldType::ARRAY); + UT_EXPECT_TRUE(ldata_copy.type == cypher::FieldData::FieldType::SCALAR); + UT_EXPECT_ANY_THROW(ldata_move == ldata); + } + + { + cypher::FieldData mdata_copy(mdata); + UT_EXPECT_TRUE(mdata_copy.type == cypher::FieldData::FieldType::MAP); + UT_EXPECT_ANY_THROW(mdata_copy == mdata); + cypher::FieldData mdata_move(std::move(mdata_copy)); + UT_EXPECT_TRUE(mdata_move.type == cypher::FieldData::FieldType::MAP); + UT_EXPECT_TRUE(mdata_copy.type == cypher::FieldData::FieldType::SCALAR); + UT_EXPECT_ANY_THROW(mdata_copy == mdata); + } +} diff --git a/test/test_cypher_v2.cpp b/test/test_cypher_v2.cpp index e31152aa27..7ca2a051a4 100644 --- a/test/test_cypher_v2.cpp +++ b/test/test_cypher_v2.cpp @@ -33,6 +33,7 @@ #include "cypher/parser/cypher_base_visitor_v2.h" #include "cypher/parser/cypher_error_listener.h" #include "cypher/rewriter/GenAnonymousAliasRewriter.h" +#include "cypher/rewriter/MultiPathPatternRewriter.h" #include "fma-common/file_system.h" #include "db/galaxy.h" #include "cypher/execution_plan/runtime_context.h" @@ -143,7 +144,7 @@ class TestCypherV2 : public TuGraphTest { UT_DBG() << dumper.dump(); } cypher::ExecutionPlanV2 execution_plan_v2; - ret = execution_plan_v2.Build(node); + ret = execution_plan_v2.Build(node, ctx_.get()); if (ret != GEAXErrorCode::GEAX_SUCCEED) { UT_LOG() << "build execution_plan_v2 failed: " << execution_plan_v2.ErrorMsg(); result = execution_plan_v2.ErrorMsg(); @@ -176,6 +177,8 @@ class TestCypherV2 : public TuGraphTest { // rewrite ast cypher::GenAnonymousAliasRewriter gen_anonymous_alias_rewriter; node->accept(gen_anonymous_alias_rewriter); + cypher::MultiPathPatternRewriter multi_path_pattern_rewriter(objAlloc_); + node->accept(multi_path_pattern_rewriter); // dump AstDumper dumper; auto ret = dumper.handle(node); @@ -191,7 +194,7 @@ class TestCypherV2 : public TuGraphTest { } LOG_INFO() << "------------------------- " << __FILE__ << " " << __LINE__; cypher::ExecutionPlanV2 execution_plan_v2; - ret = execution_plan_v2.Build(node); + ret = execution_plan_v2.Build(node, ctx_.get()); if (ret != GEAXErrorCode::GEAX_SUCCEED) { UT_LOG() << "build execution_plan_v2 failed: " << execution_plan_v2.ErrorMsg(); result = execution_plan_v2.ErrorMsg(); @@ -202,7 +205,7 @@ class TestCypherV2 : public TuGraphTest { } catch (std::exception& e) { UT_LOG() << e.what(); result = e.what(); - return false; + return true; } UT_LOG() << "-----result-----"; result = ctx_->result_->Dump(false); @@ -211,7 +214,7 @@ class TestCypherV2 : public TuGraphTest { } catch (std::exception& e) { UT_LOG() << e.what(); result = e.what(); - return false; + return true; } return true; } @@ -356,12 +359,12 @@ TEST_F(TestCypherV2, TestHint) { test_files(dir); } -// TEST_F(TestCypherV2, TestMultiMatch) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/multi_match/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestMultiMatch) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/multi_match/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestOptionalMatch) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -384,12 +387,12 @@ TEST_F(TestCypherV2, TestHint) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestParameter) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/parameter/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestParameter) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/parameter/cypher"; + test_files(dir); +} TEST_F(TestCypherV2, TestVarLenEdge) { set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -419,12 +422,12 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestWith) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/with/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestWith) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/with/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestListComprehension) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -440,12 +443,12 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestUnwind) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/unwind/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestUnwind) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/unwind/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestProcedure) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -454,12 +457,12 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestAdd) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/add/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestAdd) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/add/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestSet) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); @@ -468,12 +471,12 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestDelete) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/delete/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestDelete) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/delete/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestRemove) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -489,26 +492,26 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestMerge) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/merge/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestMerge) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/merge/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestCreatYago) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/create_yago/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestCreatYago) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/create_yago/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestAggregate) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/aggregate/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestAggregate) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/aggregate/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestAlgo) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); @@ -517,26 +520,26 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestTopn) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/topn/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestTopn) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/topn/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestLdbcSnb) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/ldbc_snb/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestLdbcSnb) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/ldbc_snb/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestOpt) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/opt/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestOpt) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/opt/cypher"; + test_files(dir); +} // TEST_F(TestCypherV2, TestFixCrashIssues) { // set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); @@ -552,16 +555,16 @@ TEST_F(TestCypherV2, TestFuncFilter) { // test_files(dir); // } -// TEST_F(TestCypherV2, TestCreateLabel) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/create_label/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestCreateLabel) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::EMPTY); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/create_label/cypher"; + test_files(dir); +} -// TEST_F(TestCypherV2, TestEdgeIdQuery) { -// set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); -// set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); -// std::string dir = test_suite_dir_ + "/edge_id_query/cypher"; -// test_files(dir); -// } +TEST_F(TestCypherV2, TestEdgeIdQuery) { + set_graph_type(GraphFactory::GRAPH_DATASET_TYPE::YAGO); + set_query_type(lgraph::ut::QUERY_TYPE::NEWCYPHER); + std::string dir = test_suite_dir_ + "/edge_id_query/cypher"; + test_files(dir); +} diff --git a/test/test_query.cpp b/test/test_query.cpp index 0a610f2fd8..d6ad5a523f 100644 --- a/test/test_query.cpp +++ b/test/test_query.cpp @@ -139,7 +139,7 @@ class TestQuery : public TuGraphTest { UT_DBG() << dumper.dump(); } cypher::ExecutionPlanV2 execution_plan_v2; - ret = execution_plan_v2.Build(node); + ret = execution_plan_v2.Build(node, ctx_.get()); if (ret != GEAXErrorCode::GEAX_SUCCEED) { UT_LOG() << "build execution_plan_v2 failed: " << execution_plan_v2.ErrorMsg(); result = execution_plan_v2.ErrorMsg();