fix(graph-node): batchInsert nodes missing from label index - #616
Merged
Conversation
batchInsert only populated the hypergraph adjacency/vector index (used by kHopNeighbors and stats) but never inserted nodes into the property graph + label index that the Cypher `MATCH (n:Label) RETURN n` scan reads. As a result, the fastest ingest path produced query-invisible nodes: they were counted in stats() and traversable by kHopNeighbors, but a label-scoped MATCH returned 0. createNode did both; batchInsert did not. Extract the shared index-registration logic into a single `register_node` helper (single source of truth) and call it from both createNode and batchInsert so the hypergraph index, property graph + label index, and optional storage all stay consistent. batchInsert now also honors per-node labels/properties (previously ignored). Adds a Rust regression test asserting that nodes registered via the shared path are consistently visible through all three read surfaces: label-scoped scan (get_nodes_by_label), kHop adjacency (k_hop_neighbors), and stats() entity counts. Discovered via agent-harness-generator ruvector benchmarking (GRAPH-ANALYTICS-PROOF §5). Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_019rVRYrRDKyxYK18kuVrDSf
….2.1 - cargo fmt on crates/ruvector-graph-node/src/lib.rs (Rustfmt CI) - regenerate Cargo.lock so the local ruvector-sona workspace member resolves at 0.2.1 (offline, no external version bumps) — fixes `cargo metadata --locked` lockfile-integrity check Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_019rVRYrRDKyxYK18kuVrDSf
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.
Summary
@ruvector/graph-node'sbatchInsertproduced query-invisible nodes: nodes inserted via the fast bulk path were counted instats()and were traversable bykHopNeighbors, but were not added to the property-graph label index that the only working Cypher pattern (MATCH (n:Label) RETURN n) reads. A user who bulk-loads and then queries by label gets silent empty results — a correctness bug in the fastest ingest path.Reproduction (before fix)
stats()andkHopNeighborssee the nodes; the label scan returns 0.Root cause
In
crates/ruvector-graph-node/src/lib.rs:create_nodeinserted into both backing indexes: the hypergraph adjacency/vector index (hg.add_entity, used bykHopNeighbors/stats/searchHyperedges) and the property graph + label index (gdb.create_node, read by the Cypher label scan).batch_insertonly calledhg.add_entityper node — it never calledgdb.create_node, and it silently ignored each node'slabels/properties.So the two indexes diverged for bulk-loaded data.
Fix
Extract the shared registration logic into a single
register_nodehelper (single source of truth) that populates the hypergraph index, the property graph + label index, and optional persistent storage. Bothcreate_nodeandbatch_insertnow call it, so all read surfaces stay consistent.batch_insertnow also honors per-nodelabels/properties(previously dropped).Test
Added a Rust regression test (
cargo test -p ruvector-graph-node) asserting that nodes registered via the shared path are consistently visible through all three read surfaces — label-scoped scan (get_nodes_by_label), kHop adjacency (k_hop_neighbors), andstats()entity count.Verified end-to-end after rebuilding the NAPI binding (
napi build --platform --release): the repro above now returnsMATCH (n:Person) = 50, plus a mixedbatchInsert+createNodecase returns the correct per-label counts, withstats/kHopunchanged.Out of scope / known issues (filed, not fixed here)
Surfaced by the same benchmarking but deliberately not addressed in this focused fix:
MATCH (n:Person {name:"Alice"})returns allPersonnodes instead of filtering. The query executor reads only the label, not the inline property map.WHERE/ aggregation patterns return empty — e.g.MATCH (a)-[r]->(b) RETURN a,r,b,MATCH (a)-[*1..2]->(b),MATCH (n) WHERE n.name="x",RETURN count(n). The supported Cypher subset is effectively justMATCH (n:Label) RETURN n. This is a larger feature gap, not a one-line correctness fix, and is left as a separate effort.MATCH (n) RETURN n(no label) returns 0 — the executor only scans by label.Provenance
Discovered via agent-harness-generator ruvector benchmarking — see
GRAPH-ANALYTICS-PROOF.md §5(thebatchInsert ↔ Cypherconsistency bug, proven with a known-answer table). $0 to find and fix (local Rust build + test, no LLM/paid API).🤖 Generated with claude-flow