Repository navigation
perf(query): narrow each variable through an or of ands in where - #130
Merged
Merged
Conversation
A WHERE such as (a.id = $x AND b.id = $y) OR (a.id = $y AND b.id = $x) reads both ends in every branch, so no part of it could be pushed below the expansion. Correlated pattern comprehensions then expanded from every node for each outer row: the official LDBC IC14 text took about two minutes on SF1. When planning a WHERE, also add, per variable, the OR of each branch without that variable's conjuncts (here a.id = $x OR a.id = $y). It is implied by the original, which stays in the filter, so results are unchanged; DataFusion's existing pushdown moves it below the expansion. IC14's official text now runs in 0.31 s with identical rows. Refs #123
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
The official LDBC IC14 text ("Trusted connection paths") takes about 2 minutes on SF1 (#123), which is way too slow, pointing at a suboptimal plan.
The benchmark suite doesn't run that text. graph-benchmark-ldbc-complex runs its own version of IC14: the same query, reworded in Cypher without
reduce(). It unwinds each step of the path, counts the replies in each direction withsize()of a pattern comprehension anchored on the two people, and adds them up withsum(). The benchmark's version takes 0.05 s and returns the same 58 rows as the official text.The cause of the slowdown isn't
reduce(): it is the pattern comprehension inside it, correlated with each path relationshiprthroughEvery branch reads both
aandb, so no part of the condition can run before the expansion reachesb. The plan cross-joined the distinct path relationships with everyPerson, expanded three hops from all of them, and filtered afterwards. The same happens for anyWHEREthat accepts either direction of an edge, inMATCH,OPTIONAL MATCH,EXISTSor a comprehension.What changed
When a
WHEREis planned, each conjunct that is an OR of ANDs now also contributes one implied predicate per variable it reads: every branch with that variable's conjuncts removed, OR-ed back together. Droppingbabove givesa.ID = startNode(r).ID OR a.ID = endNode(r).ID, which reads onlyaand outer values, so DataFusion's existing filter pushdown moves it below the expansion:a.x = 1 OR b.y = 2) or when nothing was dropped.INlists without subqueries are copied, so no pattern or comprehension is planned twice.User impact: no API change. Queries with this kind of
WHEREcan get faster; others plan exactly as before.What remains: the official text runs in 0.31 s, against 0.05 s for the benchmark's version. The remaining time is the nested-loop join comparing each relationship with every
Person, since an OR of equalities isn't a hash join. Closing that gap would need a more targeted rewrite, so this PR stops here. The benchmark repository still runs its own version; switching it to the official text, and updating the benchmarks page, can follow a release with this change.Refs #123
Validation
main, 0.31 s on this branch. Its 58 rows (paths and weights) are identical to those of the benchmark's version.MATCH(null properties, a duplicate edge) and for the IC14 shape, and check that the implied predicate sits below the expansion.cargo test --workspace,cargo clippy --workspace --all-targets -- -D warnings,make test(1,710 passed),make lint, the strict docs build andtyposall pass locally. CI pending.Benchmarks
Both suites,
memoryandlazy, run with the harness in CONTRIBUTING.md: release wheels frommainand from this branch, alternated, two runs each. Every query's answer check passed. Times are the mean of the two runs per build.mainmainrunsmemorylazymemorylazyIC12 in
memorymode came out 14% slower in both branch runs. Its plan is byte-identical on both builds (its onlyORhas single-condition branches, so the rewrite adds nothing), and a control rerun of IC12 alone, alternating builds three times, gavemain16.9 to 19.4 ms and the branch 17.8 to 19.7 ms. It is run-to-run noise.The benchmarked IC14 is the benchmark's version, whose conditions have no
OR, so the rewrite doesn't change it; the official text isn't in either suite yet.graph-benchmark-ldbc (30 queries),
memory: geomean 0.999,mainrun gap 1.001main(ms)graph-benchmark-ldbc (30 queries),
lazy: geomean 0.988,mainrun gap 0.971main(ms)graph-benchmark-ldbc-complex (IC1 to IC14),
memory: geomean 1.012,mainrun gap 1.001main(ms)graph-benchmark-ldbc-complex (IC1 to IC14),
lazy: geomean 1.003,mainrun gap 0.998main(ms)Commits: lance-csr
mainffc3b7e and this branch on top of it; graph-benchmark-ldbc 3afadb9; graph-benchmark-ldbc-complex fc7b44c. Machine: Apple M5, 24 GiB, macOS 26.6.2.🤖 Generated with Claude Code