Skip to content

Commit 86d8204

Browse files
committed
Implement “constant substitution” optimization for queries (closes #462)
1 parent 826781b commit 86d8204

File tree

3 files changed

+46
-1
lines changed

3 files changed

+46
-1
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# WIP
2+
3+
- Implement “constant substitution” optimization for queries #462
4+
15
# 1.6.3
26

37
- Fix regression in 1.6.2 #460 via @galdre

src/datascript/query.cljc

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,17 @@
526526
(prod-rel (assoc production :tuples []) (empty-rel binding)))]
527527
(update context :rels collapse-rels new-rel)))
528528

529+
(defn substitute-constant [context pattern-el]
530+
(when (free-var? pattern-el)
531+
(when-some [rel (rel-with-attr context pattern-el)]
532+
(when-some [tuple (first (:tuples rel))]
533+
(when (nil? (fnext (:tuples rel)))
534+
(let [idx (get (:attrs rel) pattern-el)]
535+
(#?(:cljs da/aget :clj get) tuple idx)))))))
536+
537+
(defn substitute-constants [context pattern]
538+
(mapv #(or (substitute-constant context %) %) pattern))
539+
529540
;;; RULES
530541

531542
(defn rule? [context clause]
@@ -790,7 +801,9 @@
790801

791802
'[*] ;; pattern
792803
(let [source *implicit-source*
793-
pattern (resolve-pattern-lookup-refs source clause)
804+
pattern (->> clause
805+
(substitute-constants context)
806+
(resolve-pattern-lookup-refs source))
794807
relation (lookup-pattern source pattern)]
795808
(binding [*lookup-attrs* (if (satisfies? db/IDB source)
796809
(dynamic-lookup-attrs source pattern)

test/datascript/test/query.cljc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,5 +265,33 @@
265265
:where [?e :s b]]
266266
db)))))
267267

268+
(deftest ^{:doc "issue-462"} test-constant-substitution
269+
(let [cnt+q (fn [query db & sources]
270+
(let [*cnt (volatile! 0)
271+
db' (d/filter db
272+
(fn [db datom]
273+
(vswap! *cnt inc)
274+
true))
275+
res (apply d/q query db' sources)]
276+
[@*cnt res]))
277+
schema {:a {:db/index true}
278+
:b {:db/index true}
279+
:c {:db/index true}}
280+
db (-> (d/empty-db schema)
281+
(d/db-with
282+
(for [eid (range 1 11)
283+
attr [:a :b :c]]
284+
[:db/add eid attr (str eid (name attr))])))]
285+
(is (= [1 #{["5b"]}] (cnt+q '[:find ?v :where [5 :b ?v]] db)))
286+
(is (= [1 #{[:b]}] (cnt+q '[:find ?a :where [5 ?a "5b"]] db)))
287+
(is (= [1 #{[5]}] (cnt+q '[:find ?e :where [?e :b "5b"]] db)))
288+
(is (= [1 #{[5 :b "5b"]}] (cnt+q '[:find ?e ?a ?v :in $ ?e ?a :where [?e ?a ?v]] db 5 :b)))
289+
(is (= [2 #{[5 :b "5b"]}] (cnt+q '[:find ?e2 ?a ?v :in $ ?a ?v :where [?e ?a ?v] [?e2 ?a ?v]] db :b "5b")))
290+
(is (= [3 #{[:a "5a"] [:b "5b"] [:c "5c"]}] (cnt+q '[:find ?a ?v :in $ ?e :where [?e ?a ?v]] db 5)))
291+
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :where [?e ?a "5b"]] db)))
292+
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :in $ ?v :where [?e ?a ?v]] db "5b")))
293+
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :in $ [?v ...] :where [?e ?a ?v]] db ["5b"])))
294+
(is (= [1 #{[5 :b]}] (cnt+q '[:find ?e ?a :where [(ground "5b") ?v] [?e ?a ?v]] db)))))
295+
268296
#_(require 'datascript.test.query :reload)
269297
#_(clojure.test/test-ns 'datascript.test.query)

0 commit comments

Comments
 (0)