This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2024-04-16
Channels
- # announcements (1)
- # architecture (319)
- # babashka (27)
- # beginners (101)
- # biff (1)
- # calva (30)
- # cider (6)
- # clj-kondo (38)
- # clojure (41)
- # clojure-boston (1)
- # clojure-europe (80)
- # clojure-nl (1)
- # clojure-norway (21)
- # clojure-uk (4)
- # community-development (7)
- # conjure (1)
- # data-science (18)
- # datalevin (6)
- # datascript (30)
- # datomic (2)
- # events (2)
- # fulcro (1)
- # graalvm (3)
- # holy-lambda (2)
- # hyperfiddle (10)
- # jobs (3)
- # lsp (2)
- # malli (9)
- # matcher-combinators (3)
- # missionary (72)
- # nbb (40)
- # off-topic (1)
- # other-languages (14)
- # planck (5)
- # re-frame (2)
- # releases (4)
- # rewrite-clj (22)
- # shadow-cljs (3)
- # sql (2)
- # squint (17)
- # yamlscript (1)
As I’ve been fleshing out my application, I’ve found that I keep ending up having something like an impedance mismatch where I have some functions that take an map, some functions that take a lookup, some functions that return maps, and some functions that return lookups. I don’t always have the right object. Is there a good rule of thumb for when to work with maps vs lookups?
I end up with stubs that look like
{:db/id 1}
When what I want is a lookup, so I end up pulling the id, but sometimes the map hasn’t been transacted yet and doesn’t have an id so I need to go hunting for a unique identifier attribute.Okay I still don’t understand. What do you mean you get stub but want lookup? What do you mean by lookup? You get these from where?
Maybe I’m using the wrong term. What I mean by lookup is a vector where the first element is a keyword for a unique identity attribute, and the second element is the value to find. Here is a contrived example where I run into this is. I first pull an entity, and then later want to pull a related entity by ref attribute.
(def schema
{:guid {:db/unique :db.unique/identity}
:child {:db/valueType :db.type/ref}})
(let [lookup [:guid some-guid]
parent (d/pull db '[*] lookup)
parent-child (:child parent)
child (d/pull db '[*] (:db/id child-parent))])
The first pull returns the child as {:db/id }
because I didn’t specify it. As a result I have a lot of checks to see if an entity has been pulled already or not when I want to process that entity later. It feels like I’m doing something wrong.
Where this really comes in is when the two fetches are separated by time.
(defn pull-parents []
(d/q '[:find (pull ?e '[*])
:where [?e :child _]]
db))
(def pull-child [parent]
(d/pull db '[* {:child ...}] (:db/id parent)))
On the other side there are transactions where I have an entity and I want to assign another entity to a ref attribute and I end up having to create a “lookup” with a unique identity attribute.
(d/transact conn [{:child [:guid (:guid child))}}])
Hopefully those examples make my question a little more clear.so use them in code but if you have better id stored as attribute too then rely on those when talking over module boundaries
Maybe my problem is that I don’t have clear module boundaries. By that do literally mean clojure namespaces or do you mean some abstract module boundary?
but all in all, just be mindful and maybe have var naming scheme so you remember what is what
Got it, I think my question may ultimately boil down to that in the end as well :thinking_face:
or maybe you are talking about that datascript functions should accept {:db/id …} maps where they expect eid or lookup?
For what it’s worth, my vote would be no. I wrote a quick function that would basically check the inputs and find the best value for a given key and fall back to db/id. I found it incredibly difficult to understand what shape my data was in at any given function call.
I just discovered why I got so confused. There is an error when trying to transact a nested entity that is also unique. I setup a schema that has one attribute that is a plain old ref and one that is a ref that is also unique.
(def conn (d/create-conn {:unique-ref {:db/valueType :db.type/ref
:db/unique :db.unique/identity}
:just-a-ref {:db/valueType :db.type/ref}}))
If I transact the plain old ref with a nested entity there is no problem
(d/transact! conn [{:just-a-ref {:foo 1}}])
{:db-before
#datascript/DB {:schema
{:unique-ref
#:db{:valueType :db.type/ref, :unique :db.unique/identity},
:just-a-ref #:db{:valueType :db.type/ref}},
:datoms []},
:db-after
#datascript/DB {:schema
{:unique-ref
#:db{:valueType :db.type/ref, :unique :db.unique/identity},
:just-a-ref #:db{:valueType :db.type/ref}},
:datoms [[1 :just-a-ref 2 536870913] [2 :foo 1 536870913]]},
:tx-data
[#datascript/Datom [2 :foo 1 536870913 true]
#datascript/Datom [1 :just-a-ref 2 536870913 true]],
:tempids {1 1, 2 2, :db/current-tx 536870913},
:tx-meta nil}
If I try to transact a nested unique ref I get an error that I need to provide an eid or lookup ref.
(d/transact! conn [{:unique-ref {:foo 1}}])
; Execution error (ExceptionInfo) at datascript.db/entid (db.cljc:1204).
; Expected number or lookup ref for entity id, got {:foo 1}
Is this a bug or a necessary constraint?Experience report... mistakenly used an empty map as a value for an attribute whose value type was a reference; seems to work OK until it is serialized and deserialized, at which point it took on the value of a reference to another nearby entity
(serialization via datascript-transit)
I'll look into that 👍