Skip to content

Instantly share code, notes, and snippets.

@wildermuthn
Last active September 14, 2015 09:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wildermuthn/390a663014d0dd4955e0 to your computer and use it in GitHub Desktop.
Save wildermuthn/390a663014d0dd4955e0 to your computer and use it in GitHub Desktop.
Experimenting with subscribing to datascript db updates in a way that at least loosely parallels a query.
(ns sb-db.experiments
(:require [datascript :as d]
[cljs.core.match :refer-macros [match]])
(:require-macros [sb.macros :refer [logc]]))
;;; Experiment to see if we can subscribe to database updates using core.match, in order to re-query database and get new data that will be (eventually) passed to a reagent component
;;; Initialize
(def schema {:show {}
:item {}})
(def db (d/create-conn schema))
;; Add some data to db
(def init
(do
(d/transact! db [{:db/id 1 :show true}])
(d/transact! db [{:item/random (rand-int 10000)}])
(d/transact! db [{:db/id 2 :item/id 1}])))
;;; Utils
(defn show-item []
(d/transact! db [{:db/id 1 :show true}]))
(defn hide-item
"If run after load, :show will have changed, and the console.log will indicate such."
[]
(d/transact! db [{:db/id 1 :show false}]))
(defn update-item-attr-we-dont-care-about []
(d/transact! db [{:item/random (rand-int 10000)}]))
;;; Filter functions that take either tx-data or a full db
(defn filter-case
"Used to process a tx-data datom. Returning truthy means that the datom indicates that the query for this component needs to be re-run."
[db {:keys [e a v]}]
(match [e a v]
[_ :item/id _] :item/id
[_ :show _] :show
[_ _ _] false))
(defn filter-by-items
"Used by d/filter on a full database. Useful for narrowing down the first version of a database for faster querying"
[db datom]
(let [k (:a datom)]
(filter-case db datom)))
(defn filter-tx-data
"Used in listen! callback to determine if the update affected the data in a way that would affect our query"
[db report]
(let [datoms (-> report :tx-data)]
(some (partial filter-case db) datoms)))
;;; Query logic
(defn show-item?
"A function used within the datalog query itself."
[b]
(= b true))
(defn get-item
"The actual query. Returns the entity id of item/id 1, but only if :show is equal to true."
[]
(d/q '[:find ?e .
:with ?v
:in $ ?f
:where
[_ :show ?v]
[(?f ?v)]
[?e :item/id 1]]
(d/filter @db filter-by-items) show-item?))
(defn get-item-entity
"Make query, get entity, return atom"
[]
(let [e (->> (get-item)
(d/entity @db))]
e))
(defn q-listen
"Make a query, return atom as data, watch for changes, update atom on changes"
[]
(let [e (atom (get-item-entity))]
(d/listen! db #(when-let [updated? (filter-tx-data db %)]
(reset! e (get-item-entity))
(logc [updated? @e] :black)))
e))
(q-listen)
;;; Call these and see js console log. A component that depended on the atom would render automatically based on correct updates to db. Notice that calling one function over and over again doesn't trigger an atom change, as there is no db-update that passes our core.match filter.
#_ (hide-item) ;; triggers query if currently showing item
#_ (show-item) ;; triggers query if currently hiding item
#_ (update-item-attr-we-dont-care-about) ;; will never trigger query
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment