Skip to content

Commit 1e75bcf

Browse files
IamDrowsytonsky
authored andcommitted
Added datafy/pull and datafy/pull-many version.
They work as pull/pull-many but return maps that impement the datafy/nav dance.
1 parent 277f533 commit 1e75bcf

2 files changed

Lines changed: 114 additions & 0 deletions

File tree

src/datascript/datafy.cljc

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
(ns datascript.datafy
2+
(:require [clojure.core.protocols :as cp]
3+
[datascript.pull-api :as dp]
4+
[clojure.string :as str]))
5+
6+
(declare datafy-entity)
7+
(declare datafy-entity-seq)
8+
9+
(defn- attr<->rattr [attr]
10+
(keyword (namespace attr)
11+
(if (str/starts-with? (name attr) "_")
12+
(subs (name attr) 1)
13+
(str "_" (name attr)))))
14+
15+
(defn- pull-pattern [ref-rattrs]
16+
(into ["*"] ref-rattrs))
17+
18+
19+
(defn- navize-entity [db-val entity]
20+
(let [ref-attrs (:db.type/ref (:rschema db-val))
21+
ref-rattrs (set (map attr<->rattr ref-attrs))
22+
many-attrs (:db.cardinality/many (:rschema db-val))
23+
pull-pattern (into ["*"] ref-rattrs)]
24+
(with-meta entity
25+
{`cp/nav (fn [coll k v]
26+
(cond
27+
(or (and (many-attrs k) (ref-attrs k))
28+
(ref-rattrs k))
29+
(datafy-entity-seq db-val
30+
(dp/pull-many db-val pull-pattern (mapv :db/id v)))
31+
(ref-attrs k)
32+
(datafy-entity db-val (dp/pull db-val pull-pattern (:db/id v)))
33+
:else v))})))
34+
35+
(defn- navize-entity-seq [db-val entities]
36+
(with-meta entities
37+
{`cp/nav (fn [coll k v]
38+
(datafy-entity db-val v))}))
39+
40+
(defn- datafy-entity [db-val entity]
41+
(with-meta entity
42+
{`cp/datafy (fn [entity]
43+
(navize-entity db-val entity))}))
44+
45+
(defn- datafy-entity-seq [db-val entities]
46+
(with-meta entities
47+
{`cp/datafy (fn [entities] (navize-entity-seq db-val entities))}))
48+
49+
(defn pull
50+
"Same as `datascript.core/pull` but the returned map implements `datafy/nav` (See [https://clojure.github.io/clojure/clojure.datafy-api.html](https://clojure.github.io/clojure/clojure.datafy-api.html).
51+
52+
This can be used to navigate through the database following ref fields.
53+
54+
Usage:
55+
```
56+
(pull db [:ref, :name] 1)
57+
; => {:db/id 1,
58+
; :name \"Child\",
59+
; :ref {:db/id 2}}
60+
(nav (datafy (pull db [:ref, :name] 1))
61+
:ref
62+
{:db/id 2})
63+
; => {:db/id 2,
64+
; :name \"Parent\",
65+
; :_ref {:db/id 1}}
66+
```
67+
"
68+
[db selector eid]
69+
(datafy-entity db (dp/pull db selector eid)))
70+
71+
(defn pull-many
72+
"Same as [[pull]] but accepts sequence of ids and returns sequence of maps that implements `datafy/nav`."
73+
[db selector eids]
74+
(datafy-entity-seq db (dp/pull-many db selector eids)))

test/datascript/test/datafy.cljc

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(ns datascript.test.datafy
2+
(:require
3+
#?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]]
4+
:clj [clojure.test :as t :refer [is are deftest testing]])
5+
[datascript.datafy :as datafy]
6+
[datascript.core :as d]
7+
[clojure.core.protocols :as cp]))
8+
9+
(defn- test-db []
10+
(let [schema {:ref {:db/valueType :db.type/ref}
11+
:namespace/ref {:db/valueType :db.type/ref}
12+
:many/ref {:db/valueType :db.type/ref
13+
:db/cardinality :db.cardinality/many}}
14+
db (-> (d/empty-db schema)
15+
(d/db-with [{:db/id 1 :name "Parent1"}
16+
{:db/id 2 :name "Child1" :ref 1 :namespace/ref 1}
17+
{:db/id 3 :name "GrandChild1" :ref 2 :namespace/ref 2}
18+
{:db/id 4 :name "Master" :many/ref [1 2 3]}]))]
19+
db))
20+
21+
(defn- nav [coll k]
22+
(cp/nav coll k (coll k)))
23+
24+
(defn d+n
25+
"Helper function to datafy/navigate for a path"
26+
[coll [k & ks]]
27+
(if (nil? k)
28+
coll
29+
(d+n (nav (cp/datafy coll) k)
30+
ks)))
31+
32+
(deftest test-navigation
33+
(let [db (test-db)
34+
entity (datafy/pull db [:ref :namespace/ref :many/_ref] 3)]
35+
(is (= 2 (:db/id (d+n entity [:ref]))))
36+
(is (= 2 (:db/id (d+n entity [:namespace/ref]))))
37+
(is (= 1 (:db/id (d+n entity [:ref :namespace/ref]))))
38+
(is (= 3 (:db/id (d+n entity [:namespace/ref :ref :_ref 0 :namespace/_ref 0]))))
39+
(is (= #{1 2 3} (set (map :db/id (d+n entity [:many/_ref 0 :many/ref])))))))
40+

0 commit comments

Comments
 (0)