Repository navigation
Added
Database::prepare(cypher) -> PreparedPlan: a genuine prepared
statement that parses and validates once. Execute it any number of
times withDatabase::execute_prepared_plan(&prepared, ¶ms, &options)or theTransaction::execute_prepared_plantwin; repeat
calls skip re-validation. The existingexecute_prepared_statement
path (pre-parsed text, re-validated per call) is unchanged (#216).
Changed
- Label checks on an expansion's far endpoint (
(a)-->(b:Person)) read
the node's label ids directly instead of decoding its whole property
map and throwing it away. Measured on a 10K-node, 5-edges-per-node
graph: label-filtered 1-hop withcount(*)18.1 µs → 13.9 µs
(−23.5%), with a property projection 17.8 µs → 16.1 µs (−9.6%) (#214). - The
LogicalPlan-driven read path (scans,Expand,Filter)
resolves Cypher variables to fixed row-slot indices at plan-build
time instead of hashing aHashMap<String, Binding>per row. Slot
compilation still runs once per execution, so very small result sets
pay a few hundred nanoseconds more up front; larger expansions
amortize it (#215). - Dependencies: redb 4.2 → 4.4, antlr4rust 0.5 → 0.6 (parser
regenerated), rustls → 0.23.46 (RUSTSEC-2026-0285/0346/0348/0349),
and, for thearrowfeature, arrow-array/schema/buffer 59 → 60, so
Database::query_arrowcallers must be on arrow 60 (#226). - Book rewritten around an end-user journey (Getting Started, The
Cypher Guide, Embedding, Reference, Operations), with Internals
demoted to an appendix; stale roadmap items and essay-length comments
trimmed across the workspace (#211, #212, #213). RETURN DISTINCT ... LIMIT kwith no ORDER BY and no aggregation now
stops the read pipeline as soon ask(plus SKIP) distinct projected
rows exist, and variable-length traversals enumerate their paths
lazily per input row instead of materializing every path up front —
together, a[:KNOWS*1..3] ... RETURN DISTINCT ... LIMIT 20over a
high-degree graph walks a few hundred paths instead of ~1M (measured
643 ms → 1.84 ms, and 71 ms → 714 µs on a fixed-2-hop DISTINCT+LIMIT,
LDBC-style SF 0.1). Emission order within a var-length hop is now
depth-first (row order without ORDER BY is unspecified), and a*0..
traversal that would exceed the safety depth cap still errors on any
fully-consumed query, but may now succeed if a satisfied
DISTINCT+LIMIT stops the enumeration before reaching the offending
branch.
Fixed
- Start-point selection prices unindexed literal-equality filters
correctly: the endpoint's emitted-row estimate gets a default 1/10
equality selectivity (its physical scan cost stays full), instead of
the filter only doubling that endpoint's cost and inverting the
anchor choice. Measured on the LDBC-style SF 0.1 workload without
indexes: 33.9ms → 5.6ms (point lookup + hop) and 650ms → 9.6ms (the
unlabeled-far-endpoint shape that anchored an AllNodesScan) (#208).
Install: cargo install marsdb-cli, pip install marsdb, or brew install knoguchi/marsdb/marsdb.