Skip to content

Instantly share code, notes, and snippets.

@refset
Forked from dvingo/crux-pull.clj
Created April 29, 2020 19:05
Show Gist options
  • Save refset/f35ba4cf88e7aa15c4c5e0e91ca8bc49 to your computer and use it in GitHub Desktop.
Save refset/f35ba4cf88e7aa15c4c5e0e91ca8bc49 to your computer and use it in GitHub Desktop.
limited datomic pull api in crux.
(ns crux-pull
(:require
[edn-query-language.core :as eql]
[crux.api :as crux]
[my-app.crux-node :refer [crux-node]]))
(defn entity
([entity-id] (entity crux-node entity-id))
([crux-node entity-id]
(crux/entity (crux/db crux-node) entity-id)))
(defn filter-keys [q ent]
(if (and
(= (count q) 1)
(= (:dispatch-key (first q)) '*))
ent
(let [ks (map :dispatch-key q)]
(select-keys ent ks))))
(declare pull-join*)
(defn pull* [parent-q q ent recursion-depth]
(case (:type q)
:join (pull-join* parent-q q ent recursion-depth)
:prop (hash-map (:dispatch-key q) (get ent (:dispatch-key q)))
ent))
(defn pull-join* [parent-q q ent recursion-depth]
(let [k (:dispatch-key q)
query (:query q)
;; todo I think I need to set recursion-depth to 0 when you find a '...
;; in order to support multiple recursive queries in one pull expression
recursive? (or (pos-int? query) (= '... query))
query-recursive-depth query
inc-recursion-depth (if (pos-int? query) (inc recursion-depth) recursion-depth)
v (k ent)
children-q (if recursive? parent-q (:children q))
children? (seq children-q)
at-recurse-limit? (= query-recursive-depth recursion-depth)
recursion-done? (and recursive? at-recurse-limit?)
new-ent (when-not recursion-done?
(if (coll? v)
(assoc ent k (mapv #(filter-keys children-q (entity %)) v))
(assoc ent k (filter-keys children-q (entity v)))))]
(cond
recursion-done? ent
(and recursive? (not at-recurse-limit?))
(assoc new-ent k (mapv (fn [child]
(filter-keys parent-q
(pull* parent-q q child inc-recursion-depth)))
(k new-ent)))
children?
(reduce (fn [acc q] (merge acc
(filter-keys children-q
(pull* parent-q q acc inc-recursion-depth))))
new-ent
children-q)
:else
new-ent)))
(defn pull [expr id]
(let [ent (entity id)
q (eql/query->ast expr)
children (:children q)
recursion-depth 0]
(filter-keys
children
(reduce (fn [acc child-q] (merge acc (pull* children child-q acc recursion-depth)))
ent
children))))
(def tasks
[{:crux.db/id :task-1
:children #{:task-2 :task-3 :task-4}
:name "task 1"}
{:crux.db/id :task-2 :children #{:task-5} :name "task 2"}
{:crux.db/id :task-3 :children #{} :name "task 3"}
{:crux.db/id :task-4 :children #{} :name "task 4"}
{:crux.db/id :task-5 :children #{} :name "task 5"}])
;; insert into db.
(pull [:name {:children '...}] :task-1)
(pull [:name {:children 1}] :task-1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment