Datahike supports two approaches to data validation: schema-on-read (flexible, validate later) and schema-on-write (strict, validate immediately). Choose at database creation time - this cannot be changed later.
| Aspect | Schema-on-read (:read) | Schema-on-write (:write, default) |
|---|---|---|
| Validation | None - accepts any data | Enforces types and structure |
| Use when | Prototyping, evolving schemas, heterogeneous data | Production systems, data integrity critical |
| Similar to | MongoDB, Redis, JSON documents | PostgreSQL, Datomic, traditional RDBMS |
| Trade-off | Flexibility vs. runtime errors | Safety vs. schema migration complexity |
Default: Datahike uses :write (schema-on-write) by default since version 0.2.0.
Schema-on-read accepts any data without validation. Set :schema-flexibility :read at database creation to enable this mode.
(require '[datahike.api :as d])
(def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440010"} :schema-flexibility :read})
(d/create-database cfg)
(def conn (d/connect cfg))
;; now you can add any arbitrary data
(d/transact conn {:tx-data [{:any "Data"}]})
Schema-on-write (the default) enforces type safety and structure. Define your schema before adding data. The schema itself is stored in the database, so you transact it like any other data.
(require '[datahike.api :as d])
;; since the :write approach is the default value we may also skip the setting
(def cfg {:store {:backend :memory :id #uuid "550e8400-e29b-41d4-a716-446655440011"} :schema-flexibility :write})
(d/create-database cfg)
(def conn (d/connect cfg))
;; define a simple schema
(def schema [{:db/ident :name :db/valueType :db.type/string :db/cardinality :db.cardinality/one}])
;; transact it
(d/transact conn {:tx-data schema})
;; now we can transact data based on the provided schema
(d/transact conn {:tx-data [{:name "Alice"}]})
The schema definition is for the most part compliant with Datomic's approach. Required are three attributes:
:db/ident: the name of the attribute, defined as a keyword with optional
namespace, e.g. :user/name:db/valueType: the type of the value associated with an attribute, e.g. db.type/string, see
below for supported types:db/cardinality: the cardinality of the value, whether the value is a single
value or a set of values, can be either :db.cardinality/one or db.cardinality/manyAdditionally, the following optional attributes are supported:
db/doc: the documentation for the attribute as a stringdb/unique: a uniqueness constraint on the attribute for a given value, can
be either db.unique/value (only one entity with this attribute can have the same value) or db.unique/identity(only one entity can have
the value for this attribute with upsert enabled)db/index: indicates whether an index for the attribute's value should be
created as a booleandb/isComponent: indicates that an attribute of type :db.type/ref references a subcomponent of the entity that has the attribute (for cascading retractions):db/valueType is :db.type/tuple, one of:
db/tupleAttrs: a collection of attributes that make up the tuple (for composite tuples)db/tupleTypes: a collection of 2-8 types that make up the tuple (for heterogeneous fixed length tuples)db/tupleType: the type of the tuple elements (for homogeneous variable length tuples)The following types are currently support in datahike:
| Value Type | Corresponding Type |
|---|---|
db.type/bigdec | java.math.BigDecimal |
db.type/bigint | java.math.BigInteger |
db.type/boolean | Boolean |
db.type/double | Double |
db.type/float | Double or Float |
db.type/instant | java.util.Date |
db.type/keyword | clojure.lang.Keyword |
db.type/long | java.lang.Long |
db.type/ref | java.lang.Long |
db.type/string | String |
db.type/symbol | clojure.lang.Symbol |
db.type/uuid | java.util.UUID |
db.type/tuple | clojure.lang.Vector |
The schema is validated using clojure.spec. For additional validation beyond schema, see Entity Specs.
Tuples store multiple values together as a single attribute. Datahike supports three tuple types:
Composite tuples combine existing attributes into a compound key:
;; Define the component attributes
(def schema [{:db/ident :user/first-name :db/valueType :db.type/string :db/cardinality :db.cardinality/one}
{:db/ident :user/last-name :db/valueType :db.type/string :db/cardinality :db.cardinality/one}
;; Composite tuple combining first and last name
{:db/ident :user/full-name
:db/valueType :db.type/tuple
:db/tupleAttrs [:user/first-name :user/last-name]
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}])
(d/transact conn {:tx-data schema})
(d/transact conn {:tx-data [{:user/first-name "Alice" :user/last-name "Smith"}]})
;; Query by the composite tuple
(d/q '[:find ?e :where [?e :user/full-name ["Alice" "Smith"]]] @conn)
Fixed-length tuples with different types for each position:
;; Geographic coordinate: [latitude longitude elevation]
(def schema [{:db/ident :location/coordinates
:db/valueType :db.type/tuple
:db/tupleTypes [:db.type/double :db.type/double :db.type/long]
:db/cardinality :db.cardinality/one}])
(d/transact conn {:tx-data schema})
(d/transact conn {:tx-data [{:location/coordinates [37.7749 -122.4194 52]}]})
Variable-length tuples where all elements have the same type:
;; RGB color values
(def schema [{:db/ident :color/rgb
:db/valueType :db.type/tuple
:db/tupleType :db.type/long
:db/cardinality :db.cardinality/one}])
(d/transact conn {:tx-data schema})
(d/transact conn {:tx-data [{:color/rgb [255 128 0]}]})
Updating existing schema is discouraged as it may cause data inconsistencies. Only updates to :db/cardinality and :db/unique are supported.
Recommended migration strategies:
(def user-schema
[{:db/ident :user/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "Unique user identifier"}
{:db/ident :user/email
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity
:db/doc "User email address"}
{:db/ident :user/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :user/roles
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/many
:db/doc "User roles (admin, editor, viewer)"}
{:db/ident :user/created-at
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db/index true}])
(def blog-schema
[{:db/ident :post/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}
{:db/ident :post/title
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :post/content
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :post/author
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "Reference to user entity"}
{:db/ident :post/tags
:db/valueType :db.type/string
:db/cardinality :db.cardinality/many}
{:db/ident :post/published-at
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one
:db/index true}])
(def org-schema
[{:db/ident :employee/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}
{:db/ident :employee/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :employee/manager
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "Reference to manager (also an employee)"}
{:db/ident :employee/department
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/one
:db/doc "Reference to department entity"}
{:db/ident :department/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}])
(def order-schema
[{:db/ident :order/id
:db/valueType :db.type/uuid
:db/cardinality :db.cardinality/one
:db/unique :db.unique/identity}
{:db/ident :order/items
:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many
:db/isComponent true
:db/doc "Line items are components - deleted when order is deleted"}
{:db/ident :item/product-id
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one}
{:db/ident :item/quantity
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one}
{:db/ident :item/price
:db/valueType :db.type/bigdec
:db/cardinality :db.cardinality/one}])
Can you improve this documentation? These fine people already did:
Konrad Kühne, Christian Weilbach, Johnny, Dmitry Podgorniy, Judith, JC & Alejandro GomezEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |