* Convert a MultiPattern (comma-separated patterns like MATCH (a), (b)) into steps. * Creates a Cartesian product of all matching combinations. * * Note: Currently only supports simple node patterns. Patterns with edges * (e.g., MATCH (a)-[:KNOWS]-(b), (c)) are not supported in comma-separated f
( multiPattern: MultiPattern, whereClause?: WhereClause, preservePriorBindings: boolean = false, )
| 1528 | * (e.g., MATCH (a)-[:KNOWS]-(b), (c)) are not supported in comma-separated form. |
| 1529 | */ |
| 1530 | function convertMultiPattern( |
| 1531 | multiPattern: MultiPattern, |
| 1532 | whereClause?: WhereClause, |
| 1533 | preservePriorBindings: boolean = false, |
| 1534 | ): Step<any>[] { |
| 1535 | const steps: Step<any>[] = []; |
| 1536 | const { patterns } = multiPattern; |
| 1537 | |
| 1538 | if (patterns.length === 0) { |
| 1539 | throw new Error("MultiPattern must have at least one pattern"); |
| 1540 | } |
| 1541 | |
| 1542 | // Validate that all patterns are simple node patterns (no edges) |
| 1543 | for (let i = 0; i < patterns.length; i++) { |
| 1544 | const pattern = patterns[i]!; |
| 1545 | if (pattern.elements.length > 1) { |
| 1546 | throw new Error( |
| 1547 | `Comma-separated MATCH patterns only support simple node patterns. ` + |
| 1548 | `Pattern ${i + 1} contains edges which is not supported. ` + |
| 1549 | `Use separate MATCH clauses for patterns with relationships.`, |
| 1550 | ); |
| 1551 | } |
| 1552 | if (pattern.elements[0]?.type !== "NodePattern") { |
| 1553 | throw new Error( |
| 1554 | `Comma-separated MATCH patterns must start with a NodePattern. ` + |
| 1555 | `Pattern ${i + 1} starts with ${pattern.elements[0]?.type ?? "nothing"}.`, |
| 1556 | ); |
| 1557 | } |
| 1558 | } |
| 1559 | |
| 1560 | // Process first pattern normally |
| 1561 | const firstPattern = patterns[0]!; |
| 1562 | const firstNode = firstPattern.elements[0] as NodePattern; |
| 1563 | const firstStepLabels = firstNode.variable ? [firstNode.variable] : undefined; |
| 1564 | |
| 1565 | // Use CartesianFetchStep if we need to preserve bindings from a prior WITH clause |
| 1566 | if (preservePriorBindings) { |
| 1567 | steps.push( |
| 1568 | new CartesianFetchStep({ |
| 1569 | vertexLabels: firstNode.labels.length > 0 ? firstNode.labels : undefined, |
| 1570 | stepLabels: firstStepLabels, |
| 1571 | }), |
| 1572 | ); |
| 1573 | } else { |
| 1574 | steps.push( |
| 1575 | new FetchVerticesStep({ |
| 1576 | vertexLabels: firstNode.labels.length > 0 ? firstNode.labels : undefined, |
| 1577 | stepLabels: firstStepLabels, |
| 1578 | }), |
| 1579 | ); |
| 1580 | } |
| 1581 | |
| 1582 | // If there's a label expression on the first node, add a filter |
| 1583 | if (firstNode.labelExpression) { |
| 1584 | steps.push( |
| 1585 | new FilterElementsStep({ |
| 1586 | condition: convertLabelExpression(firstNode.labelExpression), |
| 1587 | }), |
no test coverage detected