Skip to content

fix(cypher): MATCH returns all matching rows (Issue #269)#292

Open
aepod wants to merge 6 commits intoruvnet:mainfrom
weave-logic-ai:main
Open

fix(cypher): MATCH returns all matching rows (Issue #269)#292
aepod wants to merge 6 commits intoruvnet:mainfrom
weave-logic-ai:main

Conversation

@aepod
Copy link

@aepod aepod commented Mar 24, 2026

Summary

Fixes #269MATCH (n:Person) RETURN n now correctly returns all matching rows instead of only the last one.

Root Cause

execute_match() collapsed all match results into a single ExecutionContext via context.bind(), which overwrites previous bindings in a HashMap. Three matches → only the last survived into execute_return(), producing 1 row.

Fix

Refactored the executor to use a ResultSet pipeline (Vec<ExecutionContext>):

  • Each clause transforms ResultSet → ResultSet
  • execute_match() expands the set (one context per match)
  • execute_return() projects one output row per context
  • execute_set/delete() apply to all contexts in the set
  • Cross-product semantics for multiple patterns in a single MATCH

Tests Added

Test What it verifies
test_match_returns_multiple_rows 3 Person nodes → 3 rows (the #269 regression)
test_match_return_properties Correct property values per row (Alice, Bob)
test_match_where_filter WHERE correctly filters multi-row results
test_match_single_result 1 match → 1 row (no regression)
test_match_no_results 0 matches → 0 rows
test_match_many_nodes 100 nodes → 100 rows (stress test)

Files Changed

  • crates/rvlite/src/cypher/executor.rs — Core fix: ResultSet pipeline
  • crates/rvlite/src/cypher/mod.rs — 6 new tests

🤖 Generated with claude-flow

aepod and others added 2 commits March 24, 2026 12:34
The execute_match() function previously collapsed all match results into
a single ExecutionContext via context.bind(), which overwrote previous
bindings. MATCH (n:Person) on 3 Person nodes returned only 1 row.

This commit refactors the executor to use a ResultSet pipeline:
- type ResultSet = Vec<ExecutionContext>
- Each clause transforms ResultSet → ResultSet
- execute_match() expands the set (one context per match)
- execute_return() projects one row per context
- execute_set/delete() apply to all contexts
- Cross-product semantics for multiple patterns in one MATCH

Also adds comprehensive tests:
- test_match_returns_multiple_rows (the Issue ruvnet#269 regression)
- test_match_return_properties (verify correct values per row)
- test_match_where_filter (WHERE correctly filters multi-row)
- test_match_single_result (1 match → 1 row, no regression)
- test_match_no_results (0 matches → 0 rows)
- test_match_many_nodes (100 nodes → 100 rows, stress test)

Co-Authored-By: claude-flow <ruv@ruv.net>
RETURN n.name now produces column "n.name" instead of "?column?".
Property expressions (Expression::Property) are formatted as
"object.property" for column naming, matching standard Cypher behavior.

Co-Authored-By: claude-flow <ruv@ruv.net>
@aepod
Copy link
Author

aepod commented Mar 24, 2026

test result: ok. 30 passed; 0 failed; 2 ignored

Key results:

github-actions bot and others added 4 commits March 24, 2026 12:41
  Built from commit b2347ce

  Platforms updated:
  - linux-x64-gnu
  - linux-arm64-gnu
  - darwin-x64
  - darwin-arm64
  - win32-x64-msvc

  🤖 Generated by GitHub Actions
  Built from commit 2adb949

  Platforms updated:
  - linux-x64-gnu
  - linux-arm64-gnu
  - darwin-x64
  - darwin-arm64
  - win32-x64-msvc

  🤖 Generated by GitHub Actions
Phase 2 of the ruvector remediation plan. Replaces simulated benchmarks
with real measurements:

- Python harness: hnswlib (C++) and numpy brute-force on same datasets
- Rust test: ruvector-core HNSW with ground-truth recall measurement
- Datasets: random-10K and random-100K, 128 dimensions
- Metrics: QPS (p50/p95), recall@10 vs ground truth, memory, build time

Key findings:
- ruvector recall@10 is good: 98.3% (10K), 86.75% (100K)
- ruvector QPS is 2.6-2.9x slower than hnswlib
- ruvector build time is 2.2-5.9x slower than hnswlib
- ruvector uses ~523MB for 100K vectors (10x raw data size)
- All numbers are REAL — no hardcoded values, no simulation

Co-Authored-By: claude-flow <ruv@ruv.net>
  Built from commit 3b173a9

  Platforms updated:
  - linux-x64-gnu
  - linux-arm64-gnu
  - darwin-x64
  - darwin-arm64
  - win32-x64-msvc

  🤖 Generated by GitHub Actions
@aepod aepod closed this Mar 24, 2026
@aepod aepod reopened this Mar 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CypherEngine: MATCH RETURN produces single row — context.bind() overwrites previous bindings

1 participant