Skip to content

Commit

Permalink
Implement “constant substitution” optimization for queries (closes #462)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Apr 23, 2024
1 parent 826781b commit 86d8204
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# WIP

- Implement “constant substitution” optimization for queries #462

# 1.6.3

- Fix regression in 1.6.2 #460 via @galdre
Expand Down
15 changes: 14 additions & 1 deletion src/datascript/query.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,17 @@
(prod-rel (assoc production :tuples []) (empty-rel binding)))]
(update context :rels collapse-rels new-rel)))

(defn substitute-constant [context pattern-el]
(when (free-var? pattern-el)
(when-some [rel (rel-with-attr context pattern-el)]
(when-some [tuple (first (:tuples rel))]
(when (nil? (fnext (:tuples rel)))
(let [idx (get (:attrs rel) pattern-el)]
(#?(:cljs da/aget :clj get) tuple idx)))))))

(defn substitute-constants [context pattern]
(mapv #(or (substitute-constant context %) %) pattern))

;;; RULES

(defn rule? [context clause]
Expand Down Expand Up @@ -790,7 +801,9 @@

'[*] ;; pattern
(let [source *implicit-source*
pattern (resolve-pattern-lookup-refs source clause)
pattern (->> clause
(substitute-constants context)
(resolve-pattern-lookup-refs source))
relation (lookup-pattern source pattern)]
(binding [*lookup-attrs* (if (satisfies? db/IDB source)
(dynamic-lookup-attrs source pattern)
Expand Down
28 changes: 28 additions & 0 deletions test/datascript/test/query.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -265,5 +265,33 @@
:where [?e :s b]]
db)))))

(deftest ^{:doc "issue-462"} test-constant-substitution
(let [cnt+q (fn [query db & sources]
(let [*cnt (volatile! 0)
db' (d/filter db
(fn [db datom]
(vswap! *cnt inc)
true))
res (apply d/q query db' sources)]
[@*cnt res]))
schema {:a {:db/index true}
:b {:db/index true}
:c {:db/index true}}
db (-> (d/empty-db schema)
(d/db-with
(for [eid (range 1 11)
attr [:a :b :c]]
[:db/add eid attr (str eid (name attr))])))]
(is (= [1 #{["5b"]}] (cnt+q '[:find ?v :where [5 :b ?v]] db)))
(is (= [1 #{[:b]}] (cnt+q '[:find ?a :where [5 ?a "5b"]] db)))
(is (= [1 #{[5]}] (cnt+q '[:find ?e :where [?e :b "5b"]] db)))
(is (= [1 #{[5 :b "5b"]}] (cnt+q '[:find ?e ?a ?v :in $ ?e ?a :where [?e ?a ?v]] db 5 :b)))
(is (= [2 #{[5 :b "5b"]}] (cnt+q '[:find ?e2 ?a ?v :in $ ?a ?v :where [?e ?a ?v] [?e2 ?a ?v]] db :b "5b")))
(is (= [3 #{[:a "5a"] [:b "5b"] [:c "5c"]}] (cnt+q '[:find ?a ?v :in $ ?e :where [?e ?a ?v]] db 5)))
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :where [?e ?a "5b"]] db)))
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :in $ ?v :where [?e ?a ?v]] db "5b")))
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :in $ [?v ...] :where [?e ?a ?v]] db ["5b"])))
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :where [(ground "5b") ?v] [?e ?a ?v]] db)))))

#_(require 'datascript.test.query :reload)
#_(clojure.test/test-ns 'datascript.test.query)

0 comments on commit 86d8204

Please sign in to comment.