* Process query segments in order for flexible clause ordering. * Each segment can contain MATCH, mutations, and a WITH clause that transitions to the next segment.
(segments: QuerySegment[], steps: Step<any>[])
| 178 | * Each segment can contain MATCH, mutations, and a WITH clause that transitions to the next segment. |
| 179 | */ |
| 180 | function processQuerySegments(segments: QuerySegment[], steps: Step<any>[]): void { |
| 181 | let isFirstSegment = true; |
| 182 | let hasSeenNonOptionalMatch = false; |
| 183 | let hasSeenWithClause = false; |
| 184 | |
| 185 | for (const segment of segments) { |
| 186 | // Check if this segment has any content (MATCH, mutations, or list operations) |
| 187 | const hasMatches = segment.matches && segment.matches.length > 0; |
| 188 | const hasMutations = segment.mutations && segment.mutations.length > 0; |
| 189 | const hasUnwind = segment.unwind && segment.unwind.length > 0; |
| 190 | const hasCall = segment.call && segment.call.length > 0; |
| 191 | const hasForeach = segment.foreach && segment.foreach.length > 0; |
| 192 | const hasSet = segment.set; |
| 193 | const hasRemove = segment.remove; |
| 194 | const hasDelete = segment.delete; |
| 195 | const hasWithClause = segment.with && segment.with.length > 0; |
| 196 | |
| 197 | const hasContent = |
| 198 | hasMatches || |
| 199 | hasMutations || |
| 200 | hasUnwind || |
| 201 | hasCall || |
| 202 | hasForeach || |
| 203 | hasSet || |
| 204 | hasRemove || |
| 205 | hasDelete || |
| 206 | hasWithClause; |
| 207 | |
| 208 | // Check if the first MATCH in this segment is OPTIONAL |
| 209 | const firstMatchIsOptional = hasMatches && segment.matches![0]!.optional === true; |
| 210 | |
| 211 | // For the first segment with no MATCH but mutations/list ops/WITH, add StartStep |
| 212 | // Also add StartStep if the first MATCH is OPTIONAL (needs input path for null bindings) |
| 213 | if (isFirstSegment && ((!hasMatches && hasContent) || firstMatchIsOptional)) { |
| 214 | steps.push(new StartStep({})); |
| 215 | } |
| 216 | isFirstSegment = false; |
| 217 | |
| 218 | // Process MATCH clauses for this segment |
| 219 | if (hasMatches) { |
| 220 | for (const matchClause of segment.matches!) { |
| 221 | let patternSteps: Step<any>[]; |
| 222 | |
| 223 | if (matchClause.pattern.type === "ShortestPathPattern") { |
| 224 | patternSteps = convertShortestPathPattern( |
| 225 | matchClause.pattern as ShortestPathPattern, |
| 226 | matchClause.where, |
| 227 | ); |
| 228 | } else if (matchClause.pattern.type === "MultiPattern") { |
| 229 | patternSteps = convertMultiPattern( |
| 230 | matchClause.pattern as MultiPattern, |
| 231 | matchClause.where, |
| 232 | hasSeenWithClause, // Preserve bindings from prior WITH clause |
| 233 | ); |
| 234 | } else { |
| 235 | // After a WITH clause or non-optional MATCH, MATCH can reference variables |
| 236 | const isAnchoredContext = matchClause.optional && hasSeenNonOptionalMatch; |
| 237 | patternSteps = convertPattern( |
no test coverage detected