Repository navigation
perf(csr): expand one-hop optional match as an outer csr hop - #118
Merged
Merged
Conversation
A single-hop OPTIONAL MATCH with no WHERE of its own now runs as one outer CsrExpand over its input, instead of planning the input again as a seed and left-joining the optional side back. Input rows with no neighbour keep one row with null hop columns. Filters never move into an outer hop, and it is never counted in place. Part of #18.
prrao87
marked this pull request as ready for review
November 3, 2026 21:19
prrao87
added a commit
that referenced
this pull request
Nov 3, 2026
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.
Why
OPTIONAL MATCHplanned its input twice: once as the left side, and once more as the seed its optional pattern expands from, before joining the two back together with a left join. After #108 this copy was the largest remaining duplication in IC10's plan, as noted in #18. For a single hop, the CSR can produce the same rows from one pass over the input if input rows without a neighbour are kept.Closes #18. With #108, IC10 has gone from 209 to 42 physical operators and from 97.5 to 20.5 ms in
memorymode, with results unchanged and no regression elsewhere, which meets that issue's acceptance criteria. IC1 is not covered, because itsOPTIONAL MATCHclauses are two-hop chains; that work, and the remaining gap to other engines, is for a new issue.What changed
CsrExpandgains an outer mode, shown asouterinexplain(). It records which input rows produced output, and after each input batch it emits the rows that produced none, with nulls in the columns the hop adds. An edge to a missing target node counts as no match.OPTIONAL MATCHfrom its only shared variable, with noWHEREof its own, now plans as one outer expansion of its input when the hop uses the CSR and fetches the target's columns in place. Every otherOPTIONAL MATCHkeeps the seed and left join: its ownWHERE, more than one hop, pattern predicates, or a hop without a CSR.Validation
cargo test --workspaceandcargo clippy --workspace --all-targets -- -D warningspass.WHEREafter the optional hop, which must still drop the null rows. The plan test checks that a single hop is one outer expansion with no join, and that aWHEREon the optional part keeps the left join.typospass.CONTRIBUTING.mdharness on SF1 on an Apple M5. The two release wheels were built frommainat 9a92889 and from this branch's change on top of it. Each wheel ran twice per mode, alternating, and the table gives the better median in ms. Every query's result matched its expected answer.mainmemorylazyPlanning time for IC10 (
explain()) dropped from about 15 ms to 10 ms.Branch time divided by
maintime, as a geometric mean over every query:memorylazyEvery query (ms, better median of two runs)
memorymainmemorybranchlazymainlazybranchmemorymainmemorybranchlazymainlazybranchIC10 is the only query whose physical plan changed; every IC plan was compared between the two wheels. IC3's plan text differs only in the order of two filter conjuncts, which varies from one process to the next on
maintoo. None of the 30 custom queries usesOPTIONAL MATCH. The larger moves elsewhere are therefore noise: IC5lazy(−7.3%; its twomainruns alone were 6% apart), q12lazy(+4.9%) and IC6memory(+4.3%, whose runs overlap).🤖 Generated with Claude Code