* Convert a CREATE clause into a CreateStep. * Handles both simple node patterns and chain patterns (with relationships). * * Chain patterns like: (t)-[:Contains]->(:Attr {props})-[:IsA]->(s) * Are processed to extract all nodes that need to be created and all edges, * respecting the order so t
(createClause: CreateClause)
| 631 | * respecting the order so that variables created earlier can be referenced later. |
| 632 | */ |
| 633 | function convertCreateClause(createClause: CreateClause): CreateStep { |
| 634 | const vertices: CreateVertexConfig[] = []; |
| 635 | const edges: CreateEdgeConfig[] = []; |
| 636 | |
| 637 | // Track variables assigned to nodes (including generated ones for anonymous nodes) |
| 638 | // Maps from the node object reference to the assigned variable name |
| 639 | const nodeVariables = new Map<CreateNodePattern | CreateVariableRef, string>(); |
| 640 | |
| 641 | // Counter for generating unique anonymous variable names |
| 642 | let anonCounter = 0; |
| 643 | |
| 644 | for (const pattern of createClause.patterns) { |
| 645 | if (pattern.type === "CreateNodePattern") { |
| 646 | // Simple standalone node creation |
| 647 | const nodePattern = pattern as CreateNodePattern; |
| 648 | vertices.push({ |
| 649 | variable: nodePattern.variable, |
| 650 | label: nodePattern.labels[0] || "Node", |
| 651 | properties: convertPropertyMap(nodePattern.properties), |
| 652 | }); |
| 653 | } else if (pattern.type === "CreateChainPattern") { |
| 654 | // Chain pattern: process nodes and edges in sequence |
| 655 | const chain = pattern as CreateChainPattern; |
| 656 | const elements = chain.elements; |
| 657 | |
| 658 | // First pass: assign variables to all nodes and collect nodes to create |
| 659 | // We need to create nodes before edges so that edge endpoints can reference them |
| 660 | for (let i = 0; i < elements.length; i += 2) { |
| 661 | const nodeElement = elements[i] as CreateNodePattern | CreateVariableRef; |
| 662 | |
| 663 | if (nodeElement.type === "CreateVariableRef") { |
| 664 | // Reference to existing node - use its variable |
| 665 | nodeVariables.set(nodeElement, nodeElement.variable); |
| 666 | } else { |
| 667 | // CreateNodePattern - new node to create (with or without labels) |
| 668 | const nodePattern = nodeElement as CreateNodePattern; |
| 669 | // New node to create - assign variable (use existing or generate) |
| 670 | const variable = nodePattern.variable || `__anon_${anonCounter++}`; |
| 671 | nodeVariables.set(nodeElement, variable); |
| 672 | vertices.push({ |
| 673 | variable, |
| 674 | // Use first label if provided, otherwise use empty string for unlabeled nodes |
| 675 | label: nodePattern.labels[0] || "", |
| 676 | properties: convertPropertyMap(nodePattern.properties), |
| 677 | }); |
| 678 | } |
| 679 | } |
| 680 | |
| 681 | // Second pass: collect all edges using assigned variables |
| 682 | for (let i = 1; i < elements.length; i += 2) { |
| 683 | const edgeElement = elements[i] as CreateEdgePattern; |
| 684 | const prevNode = elements[i - 1] as CreateNodePattern | CreateVariableRef; |
| 685 | const nextNode = elements[i + 1] as CreateNodePattern | CreateVariableRef; |
| 686 | |
| 687 | const startVariable = nodeVariables.get(prevNode); |
| 688 | const endVariable = nodeVariables.get(nextNode); |
| 689 | |
| 690 | if (!startVariable || !endVariable) { |
no test coverage detected