Conversation
PathsOutputWriter::write(tableID) re-emitted the source through the full DFS whenever lowerBound==0, duplicating every non-empty source-to-source walk and dropping the length-0 row (e.g. a self-loop with *0..3 counted 6 instead of 4). Gate the re-emit on the source having no parent entry and add a writeCoveredSourceWalk hook (default: historical re-emit, a no-op for writers that skip the source) which the variable-length writer overrides to emit just the missing empty walk.
…rializing walks MATCH (a:T)-[:R*lo..up]->(b:T) RETURN b.k, count(b), avg(b.x) materialized every (a, b) walk in RECURSIVE_EXTEND before the hash join + aggregate (2.18B walks / ~85GiB for a two-row answer on the issue-475 graph, OOM killed); the existing REACHABLE_COUNT rewrite only covers single-source COUNT(DISTINCT b). Add CountRelTableOptimizer::tryRewriteGroupedReachableCount plus a Logical/PhysicalGroupedReachableCount operator pair. The physical operator counts walks per distinct end node with a level DP over CSR (O(up * E) time, O(V) memory) and folds the counts into the GROUP BY accumulators, reading each end node's properties once instead of once per walk. COUNT weights every walk; AVG weights by walk multiplicity, matching row-wise aggregation over the unoptimized plan. Scope (v1): full-table forward WALK from/to the same single node table, GROUP BY on end-node properties (or end-node ID), one optional COUNT and one optional AVG on an end-node property. Filters, DISTINCT, grouping by the start node, masks, limits and non-native storage keep the original plan.
adsharma
marked this pull request as ready for review
September 29, 2026 23:42
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the query shape in #475:
The plan materialized every
(a, b)walk inRECURSIVE_EXTENDbefore the hash join + aggregate (2.18B walks / ~85 GiB for a two-row answer on the issue graph -> OOM-killed). The existingREACHABLE_COUNTrewrite only covers single-sourceCOUNT(DISTINCT b).Commits
fix(gds): emit single length-0 row when varlen source reaches itself —
PathsOutputWriter::write(tableID)re-ran the full DFS for the source whenlowerBound==0, duplicating every non-empty source-to-source walk and dropping the length-0 row (self-loop +*0..3counted 6 instead of 4). Gate the re-emit on the source having no parent entry, with awriteCoveredSourceWalkhook (no-op default) that the variable-length writer overrides to emit just the empty walk. Regression test intest/test_files/recursive_join/n_n.test.opt(count): grouped aggregate over variable-length paths without materializing walks — new
CountRelTableOptimizer::tryRewriteGroupedReachableCount+Logical/PhysicalGroupedReachableCount. The physical operator counts walks per distinct end node with a level DP over CSR (O(up * E) time, O(V) memory) and folds the counts into the GROUP BY accumulators, reading each end node's properties once. COUNT/AVG weight every walk, matching row-wise aggregation. Scope (v1): full-table forward WALK on one node table, GROUP BY end-node properties/ID, optional COUNT + optional AVG. Test:OptimizerTest.GroupedReachableCount(incl. self-loop + negative shapes).Validation
optimizer_test.cppsyntax-checked locally; full suites (optimizer, e2e incl. recursive_join/tck) left to CI.Resulting plan (
LBUG_DUMP_LOGICAL=1)