[build] Add multigraph option to preserve parallel edges - #709
[build] Add multigraph option to preserve parallel edges#709ibrahimyuecel wants to merge 1 commit into
Conversation
Add multigraph=True parameter to build_from_json() that produces nx.MultiGraph (or nx.MultiDiGraph if directed=True) instead of nx.Graph. This preserves parallel edges with different `relation` values between the same node pair, which today collapse silently. Default (multigraph=False) keeps the legacy single-edge behaviour, so existing graphs round-trip identically. Why this matters: - Code graphs routinely have multiple relations between same pair: (file, file): imports + references (class, method): method + instantiates (module, exception): provides + throws - Measured on a 21,627-node TypeScript monorepo: 2,767 pairs (~%8 of unique pairs) carry multiple relations that currently collapse. serve.py is already MultiGraph-aware in _filter_graph_by_context (uses edges(keys=True, data=True) when isinstance MultiGraph), so traversal helpers benefit automatically. Smoke test: build_from_json(extraction with 3 (a,b) edges, multigraph=False) -> 1 edge build_from_json(extraction with 3 (a,b) edges, multigraph=True) -> 3 edges
|
Hit a related-but-distinct case of the same bug class on a 1,873-file SvelteKit codebase, supporting this PR's design. Worth flagging here in case it shapes the implementation. This PR's evidence covers same-pair multi-relation collapse (
Two real edges in the source. After On this repo: 43 such edges (41
Methodology note for anyone validating: |
[build] Add MultiGraph option to preserve parallel edges between same node pair
Summary
build_from_json()returnsnx.Graph(undirected, single edge per pair). Whenmultiple relations exist between the same
(src, tgt)pair — which is thenorm for code graphs (
imports+references,method+instantiates,contains+calls) — only one survives.This PR adds
multigraph=Trueparameter tobuild_from_json()(and a--multigraphCLI flag) to opt into
nx.MultiGraph/nx.MultiDiGraph, preserving everydistinct relation as its own edge.
Why this matters
Tested on a 21,627-node TypeScript monorepo (NestJS + Next.js 16, after this
project's local augmenter applied):
Sample lost edges:
These hidden edges break common queries:
graphify path "Module" "Service"— returnsimportsbut misses thereferences(named import) — caller traceability lostgraphify explain "ExceptionClass"— degree under-counted becausemultiple constructors that throw it collapse to one edge
Changes
1.
build.py— opt-in MultiGraph2.
serve.py— handle MultiGraph in iterationNetworkX MultiGraph requires
.edges(data=True, keys=True)to enumerateparallel edges. serve.py BFS/DFS traversal currently uses
.edges(u, v)which returns only one edge per pair on MultiGraph (drops parallel edges
during query traversal too).
3. CLI flag
4.
to_json/to_html/to_obsidian— emit edge keysNetworkX
node_link_dataalready serializes MultiGraph keys when present, soexports work without modification. HTML viz needs minor update to render
multiple edges between same pair (offset curves).
Backward compatibility
multigraph=False— identical behaviour to current.data["multigraph"]flag already exists in node_linkserialization; consumers (serve.py) detect it.
Test fixture
tests/fixtures/multigraph_collision.py:Expected:
multigraph=False: 1 edge (baz → barwith one relation, last-write wins)multigraph=True: 2 edges (imports+calls)Out of scope
support parallel edges natively); just confirm round-trip in tests.
graph.html: separate PR withvis.js multi-edge support.
Tested against
graph). Custom local augmenter for TypeScript already produced
referencesedges that get lost on import paths today.