(
source: GraphSource<any>,
input: Iterable<TraversalPath<any, any, any>>,
context?: QueryContext,
)
| 6684 | } |
| 6685 | |
| 6686 | public *traverse( |
| 6687 | source: GraphSource<any>, |
| 6688 | input: Iterable<TraversalPath<any, any, any>>, |
| 6689 | context?: QueryContext, |
| 6690 | ): IterableIterator<TraversalPath<any, any, any>> { |
| 6691 | const { distinct, items, orderBy, skip, limit, whereCondition } = this.config; |
| 6692 | |
| 6693 | // Collect all input paths for potential aggregation |
| 6694 | const paths = [...input]; |
| 6695 | this.traversed += paths.length; |
| 6696 | |
| 6697 | // Check if we have any aggregate items |
| 6698 | const hasAggregate = items.some((item) => item.type === "aggregate"); |
| 6699 | |
| 6700 | // Get non-aggregate items for grouping |
| 6701 | const nonAggregateItems = items.filter((item) => item.type !== "aggregate"); |
| 6702 | |
| 6703 | let results: TraversalPath<any, any, any>[]; |
| 6704 | |
| 6705 | if (hasAggregate) { |
| 6706 | if (nonAggregateItems.length > 0) { |
| 6707 | // Grouped aggregation mode: group by non-aggregate items, compute aggregates per group |
| 6708 | // This implements Cypher semantics: WITH n.name AS name, collect(n) AS nodes |
| 6709 | // groups results by name and collects nodes within each group |
| 6710 | const groups = new Map<string, TraversalPath<any, any, any>[]>(); |
| 6711 | |
| 6712 | for (const path of paths) { |
| 6713 | // Compute group key from non-aggregate items |
| 6714 | const keyParts: string[] = []; |
| 6715 | for (const item of nonAggregateItems) { |
| 6716 | let value: any; |
| 6717 | switch (item.type) { |
| 6718 | case "variable": { |
| 6719 | const pathNode = path.get(item.sourceVariable); |
| 6720 | value = pathNode?.value ?? null; |
| 6721 | break; |
| 6722 | } |
| 6723 | case "property": { |
| 6724 | const pathNode = path.get(item.sourceVariable); |
| 6725 | value = pathNode?.property(item.property as never) ?? null; |
| 6726 | break; |
| 6727 | } |
| 6728 | case "functionCall": { |
| 6729 | const resolvedArgs = item.args.map((arg) => |
| 6730 | resolveConditionValue(path, arg, context), |
| 6731 | ); |
| 6732 | value = evaluateFunction(item.functionName, resolvedArgs, path, item.distinct); |
| 6733 | break; |
| 6734 | } |
| 6735 | } |
| 6736 | if (value && typeof value === "object" && "id" in value) { |
| 6737 | keyParts.push(String(value.id)); |
| 6738 | } else { |
| 6739 | keyParts.push(JSON.stringify(value)); |
| 6740 | } |
| 6741 | } |
| 6742 | const groupKey = keyParts.join("|"); |
| 6743 |
nothing calls this directly
no test coverage detected