Skip to content

Instantly share code, notes, and snippets.

@solussd
Created January 8, 2025 16:36
Show Gist options
  • Save solussd/f44c67b8c55abcf9a481a9642fcae8e5 to your computer and use it in GitHub Desktop.
Save solussd/f44c67b8c55abcf9a481a9642fcae8e5 to your computer and use it in GitHub Desktop.
Datomic pull selector helpers
(ns dev.doomsun.datomic.util)
(defn attr-spec->attr
[attr-spec]
(cond-> attr-spec
(map? attr-spec) ((comp key first))
(vector? attr-spec) (-> first
(cond->
(map? (first attr-spec))
((comp key first))))))
(defn find-attr-spec-match
[pattern attr-spec]
(let [attr (attr-spec->attr attr-spec)]
(first (filter #(= attr (attr-spec->attr %)) pattern))))
(defn merge-pull-selectors
"Merges Datomic pull selectors.
Handles sub-selectors, :as, :limit, :xform etc.
A varargs arity is included for convienence."
([a b]
(vec
(reduce (fn [pset aspec]
(if-let [attr-spec-match (find-attr-spec-match pset aspec)]
(cond
(and (map? attr-spec-match) (map? aspec))
(conj (disj pset attr-spec-match)
{(key (first attr-spec-match))
(merge-pull-selectors (val (first attr-spec-match))
(val (first aspec)))})
(and (map? attr-spec-match) (vector? aspec) (map? (first aspec)))
(conj (disj pset attr-spec-match)
(vec
(concat
[{(key (first attr-spec-match))
(merge-pull-selectors (val (first attr-spec-match))
(val (first (first aspec))))}]
(rest aspec))))
(vector? aspec)
(conj (disj pset attr-spec-match)
(vec (concat [attr-spec-match] (rest aspec))))
(map? aspec)
(conj (disj pset attr-spec-match) aspec)
:else pset)
(conj pset aspec)))
(set a)
b)))
([a b & more]
(reduce merge-pull-selectors (concat [a b] more))))
(defn keypath->pull-selector
"Takes a keypath and turns it into a pull selector."
([keypath]
(if (:selector (meta (first (rseq keypath))))
(keypath->pull-selector (vec (butlast keypath)) (last keypath))
(keypath->pull-selector keypath nil)))
([keypath last-selector]
(reduce (fn [a k]
[{k a}])
(or last-selector [(peek keypath)])
(drop (if last-selector 0 1) (rseq keypath)))))
(defn pull-keypath-val
"Takes keypath and pulls the value along it, starting from the entity
identifier, from db."
[db keypath identifier]
(let [pull-sel (keypath->pull-selector keypath)]
(get-in (d/pull db pull-sel identifier) keypath)))
(comment
(merge-pull-selectors [:a]
[{:a [:b :c]}]
[:d]
[{:a [{:b [:x :y]}]}])
;; => [:d {:a [{:b [:x :y]} :c]}]
(keypath->pull-selector [:a :b :c])
;; => [{:a [{:b [:c]}]}]
(keypath->pull-selector [:x :y ^:selector [:multiple :things :under-y]])
;; => [{:x [{:y [:multiple :things :under-y]}]}]
(pull-keypath-val db
[:user/profile :profile/first-name]
[:user/email "[email protected]"])
;; => "Joe"
nil)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment