Datomic Cheat Sheet Series Queries Part 5 Transactions

In this post I work through transactions. The example queries are grabbed from the Datomic Docs. If you want to get started with Datomic, check out the first post in the series. If you want to see other queries, then check out part 1 and part 2. If you want to set up query rules then check out part 3. If you want to learn more about pull then check out part 4.

Create a database connection

Create an inline database connection

(require '[datomic.api :as d])
(def db-uri "datomic:dev://localhost:4334/oxygen")
(def conn (d/connect db-uri))
(def db (d/db conn))

Create a database connection as a function

(ns beryllium.core
  (:require [datomic.api :as d]))

(defn new-db []
  (let [db-uri "datomic:dev://localhost:4334/oxygen"
        conn (d/connect db-uri)
        db (d/db conn)]
    db))

Transact

(require '[datomic.api :as d])
(def txn ...)
(:db-after @(d/transact [txn]))
(let [tempid (d/tempid :db.part/user)
      txn {:db/id tempid :person/name name :person/email "curious-programmer.io@gmail.com"}
      {:keys [db-after tempids]} @(d/transact-async conn [txn])
      key-id (d/resolve-tempid db-after tempids tempid)]
    (get-person-by-id db-after key-id)))

Identifying entities

You can specify an entity id in three ways by using a:

  • a temporary id for a new entity being added to the database

    [[:db/add "jdoe" :person/first "Jan"]
    [:db/add "jdoe" :person/last "Doe"]]
    (d/tempid :db.part/user)
  • an existing id for an entity that's already in the database For example, this query retrieves the id of an existing entity based on an email address.

    [:find ?e :in $ ?email :where [?e :person/email ?email]]

    If the entity id returned by the query is 17592186046416, the following transaction data will set the entity's customer status:

    {:db/id 17592186046416
    :customer/status :active}

    If the entity in question has a unique identifier, you can specify the entity id by using a lookup ref. Rather than querying the database, you can provide the unique attribute, value pair corresponding to the entity you want to assert or retract a fact for. Note that a lookup ref specified in a transaction will be resolved by the transactor.

    {:db/id [:customer/email "joe@example.com"]
    :customer/status :active}
  • an identifier for an entity that's already in the database In the example below, the entity is specified by the ident :person/name:

    [:db/add :person/name :db/doc "A person's full name"]

Add data transactions

New entity

[{:person/name "Bob"
  :person/email "bob@example.com"}]

Entity reference

[{:db/id "bobid"
  :person/name "Bob"
  :person/spouse "aliceid"}
 {:db/id "aliceid"
  :person/name "Alice"
  :person/spouse "bobid"}]

Cardinality many transactions

[{:db/id #db/id[:db.part/user]
  :person/name "Bob"
  :person/email "bob@example.com"
  :person/aliases ["Robert" "Bert" "Bobby" "Curly"]}]

Nested maps in transactions

[{:db/id order-id
  :order/lineItems [{:lineItem/product chocolate
                     :lineItem/quantity 1}
                    {:lineItem/product whisky
                     :lineItem/quantity 2}]}]

Retract transactions

[[:db/retract person-id :person/email "curious-programmer.io@gmail.com"]]
[[:db.fn/retractEntity id-of-jane]
 [:db.fn/retractEntity [:person/email "jdoe@example.com"]]]

Compare and swap transactions

The following example transaction data sets entity 42's :account/balance to 110, if and only if :account/balance is 100 at the time the transaction executes:

[[:db.fn/cas 42 :account/balance 100 110]]