Assertion
Create and Connect
An assertion requires a database to exist and a connection.
- Utilize a configuration map as described in the client API tutorial:
noslide
(require '[datomic.client.api :as d]) (def cfg {:server-type :dev-local :system "datomic-samples"}) (def client (d/client cfg))
- Create a new database with
create-database
:
noslide
(d/create-database client {:db-name "tutorial"})
- Acquire a connection to the database with
connect
:
noslide
(def conn (d/connect client {:db-name "tutorial"}))
List and Map Forms
An assertion adds a single atomic fact to Datomic. Assertions are represented by ordinary data structures (lists or maps). Our inventory database will need to have enumerated types for various product attributes such as color.
- Let's start with an edn list that asserts the color green:
noslide
[:db/add "foo" :db/ident :green]
:db/add
specifies that this is an assertion- "Foo" is a temporary entity id for the new entity
:db/ident
is an attribute used for programmatic identifiers:green
is the datom's value
- The same datom can be represented by a map:
noslide
{:db/ident :green}
Maps imply (and are equivalent to) a set of assertions, all about the same entity.
Transactions
Datomic databases are updated via ACID transactions, which add a set of datoms to the database. Execute the code below at a Clojure REPL to add colors to the inventory database in a single transaction.
The transaction below adds four colors to the database:
noslide
(d/transact conn {:tx-data [{:db/ident :red} {:db/ident :green} {:db/ident :blue} {:db/ident :yellow}]})
=> ;; returns a big map
A successful transaction returns a map with information about the transaction and the state of the database. It will be explored later.
Programming with Data
In addition to colors, our inventory database will also track sizes and types of items. Since we are programming with data, it is easy to write a helper function to make these transactions more concise.
- The
make-idents
function shown below will take a collection of keywords, and return transaction-ready maps:
noslide
(defn make-idents [x] (mapv #(hash-map :db/ident %) x))
- You can quickly notice that this works by trying it out at the REPL:
noslide
(def sizes [:small :medium :large :xlarge]) (make-idents sizes)
=> [#:db{:ident :small} #:db{:ident :medium} #:db{:ident :large} #:db{:ident :xlarge}]
Note that because make-idents
function takes and returns pure data,
no database is necessary to develop and test this function.
The #:db
before the map indicates a namespaced map.
- Let's put types, colors, and sizes into the database and define a collection of colors we already added:
noslide
(def types [:shirt :pants :dress :hat]) (def colors [:red :green :blue :yellow]) (d/transact conn {:tx-data (make-idents sizes)}) (d/transact conn {:tx-data (make-idents colors)}) (d/transact conn {:tx-data (make-idents types)})
Schema
So far we have used only built-in schema such as :db/ident
. Now we
want to add some inventory-specific attributes:
- sku, a unique string identifier for a particular product
- color, a reference to a color entity
- size, a reference to a size entity
- type, a reference to a type entity
In Datomic, schema are entities just like program data. A schema entity must include:
- :db/ident, a programmatic name
- :db/valueType, a reference to an entity that specifies what type the attribute allows
- :db/cardinality, a reference to an entity that specifies whether a particular entity can possess more than one value for the attribute at a given time.
So we can add our schema like this:
noslide
(def schema-1 [{:db/ident :inv/sku :db/valueType :db.type/string :db/unique :db.unique/identity :db/cardinality :db.cardinality/one} {:db/ident :inv/color :db/valueType :db.type/ref :db/cardinality :db.cardinality/one} {:db/ident :inv/size :db/valueType :db.type/ref :db/cardinality :db.cardinality/one} {:db/ident :inv/type :db/valueType :db.type/ref :db/cardinality :db.cardinality/one}]) (d/transact conn {:tx-data schema-1})
Notice that the :inv/sku
attribute also has a :db/unique
value. This specifies that every :inv/sku
must be unique within the
database.
Sample Data
Now let's make some sample data. Again, no special API is necessary, we can just use ordinary Clojure collections. The following expression creates one example inventory entry for each combination of color, size, and type:
noslide
;;; Defined earlier, but repeated for clarity (def colors [:red :green :blue :yellow]) (def sizes [:small :medium :large :xlarge]) (def types [:shirt :pants :dress :hat]) (defn create-sample-data "Create a vector of maps of all permutations of args" [colors sizes types] (->> (for [color colors size sizes type types] {:inv/color color :inv/size size :inv/type type}) (map-indexed (fn [idx map] (assoc map :inv/sku (str "SKU-" idx)))) vec)) ;; 64 (4 x 4 x 4) maps @(def sample-data (create-sample-data colors sizes types)) @(def sample-data-transaction (d/transact conn {:tx-data sample-data}))
Now that we have asserted some data, let's look at some different ways we can retrieve it.