* Convert RETURN clause to appropriate steps. * For mutation-only queries without RETURN, adds DrainStep to consume results.
(query: Query)
| 2601 | * For mutation-only queries without RETURN, adds DrainStep to consume results. |
| 2602 | */ |
| 2603 | function convertReturnClause(query: Query): Step<any>[] { |
| 2604 | const steps: Step<any>[] = []; |
| 2605 | const returnClause = query.return; |
| 2606 | const groupByClause = query.groupBy; |
| 2607 | |
| 2608 | // If no RETURN clause (mutation-only query), add DrainStep |
| 2609 | // This ensures mutations execute (pipeline is consumed) but no results are returned |
| 2610 | if (!returnClause) { |
| 2611 | steps.push(new DrainStep({})); |
| 2612 | return steps; |
| 2613 | } |
| 2614 | |
| 2615 | // Check if we have any aggregate (using legacy format) |
| 2616 | const aggregateItems = returnClause.items.filter((item) => item.aggregate !== undefined); |
| 2617 | const aggregateItem = aggregateItems[0]; |
| 2618 | |
| 2619 | // Check if we have any function calls (like labels(), type()) |
| 2620 | // Using both legacy format and new expression format |
| 2621 | const functionItems = returnClause.items.filter( |
| 2622 | (item) => isLegacyFunctionExpression(item) !== null, |
| 2623 | ); |
| 2624 | const functionItem = functionItems[0]; |
| 2625 | const functionInfo = functionItem ? isLegacyFunctionExpression(functionItem) : null; |
| 2626 | |
| 2627 | // Check if we have any plain items (non-aggregate, non-function) |
| 2628 | // These could be plain variables, property access, or other expressions |
| 2629 | const plainItems = returnClause.items.filter( |
| 2630 | (item) => item.aggregate === undefined && isLegacyFunctionExpression(item) === null, |
| 2631 | ); |
| 2632 | |
| 2633 | // If GROUP BY is present, use GroupByStep to handle mixed aggregates and functions |
| 2634 | if (groupByClause) { |
| 2635 | // Validate: non-aggregate RETURN items must appear in GROUP BY |
| 2636 | const nonAggregateItems = returnClause.items.filter((item) => item.aggregate === undefined); |
| 2637 | |
| 2638 | for (const returnItem of nonAggregateItems) { |
| 2639 | const matchesGroupBy = groupByClause.items.some((groupByItem) => { |
| 2640 | // Match by function (e.g., labels(n)) |
| 2641 | if (returnItem.function && groupByItem.function) { |
| 2642 | return ( |
| 2643 | returnItem.function === groupByItem.function && |
| 2644 | returnItem.variable === groupByItem.variable |
| 2645 | ); |
| 2646 | } |
| 2647 | // Match by property (e.g., n.name) |
| 2648 | if (returnItem.property && groupByItem.property) { |
| 2649 | return ( |
| 2650 | returnItem.variable === groupByItem.variable && |
| 2651 | returnItem.property === groupByItem.property |
| 2652 | ); |
| 2653 | } |
| 2654 | // Match by variable (e.g., n) |
| 2655 | if ( |
| 2656 | !returnItem.function && |
| 2657 | !returnItem.property && |
| 2658 | !groupByItem.function && |
| 2659 | !groupByItem.property |
| 2660 | ) { |
no test coverage detected