deadghost
deadghost

Reputation: 5217

How do I build a transaction that has references to a variable number of entities?

I'm getting into datomic and still don't grok it. How do I build a transaction that has references to a variable number of entities?

For example this creates a transaction with a child entity and a family entity with a child attribute that references the new child entity:

(defn insert-child [id child]
  {:db/id #db/id id
   :child/first-name (:first-name child)
   :child/middle-name (:middle-name child)
   :child/last-name (:last-name child)
   :child/date-of-birth {:date-of-birth child}})

(defn insert-family [id]
  (let [child-id #db/id[:db.part/user]]
    (vector
     (insert-child child-id
                   {:first-name "Richard"
                    :middle-name "M"
                    :last-name "Stallman"})
     {:db/id id
      :family/child child-id})))

(insert-family #db/id[:db.part/user])
=> [{:db/id #db/id[:db.part/user -1000012], 
     :child/first-name "Richard", 
     :child/middle-name "M", 
     :child/last-name "Stallman", 
     :child/date-of-birth nil} 
    {:db/id #db/id[:db.part/user -1000013], 
     :family/child #db/id[:db.part/user -1000012]}]

Notice I used let for child-id. I'm not sure how to write this such that I can map over insert-child while having a family entity that references each one.

I thought about using iterate over #db/id[:db.part/user] and the number of children then mapping over both the result of iterate and a vector of children. Seems kind of convoluted and #db/id[:db.part/user] isn't a function to iterate over to begin with.

Upvotes: 1

Views: 180

Answers (1)

Ben Kamphaus
Ben Kamphaus

Reputation: 1665

Instead of using the macro form #db/id[:db.part/user] which is meant for EDN files and data literals, you should use d/tempid.

You could do something like this (using simplified child entities):

(ns family-tx
    (:require [datomic.api :refer [q db] :as d]))

(def uri "datomic:mem://testfamily")
(d/delete-database uri)
(d/create-database uri)
(def conn (d/connect uri))

(def schema [
              {:db/id (d/tempid :db.part/db)
               :db/ident :first-name
               :db/valueType :db.type/string
               :db/cardinality :db.cardinality/one
               :db.install/_attribute :db.part/db}
              {:db/id (d/tempid :db.part/db)
               :db/ident :last-name
               :db/valueType :db.type/string
               :db/cardinality :db.cardinality/one
               :db.install/_attribute :db.part/db}
              {:db/id (d/tempid :db.part/db)
               :db/ident :family/child
               :db/valueType :db.type/ref
               :db/cardinality :db.cardinality/many
               :db.install/_attribute :db.part/db}
            ])
@(d/transact conn schema)


(defn make-family-tx [kids]
  (let [kids-tx (map #(into {:db/id (d/tempid :db.part/user)} %) kids)
        kids-id (map :db/id kids-tx)]
          (conj kids-tx {:db/id (d/tempid :db.part/user)
                         :family/child kids-id})))


(def kids [{:first-name "Billy" :last-name "Bob"}
           {:first-name "Jim" :last-name "Beau"}
           {:first-name "Junior" :last-name "Bacon"}])

@(d/transact conn (make-family-tx kids))

There are a few strategies for this discussed in the Transactions docs as well (see the "Identifying Entities" section).

Upvotes: 3

Related Questions