From 9ad863adef2388ee64bd5e4b70c7bb39ea9ff970 Mon Sep 17 00:00:00 2001 From: andyfeng Date: Thu, 25 Apr 2024 16:36:33 -0400 Subject: [PATCH] Fix issue 3262 (#3384) --- .../operator/extend/extend_direction.h | 4 +- .../recursive_extend/frontier_scanner.h | 8 +- .../recursive_extend/recursive_join.h | 82 +++++++++++-------- src/processor/map/map_recursive_extend.cpp | 55 +++++++------ .../recursive_extend/frontier_scanner.cpp | 41 +++++++--- .../recursive_extend/recursive_join.cpp | 44 ++++++---- test/test_files/issue/issue4.test | 56 ++++++++++++- 7 files changed, 193 insertions(+), 97 deletions(-) diff --git a/src/include/planner/operator/extend/extend_direction.h b/src/include/planner/operator/extend/extend_direction.h index 0541cef388..e53fb57ce3 100644 --- a/src/include/planner/operator/extend/extend_direction.h +++ b/src/include/planner/operator/extend/extend_direction.h @@ -11,7 +11,7 @@ namespace planner { enum class ExtendDirection : uint8_t { FWD = 0, BWD = 1, BOTH = 2 }; struct ExtendDirectionUtils { - static inline ExtendDirection getExtendDirection(const binder::RelExpression& relExpression, + static ExtendDirection getExtendDirection(const binder::RelExpression& relExpression, const binder::NodeExpression& boundNode) { if (relExpression.getDirectionType() == binder::RelDirectionType::BOTH) { return ExtendDirection::BOTH; @@ -23,7 +23,7 @@ struct ExtendDirectionUtils { } } - static inline common::RelDataDirection getRelDataDirection(ExtendDirection extendDirection) { + static common::RelDataDirection getRelDataDirection(ExtendDirection extendDirection) { KU_ASSERT(extendDirection != ExtendDirection::BOTH); return extendDirection == ExtendDirection::FWD ? common::RelDataDirection::FWD : common::RelDataDirection::BWD; diff --git a/src/include/processor/operator/recursive_extend/frontier_scanner.h b/src/include/processor/operator/recursive_extend/frontier_scanner.h index 6ead04e735..3cadecd282 100644 --- a/src/include/processor/operator/recursive_extend/frontier_scanner.h +++ b/src/include/processor/operator/recursive_extend/frontier_scanner.h @@ -63,9 +63,9 @@ class PathScanner : public BaseFrontierScanner { public: PathScanner(TargetDstNodes* targetDstNodes, size_t k, std::unordered_map tableIDToName, - path_semantic_check_t semanticCheckFunc) + path_semantic_check_t semanticCheckFunc, bool extendInBwd) : BaseFrontierScanner{targetDstNodes, k}, tableIDToName{std::move(tableIDToName)}, - semanticCheckFunc{semanticCheckFunc} { + semanticCheckFunc{semanticCheckFunc}, extendInBwd{extendInBwd} { nodeIDs.resize(k + 1); relIDs.resize(k + 1); } @@ -97,8 +97,10 @@ class PathScanner : public BaseFrontierScanner { std::stack nbrsStack; std::stack cursorStack; std::unordered_map tableIDToName; - + // Path semantic path_semantic_check_t semanticCheckFunc; + // Extend direction + bool extendInBwd; }; /* diff --git a/src/include/processor/operator/recursive_extend/recursive_join.h b/src/include/processor/operator/recursive_extend/recursive_join.h index 72c1eceaae..48f8e34574 100644 --- a/src/include/processor/operator/recursive_extend/recursive_join.h +++ b/src/include/processor/operator/recursive_extend/recursive_join.h @@ -3,6 +3,7 @@ #include "bfs_state.h" #include "common/enums/query_rel_type.h" #include "frontier_scanner.h" +#include "planner/operator/extend/extend_direction.h" #include "planner/operator/extend/recursive_join_type.h" #include "processor/operator/mask.h" #include "processor/operator/physical_operator.h" @@ -35,25 +36,21 @@ struct RecursiveJoinDataInfo { DataPos pathPos; std::unordered_map tableIDToName; - RecursiveJoinDataInfo(const DataPos& srcNodePos, const DataPos& dstNodePos, - std::unordered_set dstNodeTableIDs, const DataPos& pathLengthPos, - std::unique_ptr localResultSetDescriptor, - const DataPos& recursiveDstNodeIDPos, - std::unordered_set recursiveDstNodeTableIDs, - const DataPos& recursiveEdgeIDPos, const DataPos& pathPos, - std::unordered_map tableIDToName) - : srcNodePos{srcNodePos}, dstNodePos{dstNodePos}, - dstNodeTableIDs{std::move(dstNodeTableIDs)}, pathLengthPos{pathLengthPos}, - localResultSetDescriptor{std::move(localResultSetDescriptor)}, - recursiveDstNodeIDPos{recursiveDstNodeIDPos}, - recursiveDstNodeTableIDs{std::move(recursiveDstNodeTableIDs)}, - recursiveEdgeIDPos{recursiveEdgeIDPos}, pathPos{pathPos}, - tableIDToName{std::move(tableIDToName)} {} - - inline std::unique_ptr copy() { - return std::make_unique(srcNodePos, dstNodePos, dstNodeTableIDs, - pathLengthPos, localResultSetDescriptor->copy(), recursiveDstNodeIDPos, - recursiveDstNodeTableIDs, recursiveEdgeIDPos, pathPos, tableIDToName); + RecursiveJoinDataInfo() = default; + EXPLICIT_COPY_DEFAULT_MOVE(RecursiveJoinDataInfo); + +private: + RecursiveJoinDataInfo(const RecursiveJoinDataInfo& other) { + srcNodePos = other.srcNodePos; + dstNodePos = other.dstNodePos; + dstNodeTableIDs = other.dstNodeTableIDs; + pathLengthPos = other.pathLengthPos; + localResultSetDescriptor = other.localResultSetDescriptor->copy(); + recursiveDstNodeIDPos = other.recursiveDstNodeIDPos; + recursiveDstNodeTableIDs = other.recursiveDstNodeTableIDs; + recursiveEdgeIDPos = other.recursiveEdgeIDPos; + pathPos = other.pathPos; + tableIDToName = other.tableIDToName; } }; @@ -75,29 +72,47 @@ struct RecursiveJoinVectors { common::ValueVector* recursiveDstNodeIDVector = nullptr; }; +struct RecursiveJoinInfo { + RecursiveJoinDataInfo dataInfo; + uint8_t lowerBound; + uint8_t upperBound; + common::QueryRelType queryRelType; + planner::RecursiveJoinType joinType; + planner::ExtendDirection direction; + + RecursiveJoinInfo() = default; + EXPLICIT_COPY_DEFAULT_MOVE(RecursiveJoinInfo); + +private: + RecursiveJoinInfo(const RecursiveJoinInfo& other) { + dataInfo = other.dataInfo.copy(); + lowerBound = other.lowerBound; + upperBound = other.upperBound; + queryRelType = other.queryRelType; + joinType = other.joinType; + direction = other.direction; + } +}; + class RecursiveJoin : public PhysicalOperator { public: - RecursiveJoin(uint8_t lowerBound, uint8_t upperBound, common::QueryRelType queryRelType, - planner::RecursiveJoinType joinType, std::shared_ptr sharedState, - std::unique_ptr dataInfo, std::unique_ptr child, - uint32_t id, const std::string& paramsString, + RecursiveJoin(RecursiveJoinInfo info, std::shared_ptr sharedState, + std::unique_ptr child, uint32_t id, const std::string& paramsString, std::unique_ptr recursiveRoot) : PhysicalOperator{PhysicalOperatorType::RECURSIVE_JOIN, std::move(child), id, paramsString}, - lowerBound{lowerBound}, upperBound{upperBound}, queryRelType{queryRelType}, - joinType{joinType}, sharedState{std::move(sharedState)}, dataInfo{std::move(dataInfo)}, + info{std::move(info)}, sharedState{std::move(sharedState)}, recursiveRoot{std::move(recursiveRoot)} {} - inline RecursiveJoinSharedState* getSharedState() const { return sharedState.get(); } + RecursiveJoinSharedState* getSharedState() const { return sharedState.get(); } void initLocalStateInternal(ResultSet* resultSet_, ExecutionContext* context) final; bool getNextTuplesInternal(ExecutionContext* context) final; - inline std::unique_ptr clone() final { - return std::make_unique(lowerBound, upperBound, queryRelType, joinType, - sharedState, dataInfo->copy(), children[0]->clone(), id, paramsString, - recursiveRoot->clone()); + std::unique_ptr clone() final { + return std::make_unique(info.copy(), sharedState, children[0]->clone(), id, + paramsString, recursiveRoot->clone()); } private: @@ -113,13 +128,8 @@ class RecursiveJoin : public PhysicalOperator { void updateVisitedNodes(common::nodeID_t boundNodeID); private: - uint8_t lowerBound; - uint8_t upperBound; - common::QueryRelType queryRelType; - planner::RecursiveJoinType joinType; - + RecursiveJoinInfo info; std::shared_ptr sharedState; - std::unique_ptr dataInfo; // Local recursive plan std::unique_ptr localResultSet; diff --git a/src/processor/map/map_recursive_extend.cpp b/src/processor/map/map_recursive_extend.cpp index 135823c696..3455660166 100644 --- a/src/processor/map/map_recursive_extend.cpp +++ b/src/processor/map/map_recursive_extend.cpp @@ -20,48 +20,51 @@ static std::shared_ptr createSharedState( return std::make_shared(std::move(semiMasks)); } -std::unique_ptr PlanMapper::mapRecursiveExtend( - planner::LogicalOperator* logicalOperator) { - auto extend = (LogicalRecursiveExtend*)logicalOperator; +std::unique_ptr PlanMapper::mapRecursiveExtend(LogicalOperator* logicalOperator) { + auto extend = logicalOperator->constPtrCast(); auto boundNode = extend->getBoundNode(); auto nbrNode = extend->getNbrNode(); auto rel = extend->getRel(); auto recursiveInfo = rel->getRecursiveInfo(); - auto lengthExpression = rel->getLengthExpression(); // Map recursive plan auto logicalRecursiveRoot = extend->getRecursiveChild(); auto recursiveRoot = mapOperator(logicalRecursiveRoot.get()); auto recursivePlanSchema = logicalRecursiveRoot->getSchema(); - auto recursivePlanResultSetDescriptor = - std::make_unique(recursivePlanSchema); - auto recursiveDstNodeIDPos = - DataPos(recursivePlanSchema->getExpressionPos(*recursiveInfo->nodeCopy->getInternalID())); - auto recursiveEdgeIDPos = DataPos( - recursivePlanSchema->getExpressionPos(*recursiveInfo->rel->getInternalIDProperty())); // Generate RecursiveJoin auto outSchema = extend->getSchema(); auto inSchema = extend->getChild(0)->getSchema(); - auto boundNodeIDPos = DataPos(inSchema->getExpressionPos(*boundNode->getInternalID())); - auto nbrNodeIDPos = DataPos(outSchema->getExpressionPos(*nbrNode->getInternalID())); - auto lengthPos = DataPos(outSchema->getExpressionPos(*lengthExpression)); auto sharedState = createSharedState(*nbrNode, *clientContext->getStorageManager()); - auto pathPos = DataPos(); - if (extend->getJoinType() == planner::RecursiveJoinType::TRACK_PATH) { - pathPos = DataPos(outSchema->getExpressionPos(*rel)); + // Data info + auto dataInfo = RecursiveJoinDataInfo(); + dataInfo.srcNodePos = getDataPos(*boundNode->getInternalID(), *inSchema); + dataInfo.dstNodePos = getDataPos(*nbrNode->getInternalID(), *outSchema); + dataInfo.dstNodeTableIDs = nbrNode->getTableIDsSet(); + dataInfo.pathLengthPos = getDataPos(*rel->getLengthExpression(), *outSchema); + dataInfo.localResultSetDescriptor = std::make_unique(recursivePlanSchema); + dataInfo.recursiveDstNodeIDPos = + getDataPos(*recursiveInfo->nodeCopy->getInternalID(), *recursivePlanSchema); + dataInfo.recursiveDstNodeTableIDs = recursiveInfo->node->getTableIDsSet(); + dataInfo.recursiveEdgeIDPos = + getDataPos(*recursiveInfo->rel->getInternalIDProperty(), *recursivePlanSchema); + if (extend->getJoinType() == RecursiveJoinType::TRACK_PATH) { + dataInfo.pathPos = getDataPos(*rel, *outSchema); + } else { + dataInfo.pathPos = DataPos::getInvalidPos(); } - std::unordered_map tableIDToName; for (auto& entry : clientContext->getCatalog()->getTableEntries(clientContext->getTx())) { - tableIDToName.insert({entry->getTableID(), entry->getName()}); + dataInfo.tableIDToName.insert({entry->getTableID(), entry->getName()}); } - auto dataInfo = std::make_unique(boundNodeIDPos, nbrNodeIDPos, - nbrNode->getTableIDsSet(), lengthPos, std::move(recursivePlanResultSetDescriptor), - recursiveDstNodeIDPos, recursiveInfo->node->getTableIDsSet(), recursiveEdgeIDPos, pathPos, - std::move(tableIDToName)); + // Info + auto info = RecursiveJoinInfo(); + info.dataInfo = std::move(dataInfo); + info.lowerBound = rel->getLowerBound(); + info.upperBound = rel->getUpperBound(); + info.queryRelType = rel->getRelType(); + info.joinType = extend->getJoinType(); + info.direction = extend->getDirection(); auto prevOperator = mapOperator(logicalOperator->getChild(0).get()); - return std::make_unique(rel->getLowerBound(), rel->getUpperBound(), - rel->getRelType(), extend->getJoinType(), sharedState, std::move(dataInfo), - std::move(prevOperator), getOperatorID(), extend->getExpressionsForPrinting(), - std::move(recursiveRoot)); + return std::make_unique(std::move(info), sharedState, std::move(prevOperator), + getOperatorID(), extend->getExpressionsForPrinting(), std::move(recursiveRoot)); } } // namespace processor diff --git a/src/processor/operator/recursive_extend/frontier_scanner.cpp b/src/processor/operator/recursive_extend/frontier_scanner.cpp index c4f8d231ca..57ccc2e05b 100644 --- a/src/processor/operator/recursive_extend/frontier_scanner.cpp +++ b/src/processor/operator/recursive_extend/frontier_scanner.cpp @@ -122,18 +122,29 @@ void PathScanner::initDfs(const frontier::node_rel_id_t& nodeAndRelID, size_t cu initDfs(nbrs->at(0), currentDepth - 1); } +static void writePathRels(RecursiveJoinVectors* vectors, sel_t pos, nodeID_t srcNodeID, + nodeID_t dstNodeID, relID_t relID, const std::string& labelName) { + vectors->pathRelsSrcIDDataVector->setValue(pos, srcNodeID); + vectors->pathRelsDstIDDataVector->setValue(pos, dstNodeID); + vectors->pathRelsIDDataVector->setValue(pos, relID); + StringVector::addString(vectors->pathRelsLabelDataVector, pos, labelName); +} + void PathScanner::writePathToVector(RecursiveJoinVectors* vectors, sel_t& vectorPos, sel_t& nodeIDDataVectorPos, sel_t& relIDDataVectorPos) { if (semanticCheckFunc && !semanticCheckFunc(nodeIDs, relIDs)) { return; } KU_ASSERT(vectorPos < DEFAULT_VECTOR_CAPACITY); + // Allocate list entries. auto nodeTableEntry = ListVector::addList(vectors->pathNodesVector, k > 0 ? k - 1 : 0); auto relTableEntry = ListVector::addList(vectors->pathRelsVector, k); vectors->pathNodesVector->setValue(vectorPos, nodeTableEntry); vectors->pathRelsVector->setValue(vectorPos, relTableEntry); + // Write dst writeDstNodeOffsetAndLength(vectors->dstNodeIDVector, vectors->pathLengthVector, vectorPos); vectorPos++; + // Write path nodes. for (auto i = 1u; i < k; ++i) { auto nodeID = nodeIDs[i]; vectors->pathNodesIDDataVector->setValue(nodeIDDataVectorPos, nodeID); @@ -142,17 +153,25 @@ void PathScanner::writePathToVector(RecursiveJoinVectors* vectors, sel_t& vector labelName.data(), labelName.length()); nodeIDDataVectorPos++; } - for (auto i = 0u; i < k; ++i) { - auto srcNodeID = nodeIDs[i]; - auto dstNodeID = nodeIDs[i + 1]; - vectors->pathRelsSrcIDDataVector->setValue(relIDDataVectorPos, srcNodeID); - vectors->pathRelsDstIDDataVector->setValue(relIDDataVectorPos, dstNodeID); - auto relID = relIDs[i]; - vectors->pathRelsIDDataVector->setValue(relIDDataVectorPos, relID); - auto labelName = tableIDToName.at(relID.tableID); - StringVector::addString(vectors->pathRelsLabelDataVector, relIDDataVectorPos, - labelName.data(), labelName.length()); - relIDDataVectorPos++; + // Write path rels. + if (extendInBwd) { + for (auto i = 0u; i < k; ++i) { + auto srcNodeID = nodeIDs[i + 1]; + auto dstNodeID = nodeIDs[i]; + auto relID = relIDs[i]; + writePathRels(vectors, relIDDataVectorPos, srcNodeID, dstNodeID, relID, + tableIDToName.at(relID.tableID)); + relIDDataVectorPos++; + } + } else { + for (auto i = 0u; i < k; ++i) { + auto srcNodeID = nodeIDs[i]; + auto dstNodeID = nodeIDs[i + 1]; + auto relID = relIDs[i]; + writePathRels(vectors, relIDDataVectorPos, srcNodeID, dstNodeID, relID, + tableIDToName.at(relID.tableID)); + relIDDataVectorPos++; + } } } diff --git a/src/processor/operator/recursive_extend/recursive_join.cpp b/src/processor/operator/recursive_extend/recursive_join.cpp index 764e07408c..80d9291cb9 100644 --- a/src/processor/operator/recursive_extend/recursive_join.cpp +++ b/src/processor/operator/recursive_extend/recursive_join.cpp @@ -6,22 +6,28 @@ #include "processor/operator/recursive_extend/variable_length_state.h" using namespace kuzu::common; +using namespace kuzu::planner; namespace kuzu { namespace processor { -void RecursiveJoin::initLocalStateInternal(ResultSet* /*resultSet_*/, ExecutionContext* context) { +void RecursiveJoin::initLocalStateInternal(ResultSet*, ExecutionContext* context) { + auto& dataInfo = info.dataInfo; populateTargetDstNodes(context); vectors = std::make_unique(); - vectors->srcNodeIDVector = resultSet->getValueVector(dataInfo->srcNodePos).get(); - vectors->dstNodeIDVector = resultSet->getValueVector(dataInfo->dstNodePos).get(); - vectors->pathLengthVector = resultSet->getValueVector(dataInfo->pathLengthPos).get(); + vectors->srcNodeIDVector = resultSet->getValueVector(dataInfo.srcNodePos).get(); + vectors->dstNodeIDVector = resultSet->getValueVector(dataInfo.dstNodePos).get(); + vectors->pathLengthVector = resultSet->getValueVector(dataInfo.pathLengthPos).get(); auto semantic = context->clientContext->getClientConfig()->recursivePatternSemantic; path_semantic_check_t semanticCheck = nullptr; std::vector> scanners; - switch (queryRelType) { + auto joinType = info.joinType; + auto lowerBound = info.lowerBound; + auto upperBound = info.upperBound; + auto extendInBWD = info.direction == ExtendDirection::BWD; + switch (info.queryRelType) { case QueryRelType::VARIABLE_LENGTH: { - switch (joinType) { + switch (info.joinType) { case planner::RecursiveJoinType::TRACK_PATH: { switch (semantic) { case PathSemantic::TRIAL: { @@ -33,12 +39,12 @@ void RecursiveJoin::initLocalStateInternal(ResultSet* /*resultSet_*/, ExecutionC default: semanticCheck = nullptr; } - vectors->pathVector = resultSet->getValueVector(dataInfo->pathPos).get(); + vectors->pathVector = resultSet->getValueVector(dataInfo.pathPos).get(); bfsState = std::make_unique>(upperBound, targetDstNodes.get()); for (auto i = lowerBound; i <= upperBound; ++i) { scanners.push_back(std::make_unique(targetDstNodes.get(), i, - dataInfo->tableIDToName, semanticCheck)); + dataInfo.tableIDToName, semanticCheck, extendInBWD)); } } break; case planner::RecursiveJoinType::TRACK_NONE: { @@ -64,12 +70,12 @@ void RecursiveJoin::initLocalStateInternal(ResultSet* /*resultSet_*/, ExecutionC } switch (joinType) { case planner::RecursiveJoinType::TRACK_PATH: { - vectors->pathVector = resultSet->getValueVector(dataInfo->pathPos).get(); + vectors->pathVector = resultSet->getValueVector(dataInfo.pathPos).get(); bfsState = std::make_unique>(upperBound, targetDstNodes.get()); for (auto i = lowerBound; i <= upperBound; ++i) { scanners.push_back(std::make_unique(targetDstNodes.get(), i, - dataInfo->tableIDToName, nullptr)); + dataInfo.tableIDToName, nullptr, extendInBWD)); } } break; case planner::RecursiveJoinType::TRACK_NONE: { @@ -91,12 +97,12 @@ void RecursiveJoin::initLocalStateInternal(ResultSet* /*resultSet_*/, ExecutionC } switch (joinType) { case planner::RecursiveJoinType::TRACK_PATH: { - vectors->pathVector = resultSet->getValueVector(dataInfo->pathPos).get(); + vectors->pathVector = resultSet->getValueVector(dataInfo.pathPos).get(); bfsState = std::make_unique>(upperBound, targetDstNodes.get()); for (auto i = lowerBound; i <= upperBound; ++i) { scanners.push_back(std::make_unique(targetDstNodes.get(), i, - dataInfo->tableIDToName, nullptr)); + dataInfo.tableIDToName, nullptr, extendInBWD)); } } break; case planner::RecursiveJoinType::TRACK_NONE: { @@ -235,13 +241,14 @@ void RecursiveJoin::initLocalRecursivePlan(ExecutionContext* context) { KU_ASSERT(op->getNumChildren() == 1); op = op->getChild(0); } + auto& dataInfo = info.dataInfo; scanFrontier = (ScanFrontier*)op; - localResultSet = std::make_unique(dataInfo->localResultSetDescriptor.get(), + localResultSet = std::make_unique(dataInfo.localResultSetDescriptor.get(), context->clientContext->getMemoryManager()); vectors->recursiveDstNodeIDVector = - localResultSet->getValueVector(dataInfo->recursiveDstNodeIDPos).get(); + localResultSet->getValueVector(dataInfo.recursiveDstNodeIDPos).get(); vectors->recursiveEdgeIDVector = - localResultSet->getValueVector(dataInfo->recursiveEdgeIDPos).get(); + localResultSet->getValueVector(dataInfo.recursiveEdgeIDPos).get(); recursiveRoot->initLocalState(localResultSet.get(), context); } @@ -264,9 +271,10 @@ void RecursiveJoin::populateTargetDstNodes(ExecutionContext* context) { } } targetDstNodes = std::make_unique(numTargetNodes, std::move(targetNodeIDs)); - for (auto tableID : dataInfo->recursiveDstNodeTableIDs) { - if (!dataInfo->dstNodeTableIDs.contains(tableID)) { - targetDstNodes->setTableIDFilter(dataInfo->dstNodeTableIDs); + auto& dataInfo = info.dataInfo; + for (auto tableID : dataInfo.recursiveDstNodeTableIDs) { + if (!dataInfo.dstNodeTableIDs.contains(tableID)) { + targetDstNodes->setTableIDFilter(dataInfo.dstNodeTableIDs); return; } } diff --git a/test/test_files/issue/issue4.test b/test/test_files/issue/issue4.test index f27f8e8a9b..ef8b4da9d2 100644 --- a/test/test_files/issue/issue4.test +++ b/test/test_files/issue/issue4.test @@ -3,10 +3,64 @@ -- +-CASE 3262 +-STATEMENT CREATE NODE TABLE Exercise (xxx String, PRIMARY KEY (xxx)); +---- ok +-STATEMENT CREATE REL TABLE next (FROM Exercise TO Exercise, xxx String); +---- ok +-STATEMENT CREATE NODE TABLE Source (xxx String, PRIMARY KEY (xxx)); +---- ok +-STATEMENT CREATE NODE TABLE User (xxx String, PRIMARY KEY (xxx)); +---- ok +-STATEMENT CREATE REL TABLE GROUP has (FROM User TO Source, FROM Source TO Exercise, xxx String); +---- ok +-STATEMENT CREATE (ex:Exercise {xxx: "11-ex"}); +---- ok +-STATEMENT CREATE (ex:Exercise {xxx: "12-ex"}); +---- ok +-STATEMENT CREATE (ex:Exercise {xxx: "13-ex"}); +---- ok +-STATEMENT MATCH (ex1:Exercise {xxx: "11-ex"}), (ex2:Exercise {xxx: "12-ex"}) CREATE (ex1)-[:next {xxx: "next"}]->(ex2); +---- ok +-STATEMENT MATCH (ex1:Exercise {xxx: "12-ex"}), (ex2:Exercise {xxx: "13-ex"}) CREATE (ex1)-[:next {xxx: "next"}]->(ex2); +---- ok +-STATEMENT CREATE (src:Source {xxx: "1-src"}); +---- ok +-STATEMENT MATCH (src:Source {xxx: "1-src"}), (ex:Exercise {xxx: "11-ex"}) CREATE (src)-[:has {xxx: "has"}]->(ex); +---- ok +-STATEMENT MATCH (src:Source {xxx: "1-src"}), (ex:Exercise {xxx: "12-ex"}) CREATE (src)-[:has {xxx: "has"}]->(ex); +---- ok +-STATEMENT MATCH (src:Source {xxx: "1-src"}), (ex:Exercise {xxx: "13-ex"}) CREATE (src)-[:has {xxx: "has"}]->(ex); +---- ok +-STATEMENT CREATE (usr:User {xxx: "usr"}); +---- ok +-STATEMENT MATCH (usr:User {xxx: "usr"}), (src:Source {xxx: "1-src"}) CREATE (usr)-[:has {xxx: "has"}]->(src); +---- ok +-STATEMENT CREATE (ex:Exercise {xxx: "21-ex"}); +---- ok +-STATEMENT CREATE (ex:Exercise {xxx: "22-ex"}); +---- ok +-STATEMENT MATCH (ex1:Exercise {xxx: "21-ex"}), (ex2:Exercise {xxx: "22-ex"}) CREATE (ex1)-[:next {xxx: "next"}]->(ex2); +---- ok +-STATEMENT CREATE (src:Source {xxx: "2-src"}); +---- ok +-STATEMENT MATCH (src:Source {xxx: "2-src"}), (ex:Exercise {xxx: "21-ex"}) CREATE (src)-[:has {xxx: "has"}]->(ex); +---- ok +-STATEMENT MATCH (src:Source {xxx: "2-src"}), (ex:Exercise {xxx: "22-ex"}) CREATE (src)-[:has {xxx: "has"}]->(ex); +---- ok +-STATEMENT MATCH (usr:User {xxx: "usr"}), (src:Source {xxx: "2-src"}) CREATE (usr)-[:has {xxx: "has"}]->(src); +---- ok +-STATEMENT MATCH (usr:User)-[h1:has]->(src:Source {xxx: "1-src"}) RETURN h1; +---- 1 +(3:0)-{_LABEL: has_User_Source, _ID: 5:0, xxx: has}->(2:0) +-STATEMENT MATCH (usr:User)-[h1:has*1..1]->(src:Source {xxx: "1-src"}) RETURN rels(h1)[1]; +---- 1 +(3:0)-{_LABEL: has_User_Source, _ID: 5:0, xxx: has}->(2:0) + -CASE 3083 -STATEMENT CREATE NODE TABLE test ( prop0 STRING, prop1 STRING[], prop2 STRING, prop3 INT64, prop4 STRING, PRIMARY KEY(prop4) ) ---- ok --STATEMENT MERGE (n:test { prop4: "efwb2143d10ccfw" }) SET n.prop0 = "efwoj23", n.prop1 = ["eee", "wefwhiihifwe23343", "dmkwlenfwef232323"], n.prop2 = "NOT eee IS NULL AND dmkwlenfwef232323 < '2023-01-10T00:00:00-05:00' AND dmkwlenfwef232323 >= '2022-01-01T00:00:00-05:00'", n.prop3 = 5 RETURN n.prop4 +-STATEMENT MERGE (n:test { prop4: "efwb2143d10ccfw" }) SET n.prop0 = "efwoj23", n.prop1 = ["eee", "wefwhiihifwe23343", "dmkwlenfwef232323"], n.prop2 = "NOT eee IS NULL AND dmkwlenfwef232323 <'2023-01-10T00:00:00-05:00' AND dmkwlenfwef232323 >='2022-01-01T00:00:00-05:00'", n.prop3 = 5 RETURN n.prop4 ---- ok -STATEMENT MERGE (n:test { prop4: "sdnweh2382933228" }) SET n.prop0 = "efwoj23", n.prop1 = ["customer_name", "wefwhiihifwe23343", "dmkwlenfwef232323"], n.prop2 = "NOT customer_name IS NULL AND NOT dmkwlenfwef232323 IS NULL" RETURN n.prop4 ---- ok