Problem
For a multi-scope repository, ask runs personalized PageRank separately for each participating scope. Each call currently applies a nodeFilter by rebuilding the real-node set and scanning every graph edge to reconstruct the filtered adjacency.
With S matching scopes, topology preparation therefore costs approximately:
even though the scopes are disjoint partitions of the same immutable graph. Large monorepos can turn a single query into many repeated full-graph scans before scope fusion completes.
User-visible effect
Preparing all disjoint scope topologies in one pass reduces CPU work and query latency in multi-scope repositories. The benefit grows with the number of matching scopes because the graph is scanned once instead of once per scope.
It does not change retrieval order or PageRank values. It also deliberately has no effect on single-scope repositories, graphRank: false, or no-match queries.
Synthetic graph-ranking benchmark
Local Node.js v22.22.0 arm64 microbenchmark with 12,000 nodes, 36,000 intra-scope edges, the normal 25 PageRank iterations, two warm-ups, and the median of seven measured runs:
| matching scopes |
current filtered walks |
prepared partitions |
speedup |
| 5 |
9.87 ms |
6.27 ms |
1.6× |
| 20 |
28.44 ms |
11.98 ms |
2.4× |
| 50 |
72.96 ms |
21.43 ms |
3.4× |
This isolates the graph-ranking stage rather than claiming an end-to-end graft ask latency improvement. Real impact depends on graph shape, the number of matching scopes, and the share of query time spent outside PageRank.
Expected behavior
Partition the graph topology once, then reuse each prepared partition for its scope-specific seed walk:
O(nodes + edges) topology preparation
+ per-scope PageRank iteration
The optimization should:
- preserve exact PageRank scores, edge multiplicity, and deterministic order
- exclude cross-scope and unresolved edges exactly as independent filtered walks do
- remain lazy so
graphRank: false and no-match queries do not scan graph edges
- leave single-scope behavior unchanged
Reproduction / regression guard
A graph with two independent scopes can expose counters on its nodes and edges arrays. Preparing both partitions should read each array once, and running both prepared walks should produce byte-equivalent entries to two independent nodeFilter walks.
I have a focused implementation and regression coverage ready.
Problem
For a multi-scope repository,
askruns personalized PageRank separately for each participating scope. Each call currently applies anodeFilterby rebuilding the real-node set and scanning every graph edge to reconstruct the filtered adjacency.With
Smatching scopes, topology preparation therefore costs approximately:even though the scopes are disjoint partitions of the same immutable graph. Large monorepos can turn a single query into many repeated full-graph scans before scope fusion completes.
User-visible effect
Preparing all disjoint scope topologies in one pass reduces CPU work and query latency in multi-scope repositories. The benefit grows with the number of matching scopes because the graph is scanned once instead of once per scope.
It does not change retrieval order or PageRank values. It also deliberately has no effect on single-scope repositories,
graphRank: false, or no-match queries.Synthetic graph-ranking benchmark
Local Node.js v22.22.0 arm64 microbenchmark with 12,000 nodes, 36,000 intra-scope edges, the normal 25 PageRank iterations, two warm-ups, and the median of seven measured runs:
This isolates the graph-ranking stage rather than claiming an end-to-end
graft asklatency improvement. Real impact depends on graph shape, the number of matching scopes, and the share of query time spent outside PageRank.Expected behavior
Partition the graph topology once, then reuse each prepared partition for its scope-specific seed walk:
The optimization should:
graphRank: falseand no-match queries do not scan graph edgesReproduction / regression guard
A graph with two independent scopes can expose counters on its
nodesandedgesarrays. Preparing both partitions should read each array once, and running both prepared walks should produce byte-equivalent entries to two independentnodeFilterwalks.I have a focused implementation and regression coverage ready.