Loading the good stuff
Gradient background

Datomic Cheat Sheet Series - Queries (Part 5) - Transactions

Clarice Bouwer

Software Engineering Team Lead and Director of Cloudsure

Thursday, 18 October 2018 · Estimated 2 minute read

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

Copy
(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

Copy
(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

Copy
(require '[datomic.api :as d])
(def txn ...)
(:db-after @(d/transact [txn]))
Copy
(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

    Copy
    [[:db/add "jdoe" :person/first "Jan"]
     [:db/add "jdoe" :person/last "Doe"]]
    Copy
     (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.

    Copy
    [: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:

    Copy
    {: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.

    Copy
    {: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:

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

Add data transactions

New entity

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

Entity reference

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

Cardinality many transactions

Copy
[{: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

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

Retract transactions

Copy
[[:db/retract person-id :person/email "curious-programmer.io@gmail.com"]]
Copy
[[: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:

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