Skip to content

Commit bb43871

Browse files
adityamukhoclaude
andauthored
perf: predicate push-down for Expr clauses + mixed rule optimization (#207, #206) (#249)
* docs: add design spec for Wave 2 PR 1 (predicate push-down + mixed rule optimization) Covers issues #207 and #206: extends optimizer::plan() to accept Expr clauses and interleave them at the earliest position where their variables are bound, and routes the StratifiedEvaluator mixed-rules path through the updated planner. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * docs: add Wave 2 PR 1 implementation plan (predicate push-down + mixed rules) TDD plan covering optimizer.rs, matcher.rs, executor.rs, evaluator.rs, and a new Criterion benchmark. 9 tasks, each with exact file paths, code blocks, and expected test output. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(optimizer): extend plan() to accept Expr clauses with push-down positioning Adds expr_vars() and pattern_bound_vars() helpers. plan() now accepts Vec<WhereClause> (Pattern + Expr) and returns Vec<(WhereClause, Option<IndexHint>)>. Each Expr is inserted at the earliest position after all its variables are bound by preceding patterns. No-variable Exprs and Exprs with unbound variables are appended at the end. Closes part of #207. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(optimizer): remove redundant import, make expr_vars private Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(matcher): add match_with_hint_seeded() for incremental plan execution pub(crate) method used by the executor and evaluator plan loops added in #207: applies an index hint for the first pattern (unit seed) and falls back to hash-join for subsequent patterns. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(executor): adapt to Task B's plan() signature change Task B refactored plan() to accept Vec<WhereClause> and return Vec<(WhereClause, Option<IndexHint>)>, but executor.rs wasn't updated. Extract pattern clauses with their hints to unblock Task C testing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(executor): inline Expr push-down in execute_query() and execute_query_with_rules() Replaces the two-phase pattern-match-then-expr-filter with a single ordered loop in both query execution paths. Pattern clauses expand bindings via match_with_hint_seeded(); Expr clauses filter/extend inline at their push-down position. Removes both top-level apply_expr_clauses post-passes. Not/NotJoin/Or/OrJoin handling is unchanged. Also suppresses dead-code on match_patterns_with_hints and get_patterns (retained for future use), fixes collapsible_if and indexing_slicing warnings introduced by the new code in optimizer.rs and matcher.rs. Closes part of #207. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * style(executor): use Binding alias and fix evaluate_branch doc comment Use the Binding type alias instead of spelling out the full HashMap type in the inline plan loops. Fix the evaluate_branch doc comment which incorrectly stated it mirrored the top-level execute_query order (the top-level now uses an interleaved push-down loop). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(evaluator): route mixed-rules path through plan() for Expr push-down StratifiedEvaluator mixed-rules loop now collects Pattern + Expr clauses and calls optimizer::plan(), processing them in the same ordered loop as the executor. Expr clauses are pushed down to the earliest valid position in the rule body. Not/NotJoin filtering is unchanged. Closes #206. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(evaluator): propagate get_asserted_facts error instead of swallowing it Replace unwrap_or_default() with ? so storage read failures surface as errors rather than silently producing an empty candidate set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * bench: add query/predicate_pushdown benchmark group Measures multi-pattern query with a selective [(> ?v N)] predicate at 1K/10K/100K fact scales. Threshold set at 90th percentile (10% pass rate). Provides baseline for evaluating push-down gains. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * style: apply rustfmt formatting Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent aa15246 commit bb43871

8 files changed

Lines changed: 2407 additions & 121 deletions

File tree

benches/minigraf_bench.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,40 @@ fn bench_btree_lookup(c: &mut Criterion) {
15031503
}
15041504
}
15051505

1506+
// ── query/predicate_pushdown ──────────────────────────────────────────────────
1507+
1508+
fn bench_predicate_pushdown(c: &mut Criterion) {
1509+
// Fixture: n entities each with :val (integer) and :name (string).
1510+
// Uses helpers::populate_with_names(n).
1511+
//
1512+
// Query: multi-pattern with a selective Expr predicate.
1513+
// (query [:find ?e ?n :where [?e :val ?v] [?e :name ?n] [(> ?v <threshold>)]])
1514+
//
1515+
// Threshold = 90th percentile → ~10% of entities pass the filter.
1516+
const SCALES: &[(&str, usize)] = &[("1k", 1_000), ("10k", 10_000), ("100k", 100_000)];
1517+
1518+
let mut group = c.benchmark_group("query/predicate_pushdown");
1519+
group.sample_size(10);
1520+
1521+
for &(label, n) in SCALES {
1522+
let db = helpers::populate_with_names(n);
1523+
let threshold = (n as i64) * 9 / 10;
1524+
let query = format!(
1525+
"(query [:find ?e ?n :where [?e :val ?v] [?e :name ?n] [(> ?v {})]])",
1526+
threshold
1527+
);
1528+
group.bench_with_input(
1529+
BenchmarkId::from_parameter(label),
1530+
&(db, query),
1531+
|b, (db, q)| {
1532+
b.iter(|| db.execute(q).unwrap());
1533+
},
1534+
);
1535+
}
1536+
1537+
group.finish();
1538+
}
1539+
15061540
criterion_group!(
15071541
benches,
15081542
bench_insert,
@@ -1527,5 +1561,6 @@ criterion_group!(
15271561
bench_prepared,
15281562
bench_retract,
15291563
bench_btree_lookup,
1564+
bench_predicate_pushdown,
15301565
);
15311566
criterion_main!(benches);

0 commit comments

Comments
 (0)