How to Setup Datomic Free with Clojure

23 January 2017

This Tutorial focuses on getting up and running with Datomic Free in your’ Clojure project. Datomic Free is recommended for open source applications. It includes a memory database and embedded Datomic Datalog, but it is limited to two simultaneous peers.

Download Datomic

To get started download the latest Datomic Free version from the Datomic website. Extract the .zip files locally and run the following commands in the system shell from the root directory of the Datomic Free install:

$ cd /home/user/datomic/datomic-free-<VERSION>/ #Change to the root directory and replace <VERSION> with your download version


Add Datomic to your Clojure Project

Add Datomic to the list of dependencies in your project.clj.

(defproject datomic_app "0.1.0-SNAPSHOT"
  :description "Datomic Example Setup"
  :url "http://example.com/FIXME"
  :license {:name "Eclipse Public License"
            :url "http://www.eclipse.org/legal/epl-v10.html"}
  :dependencies [[org.clojure/clojure "1.9.0-alpha14"]
                 [com.datomic/datomic-free "0.9.5544"]] # Change to your download version 
  :main ^:skip-aot datomic-app.core
  :target-path "target/%s"
  :profiles {:uberjar {:aot :all}})

Include the datomic.api in the namespace of the core.clj file.

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

Start Datomic Transactor

In order to transact data to your database you need to start a local transactor. Data will only be saved to the Datomic database when the transactor is running. You can edit the transactor.properties file (datomic-free-0.9.5544/config/samples/free-transactor-template.properties) to insert a license key for example, but this is not necessary for the Datomic Free version. Start a local transactor from your system shell from the root directory of the Datomic Free install:

$ bin/transactor path/to/free-transactor-template.properties # Replace path/to/ with the path to the file.

You will see the following output:

Launching with Java options -server -Xms1g -Xmx1g -XX:+UseG1GC -XX:MaxGCPauseMillis=50
Starting datomic:free://localhost:4334/<DB-NAME>, storing data in: data ...
System started datomic:free://localhost:4334/<DB-NAME>, storing data in: data

Datomic Free uses an H2 database engine) and stores your data in the data folder of the Datomic Free root directory.

Create a Database

Add the following code to your core.clj file to create a Datomic database:

(def  uri "datomic:free://localhost:4334/example") #Change example to the name of your database

(d/create-database uri)

(def conn (d/connect uri))

Transact Data

Datoms represent the data or facts that a Datomic database stores. The set of attibutes a datom can specify is defnined by the database’s schema. A schema describes the set of attibutes that can be associated with entities in each Datomic Database. See the Datomic Schema Documentation for more information about Datomic schemas.

There are three required schema attributes:

  • :db/ident
  • :db/valueType
  • :db/cardinality.

Below is an example of a simple schema for adding user email addresses of individuals in the Datomic database:

Schema Example 1

#Schema for email entity
(def schema
  [{:db/id #db/id[:db.part/db]
    :db/ident :person/email
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/unique :db.unique/value
    :db/doc "The email of the person"
    :db.install/_attribute :db.part/db}])

(d/transact conn schema)

#Add user email addresses to database
(def add-emails
  [{:db/id #db/id[:db.part/user -1000001] :person/email "example@gmail.com"}
   {:db/id #db/id[:db.part/user -1000002] :person/email "john@gmail.com"}
   {:db/id #db/id[:db.part/user -1000003] :person/email "jane@gmail.com"}])

(d/transact conn add-emails)

Note that partition edn tagged data literals may not be used in combination with Datomic tempid when transacting data to the database i.e.{:db/id #db/id[:db.part/user -1000001] :name/email “example@gmail.com”}. A partition may be specified in some of the following ways when transacting data to the database:

{:db/id #db/id[:db.part/user -1000001]  :name/email "example@gmail.com"} #Using edn tagged data literals and providing a temporary id

{:db/id (d/tempid :db.part/user)  :name/email "example@gmail.com"} #Using tempids

{:db/id (d/tempid :db.part/user -9999999)  :name/email "example@gmail.com"} #Using tempids with numerical temporary id

More information about transactions is provided in the Datomic documentation.

#Query database for all email address entities ids
(let [db (d/db conn)]  #Make sure you use the latest db with all the above changes added
   (d/q '[:find ?e
          :in $
          :where
          [?e :person/email _]]
        db))

=> #{[17592186045418] [17592186045419] [17592186045420]}

Schema Example 2

Entities can refer to other entities with attributes of reference type. When you create a reference type, Datomic automatically indexes the reference in both directions. An example of a schema with reference types:

(def schema
  [{:db/id #db/id[:db.part/db]
    :db/ident :suburb/name
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/doc "The name of the suburb"
   :db.install/_attribute :db.part/db}

   {:db/id #db/id[:db.part/db]
    :db/ident :suburb/city
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/doc "The name of the city the suburb is located in"
    :db.install/_attribute :db.part/db}

   {:db/id #db/id[:db.part/db]
    :db/ident :city/province
    :db/valueType :db.type/ref
    :db/cardinality :db.cardinality/one
    :db/doc "The name of the province in which the city is located"
    :db.install/_attribute :db.part/db}

    {:db/id #db/id[:db.part/db]
    :db/ident :province/name
    :db/valueType :db.type/string
    :db/cardinality :db.cardinality/one
    :db/doc "The name of the province in which the city is located"
    :db.install/_attribute :db.part/db}])


(d/transact conn schema-3)

(def add-suburbs
  [{:db/id #db/id [:db.part/suburb -1000076]   :suburb/name "Bellville" :suburb/city "Cape Town" :province/name "Western Cape"}
   {:db/id #db/id [:db.part/suburb -1000089] :suburb/name "Athlone" :suburb/city "Cape Town" :province/name "Western Cape"}
   {:db/id (d/tempid :db.part/suburb) :suburb/name "Rondebosch" :suburb/city "Cape Town" :province/name "Western Cape"}])


(d/transact conn add-suburbs)

String tempids create entities in the default partition for the transactor process. The default partition of the Datomic database can be set by editing the transactor properties file. If no partition is specified the transactor will use the built-in :db.part/user partition. New partitions, like :db.part/suburb in example 2 above, can be created by following the Datomic Schema Documentation.

Create a Database Backup

A database backup cannot be created for an in memory database. To backup your database start the transactor and run the following command in the system shell from the root directory of the Datomic Free install with the uri of your database and the path to your backup uri:

$ bin/datomic -Xmx4g -Xms4g backup-db from-db-uri to-backup-uri #Change uri paths

The following backs up the example database to a local backup folder in the Datomic Free root directory.

$ bin/datomic -Xmx4g -Xms4g backup-db datomic:free://localhost:4334/example file:backup

Use the Datomic Console

The Datomic Console is a graphical UI for datamic databases. It may not be used to view an in memory database. To use the Datomic console, first follow the instructions in the README-CONSOLE file of the root folder.

Resources