* Convert a quantified edge pattern to a RepeatStep. * For variable-length paths, we need to repeat both the edge traversal and vertex traversal.
(
baseStep: EdgeStep,
quantifier: Quantifier,
destNodeVariable?: string,
edgeProperties?: { [key: string]: any },
)
| 1698 | * For variable-length paths, we need to repeat both the edge traversal and vertex traversal. |
| 1699 | */ |
| 1700 | function convertQuantifiedEdge( |
| 1701 | baseStep: EdgeStep, |
| 1702 | quantifier: Quantifier, |
| 1703 | destNodeVariable?: string, |
| 1704 | edgeProperties?: { [key: string]: any }, |
| 1705 | ): RepeatStep<Step<any>[]> { |
| 1706 | const { min, max } = quantifier; |
| 1707 | |
| 1708 | // Build the steps for each iteration |
| 1709 | const iterationSteps: Step<any>[] = [baseStep]; |
| 1710 | |
| 1711 | // Add filter for edge properties if specified |
| 1712 | if (edgeProperties && Object.keys(edgeProperties).length > 0) { |
| 1713 | const propertyConditions = Object.entries(edgeProperties).map( |
| 1714 | ([key, value]) => ["=", key, value] as StepCondition, |
| 1715 | ); |
| 1716 | const condition: StepCondition = |
| 1717 | propertyConditions.length === 1 |
| 1718 | ? propertyConditions[0]! |
| 1719 | : (["and", ...propertyConditions] as StepCondition); |
| 1720 | iterationSteps.push(new FilterElementsStep({ condition })); |
| 1721 | } |
| 1722 | |
| 1723 | // For variable-length paths, we need to traverse edge -> vertex repeatedly |
| 1724 | // The vertex step should use the same direction as the edge step to continue in that direction |
| 1725 | const vertexStep = new VertexStep({ |
| 1726 | direction: baseStep.config.direction, |
| 1727 | edgeLabels: [], |
| 1728 | }); |
| 1729 | iterationSteps.push(vertexStep); |
| 1730 | |
| 1731 | const stepLabels = destNodeVariable ? [destNodeVariable] : undefined; |
| 1732 | |
| 1733 | // Handle zero-min case: {0,n} or {0} includes the starting node |
| 1734 | const emitInput = min === 0; |
| 1735 | // emitStart is 1-indexed (iteration 1 = 1 hop), so set it to min (but at least 1) |
| 1736 | const effectiveMin = min ?? 1; |
| 1737 | const emitStart = effectiveMin > 0 ? effectiveMin : 1; |
| 1738 | |
| 1739 | // Exact count: *2 or {2} means exactly 2 hops |
| 1740 | if (max !== undefined && effectiveMin === max) { |
| 1741 | // For exact count with zero min, we need emit to also get the input |
| 1742 | if (emitInput) { |
| 1743 | return new RepeatStep({ times: max, stepLabels, emitInput }, iterationSteps); |
| 1744 | } |
| 1745 | return new RepeatStep({ times: max, stepLabels }, iterationSteps); |
| 1746 | } |
| 1747 | |
| 1748 | // Range: *1..3 or {1,3} means 1 to 3 hops (with emitStart respecting min) |
| 1749 | if (max !== undefined) { |
| 1750 | return new RepeatStep( |
| 1751 | { times: max, emit: true, emitStart, emitInput, stepLabels }, |
| 1752 | iterationSteps, |
| 1753 | ); |
| 1754 | } |
| 1755 | |
| 1756 | // Open-ended: *2.. or {2,} means 2 or more hops |
| 1757 | // Use a large number for open-ended paths |
no test coverage detected