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] |
                              [db-fn db-fn-args])
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
db-fn-arg                  = (value | [value] | {value value})

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] |
                              [db-fn db-fn-args])

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] |
                              [db-fn db-fn-args])
db-fn                      = identifier
db-fn-arg                  = (value | [value] | {value value})

Transaction functions are code that is stored in the database and executed inside of a transaction. Transaction functions provide semanticss 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.

Individual transaction functions are documented under Transaction Functions.