Transaction Data Reference

Datomic represents transaction requests as data structures. This is a significant difference from SQL databases, where requests are submitted as strings. Using data instead of strings makes it easier to build requests programmatically.

Transaction Grammar

Syntax

'' literal
"" string
[] = list or vector
{} = map {k1 v1 ...}
() grouping
| choice
? zero or one
+ one or more

The symbols keyword, string, boolean, instant, uuid, long, bigint, float, double, and bigdec are the primitive value types supported by datomic.

Grammar

tx-data                    = [list-form | map-form]+
list-form                  = ([op n-identifier identifier value] |
                              [tx-fn tx-fn-arg*])
map-form                   = {keyword (value | map-form | [map-form])}
op                         = (':db/add' | ':db/retract')
n-identifier               = (identifier | tempid)
tempid                     = string
identifier                 = eid | lookup-ref | ident
eid                        = nat-int
lookup-ref                 = [identifier value]
ident                      = keyword
value                      = (string | keyword | boolean | ref | instant | uuid | number)
ref                        = n-identifier
number                     = (long | bigint | float | double | bigdec)
db-fn                      = identifier
classpath-fn               = qualified symbol
tx-fn-arg                  = (value | [value] | {value value})
tx-fn                      = db-fn | classpath-fn

Tx Data

tx-data                    = [list-form | map-form]+

Transaction data is a list of list forms or map forms.

List Form

list-form                  = ([op n-identifier identifier value] |
                              [tx-fn tx-fn-arg*])

Each list in a transaction represents one of

  • addition
  • retraction
  • invocation of a transaction function

Example List Forms

[:db/add "John" :user/last-name "Doe"]
[:db/retract "John" :user/last-name "Doe"]
[:db/cas "my-account" :account/balance + 10]

Map Form

map-form                   = {keyword (value | map-form | [map-form])}

The map form provides a compact representation for multiple assertions about a single entity. Each map form is equivalent to a set of one or more :db/add operations. Map forms may include the special :db/id key to specify the entity identifier that the assertions are about.

Example Map Form

For example, the following map form:

{:order/id "X-001"
 :order/line-items [{:item/sku "SKU-7" :item/count 2}]

is equivalent to the following list forms:

[:db/add "-42" :order/id "X-001"]
[:db/add "-42" :order/line-items "-43"]
[:db/add "-43" :item/sku "SKU-7"]
[:db/add "-43" :item/count 2] 

where "-42" and "-43" are tempids created by Datomic, and are guaranteed not to collide with any application tempids.

Op

op                         = (':db/add' | ':db/retract')

The op is the first item of a list form, and it specifies whether the datoms is an assertion (:db/add), or a retraction (:db/retract).

Example Ops

The following two transactions first assert that entity 42 likes pizza, then retracts this fact.

;; tx 1
[[:db/add 42 :likes "pizza"]]

;; tx 2
[[:db/retract 42 :likes "pizza"]]

Entity Identifiers

n-identifier               = (identifier | tempid)
tempid                     = string
identifier                 = eid | lookup-ref | ident
eid                        = nat-int
lookup-ref                 = [identifier value]
ident                      = keyword

Every datom is about an entity. There are three possible ways to identify an existing entity:

  • an eid for an entity that is already in the database
  • an ident for an entity that has a :db/ident
  • a lookup ref for entity that has a unique attribute

In transactions, there is a fourth option for identifying an entity that might not exist yet:

  • a tempid for a new entity being added to the database

Tempids

tempid                     = string

When you are adding data to a new entity, you identify it using a tempid. Datomic will convert tempids to entity ids when applying a transaction.

A temporary id is simply a string. The content of the string does not matter to Datomic – it is used only as an opaque identifier. Datomic guarantees that multiple uses of the same tempid within a transaction will resolve to the same entity id.

Tempid example

The following list forms use the tempid "foo" twice to indicate that two datoms are about the same entity.

[:db/add "foo" :item/sku "sku-42"]
[:db/add "foo" :item/description "Chandrian Repellant"]

Datomic does not care what name you use for a tempid, but it is often convenient to use a string that uniquely identifies the entity within the domain. So the previous example might also be stated as:

[:db/add "sku-42" :item/sku "sku-42"]
[:db/add "sku-42" :item/description "Chandrian Repellant"]

Implicit Tempid

If no :db/id value is specified in a transaction map form, Datomic will assign a unique tempid.

For new entities, you only need to explicitly specify a temporary id if:

  • you want to recover the assigned entity id from the transaction result
  • you need to refer to that entity elsewhere in the same transaction
  • Implicit Tempid Example

    In the following example, "sku-42" is explicit so that the item and its occurrence in inventory can be linked. The "sku-43" entity occurs only once, so no explicit tempid is needed.

    {:db/id "sku-42"
     :item/sku "sku-42"
     :item/description "Chandrian Repellant"}
    {:item/sku "sku-43"
     :item/description "Iron Wheel"}
    {:inventory/item "sku-42"
     :inventory/count 10}
    

Identifiers

identifier                 = eid | lookup-ref | ident

An identifier uniquely identifies an entity within a single Datomic database. An identifier is either an entity id, a lookup-ref, or an ident.

Entity Ids

eid                        = nat-int

Datomic assigns an entity id (eid) to every entity, and this eid is stored in the e position of every datom. Entity ids are non-negative integers, but this is an implementation detail. Applications should treat entity ids as opaque identifiers.

Lookup Refs

lookup-ref                 = [identifier value]

A lookup-ref allows you to specify entities by their domain-unique identities. Inside a lookup-ref, the identifier names a unique attribute in the database, and the value is a valid value for that attribute. For example, this lookup ref:

[:person/email "joe@example.com"]

identifies the entity with the :person/email value "joe@example.com".

Idents

Idents are programmatic names that associate a keyword with an entity id. All idents for a database are interned and cached in memory on every Datomic compute node.

To create an ident, add a :db/ident value for an entity. The transaction data below upserts four idents: :red, :green, :blue, and :yellow

[{:db/ident :red}
 {:db/ident :green}
 {:db/ident :blue}
 {:db/ident :yellow}]

In a subsequent transactio, you can the use an ident instead of referring to an entity by name. The example below uses the :yellow instead of an entity id.

{:db/doc "The submarine where we live"
 :color :yellow}

Values

value                      = (string | keyword | boolean | ref | instant | uuid | number)

The value type of a datom is dictated by its attribute's schema. The value types are defined and demonsrated in the Schema Data Reference.

Transaction Functions

list-form                  = ([op n-identifier identifier value] |
                              [tx-fn tx-fn-arg*])
db-fn                      = identifier
db-fn-arg                  = (value | [value] | {value value})

Transaction functions are code that is executed inside of a transaction. Transaction functions provide semantics beyond basic asserts or retracts; in particular they allow the data added in a transaction to be derived from the current value of the database.

There are two kinds of transaction functions: database functions are loaded from the database, and classpath functions are loaded from the Java classpath.

The set of database functions in Datomic Cloud is fixed, and is documented under Built-In Transaction Functions.

You can write your own classpath functions and deploy them using Ions.