-
Notifications
You must be signed in to change notification settings - Fork 391
Description
Bug Description
CypherEngine.execute() in rvlite WASM returns at most one row for any MATCH ... RETURN query, even when multiple nodes match. Property access (n.name, n.value) works syntactically but only returns data for the last matched node.
Environment
- rvlite npm: v0.2.4 (WASM reports "v0.2.0")
- ruvector npm: v0.2.16
- Node.js: v25.7.0
- Platform: macOS (Apple Silicon)
- Usage: via MCP server (
npx ruvector mcp start) +rvlite_cyphertool
Steps to Reproduce
import { initSync, RvLite, CypherEngine } from 'rvlite';
import { readFileSync } from 'fs';
const wasmBuffer = readFileSync('node_modules/rvlite/dist/wasm/rvlite_bg.wasm');
initSync({ module: wasmBuffer });
const db = new RvLite();
const cypher = new CypherEngine(db);
// Create multiple nodes
cypher.execute('CREATE (:Person {name: "Alice", age: 30})');
cypher.execute('CREATE (:Person {name: "Bob", age: 25})');
cypher.execute('CREATE (:Person {name: "Charlie", age: 35})');
// Query all — expect 3 rows, get 1 empty row
const result = cypher.execute('MATCH (p:Person) RETURN p.name, p.age');
console.log(result);Expected Output
{
"columns": ["p.name", "p.age"],
"rows": [
{ "p.name": "Alice", "p.age": 30 },
{ "p.name": "Bob", "p.age": 25 },
{ "p.name": "Charlie", "p.age": 35 }
]
}Actual Output
{
"columns": ["?column?", "?column?"],
"rows": [{}]
}Root Cause Analysis
Located in crates/rvlite/src/cypher/executor.rs, in the execute_match() function:
// Current implementation — overwrites context on each iteration
for match_ctx in matches {
for (var, val) in match_ctx.variables {
context.bind(var, val); // ← Overwrites previous bindings!
}
}The issue: all match results are merged into a single ExecutionContext via context.bind(). Since each call to .bind() overwrites the variable, only the last matched node survives. Then execute_return() produces a single row from whatever the final context holds.
The parser (parser.rs) and property access (Expression::Property evaluation) work correctly — the bug is purely in how match results flow into the return clause.
Suggested Fix
execute_match() should return a Vec<ExecutionContext> (one per match), and execute_return() should iterate over all contexts to produce multiple rows:
// Proposed fix sketch
fn execute_match(&mut self, pattern: &MatchPattern) -> Vec<ExecutionContext> {
let matches = self.graph_store.find_matches(pattern);
// Return each match as its own context — don't merge
matches.into_iter().map(|match_ctx| {
let mut ctx = ExecutionContext::new();
for (var, val) in match_ctx.variables {
ctx.bind(var, val);
}
ctx
}).collect()
}
fn execute_return(&mut self, contexts: &[ExecutionContext], return_items: &[ReturnItem]) -> CypherResult {
let mut rows = Vec::new();
for ctx in contexts {
let mut row = HashMap::new();
for item in return_items {
let value = self.evaluate_expression_ctx(&item.expression, ctx);
let alias = item.alias.as_deref().unwrap_or(&item.expression.to_string());
row.insert(alias.to_string(), value);
}
rows.push(row);
}
CypherResult { columns: /* from return_items */, rows }
}Additionally, execute_return should use the actual return item expressions as column names instead of "?column?".
Related
- Issue Bug: GraphDatabase.open() doesn't load persisted data; Cypher relationship traversals return empty results #64 fixed a similar problem for
ruvector-graph-node(NAPI bindings), but the fix was not applied to the WASMrvlitecrate - The
graph_store.rscorrectly stores and retrieves node properties — the storage layer is not affected
Impact
This blocks all multi-row Cypher queries in the WASM/MCP path, which means:
- Knowledge graph queries cannot enumerate nodes
- Pattern matching across relationships returns incomplete data
- Any application relying on
rvlite_cypherMCP tool for graph queries gets wrong results
Workarounds (current)
- Use targeted single-node queries with property filters:
MATCH (n:Person {name: 'Alice'}) RETURN n - Use
rvlite_sqlinstead of Cypher for multi-row queries - Use
hooks_remember/hooks_recall(vector search) instead of graph queries
Thank you for the great work on ruvector! Happy to submit a PR with the fix if helpful.