| 124 | } |
| 125 | |
| 126 | void Planner::appendRecursiveExtend(const std::shared_ptr<NodeExpression>& boundNode, |
| 127 | const std::shared_ptr<NodeExpression>& nbrNode, const std::shared_ptr<RelExpression>& rel, |
| 128 | ExtendDirection direction, LogicalPlan& plan) { |
| 129 | // GDS pipeline |
| 130 | auto recursiveInfo = rel->getRecursiveInfo(); |
| 131 | // Fill bind data with direction information. This can only be decided at planning time. |
| 132 | auto bindData = recursiveInfo->bindData.get(); |
| 133 | bindData->nodeOutput = nbrNode; |
| 134 | bindData->nodeInput = boundNode; |
| 135 | bindData->extendDirection = direction; |
| 136 | // If we extend from right to left, we need to print path in reverse direction. |
| 137 | bindData->flipPath = *boundNode == *rel->getRightNode(); |
| 138 | auto resultColumns = recursiveInfo->function->getResultColumns(*bindData); |
| 139 | auto recursiveExtend = std::make_shared<LogicalRecursiveExtend>(recursiveInfo->function->copy(), |
| 140 | *recursiveInfo->bindData, resultColumns); |
| 141 | if (recursiveInfo->nodePredicate != nullptr) { |
| 142 | auto p = getNodeSemiMaskPlan(SemiMaskTargetType::RECURSIVE_EXTEND_PATH_NODE, |
| 143 | *recursiveInfo->node, recursiveInfo->nodePredicate); |
| 144 | recursiveExtend->addChild(p.getLastOperator()); |
| 145 | } |
| 146 | recursiveExtend->computeFactorizedSchema(); |
| 147 | auto probePlan = LogicalPlan(); |
| 148 | probePlan.setLastOperator(std::move(recursiveExtend)); |
| 149 | // Scan path node property pipeline |
| 150 | std::shared_ptr<LogicalOperator> pathNodePropertyScanRoot = nullptr; |
| 151 | if (!recursiveInfo->nodeProjectionList.empty()) { |
| 152 | auto pathNodePropertyScanPlan = LogicalPlan(); |
| 153 | createPathNodePropertyScanPlan(recursiveInfo->node, recursiveInfo->nodeProjectionList, |
| 154 | pathNodePropertyScanPlan); |
| 155 | pathNodePropertyScanRoot = pathNodePropertyScanPlan.getLastOperator(); |
| 156 | } |
| 157 | // Scan path rel property pipeline |
| 158 | std::shared_ptr<LogicalOperator> pathRelPropertyScanRoot = nullptr; |
| 159 | if (!recursiveInfo->relProjectionList.empty()) { |
| 160 | auto pathRelPropertyScanPlan = LogicalPlan(); |
| 161 | auto relProperties = recursiveInfo->relProjectionList; |
| 162 | relProperties.push_back(recursiveInfo->rel->getInternalID()); |
| 163 | bool extendFromSource = *boundNode == *rel->getSrcNode(); |
| 164 | createPathRelPropertyScanPlan(recursiveInfo->node, recursiveInfo->nodeCopy, |
| 165 | recursiveInfo->rel, direction, extendFromSource, relProperties, |
| 166 | pathRelPropertyScanPlan); |
| 167 | pathRelPropertyScanRoot = pathRelPropertyScanPlan.getLastOperator(); |
| 168 | } |
| 169 | // Construct path by probing scanned properties |
| 170 | auto pathPropertyProbe = |
| 171 | std::make_shared<LogicalPathPropertyProbe>(rel, probePlan.getLastOperator(), |
| 172 | pathNodePropertyScanRoot, pathRelPropertyScanRoot, RecursiveJoinType::TRACK_PATH); |
| 173 | pathPropertyProbe->direction = direction; |
| 174 | pathPropertyProbe->extendFromLeft = *boundNode == *rel->getLeftNode(); |
| 175 | pathPropertyProbe->pathNodeIDs = recursiveInfo->bindData->pathNodeIDsExpr; |
| 176 | pathPropertyProbe->pathEdgeIDs = recursiveInfo->bindData->pathEdgeIDsExpr; |
| 177 | pathPropertyProbe->computeFactorizedSchema(); |
| 178 | auto transaction = Transaction::Get(*clientContext); |
| 179 | auto extensionRate = |
| 180 | cardinalityEstimator.getExtensionRate(*rel, *boundNode, direction, transaction); |
| 181 | auto resultCard = |
| 182 | cardinalityEstimator.multiply(extensionRate, plan.getLastOperator()->getCardinality()); |
| 183 | pathPropertyProbe->setCardinality(resultCard); |
nothing calls this directly
no test coverage detected