From 1b39bf2075a7048bf2ef97c98f3f4813f87f1537 Mon Sep 17 00:00:00 2001 From: galdre Date: Tue, 13 Feb 2024 10:18:20 -0700 Subject: [PATCH] Query: shortcircuit clause resolution when result is guaranteed to be empty. The `-collect` fn, run at the end of a query, correctly shortcircuits with the following comment: "one empty rel means final set has to be empty" However, while processing the query clauses, there was no shortcircuit. Also, since the implementation of `lookup-pattern` is agnostic to existing rels, this means that attempts to write short-circuiting clauses at the top of a complex query have very little effect: the query engine will continue querying the indexes for every tuple that could possibly match every clause. This adds a shortcircuit in `resolve-clause` if any existing relation is empty. The check is cheap, and it should provide a substantial performance boost in many common cases. --- bench/datascript/bench/datascript.cljc | 17 ++++++++++++++++- src/datascript/query.cljc | 14 ++++++++------ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/bench/datascript/bench/datascript.cljc b/bench/datascript/bench/datascript.cljc index 98c32947..e1e54307 100644 --- a/bench/datascript/bench/datascript.cljc +++ b/bench/datascript/bench/datascript.cljc @@ -110,6 +110,19 @@ [?e :sex :male]] @*db100k))) +(defn bench-q5-shortcircuit [] + (bench/bench + (d/q '[:find ?e ?n ?l ?a ?s ?al + :in $ ?n ?a + :where [?e :name ?n] + [?e :age ?a] + [?e :last-name ?l] + [?e :sex ?s] + [?e :alias ?al]] + @*db100k + "Anastasia" + 35))) + (defn bench-qpred1 [] (bench/bench (d/q '[:find ?e ?s @@ -226,6 +239,7 @@ "q2" bench-q2 "q3" bench-q3 "q4" bench-q4 + "q5-shortcircuit" bench-q5-shortcircuit "qpred1" bench-qpred1 "qpred2" bench-qpred2 "pull-one-entities" bench-pull-one-entities @@ -277,6 +291,7 @@ (bench-q2) (bench-q3) (bench-q4) + (bench-q5-shortcircuit) (bench-qpred1) (bench-qpred2) (bench-pull-one-entities) @@ -298,4 +313,4 @@ (bench-rules-long-30x3) (bench-rules-long-30x5) (bench-freeze) - (bench-thaw)) \ No newline at end of file + (bench-thaw)) diff --git a/src/datascript/query.cljc b/src/datascript/query.cljc index 9ae4ad33..0cea2d15 100644 --- a/src/datascript/query.cljc +++ b/src/datascript/query.cljc @@ -791,12 +791,14 @@ (update context :rels collapse-rels relation)))))) (defn resolve-clause [context clause] - (if (rule? context clause) - (if (source? (first clause)) - (binding [*implicit-source* (get (:sources context) (first clause))] - (resolve-clause context (next clause))) - (update context :rels collapse-rels (solve-rule context clause))) - (-resolve-clause context clause))) + (if (->> (:rels context) (some (comp empty? :tuples))) + context ; The result is empty; short-circuit processing + (if (rule? context clause) + (if (source? (first clause)) + (binding [*implicit-source* (get (:sources context) (first clause))] + (resolve-clause context (next clause))) + (update context :rels collapse-rels (solve-rule context clause))) + (-resolve-clause context clause)))) (defn -q [context clauses] (binding [*implicit-source* (get (:sources context) '$)]