Fork me on GitHub
#datomic
<
2024-05-27
>
Phillip Mates10:05:39

Hi folks, wanted to share a snippet of code that might be useful to people using composite tuple attributes as identity attributes I've been doing this in a project and found that it comes with the constraint that you must always specify the composite tuple attribute by hand when updating the entity. Only base values are valid in tuple, so you must resolve lookup refs beforehand. This limits the structure of your code: you have to have the db available when building transactions. In the end I created a datomic db-fn to do all the work and figured I'd share it to see what folks think:

(def upsert-tuple-id
  "updates an entity that has a composite tuple as an id"
  (d/function
   {:lang :clojure
    :params '[db id-attr attrs]
    :code '(let [{:db/keys [valueType unique tupleAttrs]} (d/entity db [:db/ident id-attr])
                 resolve-id (fn [v]
                              (cond
                                (or (number? v)
                                    (string? v))
                                ;; db/id or temp-id
                                v

                                (map? v)
                                ;; resolve upsert map to either existing db/id or provided temp-id
                                (or ;; find id attr in map and lookup if entity with that id exists
                                 (some (fn [[attr val]]
                                         (when (:db/unique (datomic.api/entity db attr))
                                           (:db/id (datomic.api/entity db [attr val]))))
                                       v)
                                    ;; temp-id
                                 (:db/id v))

                                (seqable? v) ;; vector for in-mem transactor, ArrayList for persistent transactor 
                                ;; resolve lookup ref to db/id
                                (:db/id (d/entity db v))))
                 resolved-tuple-attrs (mapv (fn [a] (cond-> (get attrs a)
                                                      (= :db.type/ref (:db/valueType (d/entity db [:db/ident a])))
                                                      resolve-id))
                                            tupleAttrs)]
             (when-not (= :db.type/tuple valueType)
               (throw (ex-info "The provided id-attribute doesn't have type tuple"
                               {:provided-id-attribute id-attr})))
             [(assoc attrs id-attr resolved-tuple-attrs)])}))
full code (context + a deeper investigation) in the 🧵 Including examples of additional weirdness I found, like the fact that temp db-ids are valid in tuples only if the temp id was used to insert a new entity. This is useful if you want to do an initial insert, but rerunning the tx fails.

🚀 1
Stefan Langwald11:05:35

hey, in this blog post (https://www.cognitect.com/blog/2017/4/6/developing-the-language-of-the-domain) there is a rather beatiful DSL for entities + visualisation, was there ever anything released?

cch115:05:50

Based on what I read, the primary "tool" is just data -not a DSL.

onetom02:06:48

that diagram looks like the twopi layout of graphviz https://graphviz.org/docs/layouts/twopi/

👀 1
Stefan Langwald10:06:20

thanks Tom/Tamas

Stefan Langwald11:05:16

not directly datomic related but I couldnt find any mention of this blog post in slack's search