Skip to content

Instantly share code, notes, and snippets.

@rynkowsg
Last active December 20, 2022 13:47
Show Gist options
  • Save rynkowsg/690ca7f7985e702df70623500d806199 to your computer and use it in GitHub Desktop.
Save rynkowsg/690ca7f7985e702df70623500d806199 to your computer and use it in GitHub Desktop.
Atom DB with nav support
(ns atom-db
(:require
[clojure.core.protocols :as ccp]
[clojure.datafy :as cd]))
(defn init-db []
(atom {}))
(defn- navigify [db path]
(vary-meta
path
assoc
:db db
`ccp/datafy (fn [v]
(with-meta
{:value v}
{`ccp/nav (fn [xs k v]
(let [db (-> xs k meta :db deref)]
(get-in db v)))}))))
(defn add-item [db path item]
(let [res (atom nil)
assoc-next (fn [s]
(let [items-count (count (get-in s path))
item-id items-count
full-path (navigify db (conj path item-id))
ident (last path)
item-with-id (assoc item ident item-id)
item' (vary-meta item-with-id assoc :path full-path)]
(reset! res full-path)
(assoc-in s full-path item')))]
(swap! db assoc-next)
@res))
(defn get-item
([path]
(get-in (some-> path meta :db deref) path))
([db path]
(get-in (-> db deref) path)))
(comment
@(def db (init-db))
@(def product0 (add-item db [:product/id] {:name "backpack"}))
@(def product1 (add-item db [:product/id] {:name "laptop"}))
@(def product2 (add-item db [:product/id] {:name "ball"}))
@(def person0 (add-item db [:person/id] {:name "Mike" :friends []}))
@(def person1 (add-item db [:person/id] {:name "Tom" :friends [person0]}))
@(def person2 (add-item db [:person/id] {:name "Alice" :friends [person0 person1]}))
@(def tx0 (add-item db [:tx/id] {:kind :purchase :person person0 :product product0}))
@(def tx1 (add-item db [:tx/id] {:kind :purchase :person person1 :product product1}))
@(def tx2 (add-item db [:tx/id] {:kind :purchase :person person2 :product product2}))
(deref db)
(get-item db [:person/id 2])
(get-item person2)
(get-item db [:product/id 2])
(get-item product2)
(get-item db [:tx/id 2])
(get-item tx2)
(as->
;; pick third tx
(get-item db [:tx/id 2]) $
;; pick person from the tx
(:person $)
;; datafy it & nav
(cd/datafy $)
(cd/nav $ :value (:value $))
;; pick second friend of that person
(cd/nav $ :friends (:friends $))
(cd/nav $ 1 (get $ 1))
;; datafy it & nav
(cd/datafy $)
(cd/nav $ :value (:value $))
;; pick only friend of that person
(cd/nav $ :friends (:friends $))
(cd/nav $ 0 (get $ 0))
;; datafy it & nav
(cd/datafy $)
(cd/nav $ :value (:value $))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment