SkillAgentSearch skills...

Datascript

Immutable database and Datalog query engine for Clojure, ClojureScript and JS

Install / Use

/learn @tonsky/Datascript
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<img src="./extras/logo.svg">

What if creating a database would be as cheap as creating a Hashmap?

An immutable in-memory database and Datalog query engine in Clojure and ClojureScript.

DataScript is meant to run inside the browser. It is cheap to create, quick to query and ephemeral. You create a database on page load, put some data in it, track changes, do queries and forget about it when the user closes the page.

DataScript databases are immutable and based on persistent data structures. In fact, they’re more like data structures than databases (think Hashmap). Unlike querying a real SQL DB, when you query DataScript, it all comes down to a Hashmap lookup. Or series of lookups. Or array iteration. There’s no particular overhead to it. You put a little data in it, it’s fast. You put in a lot of data, well, at least it has indexes. That should do better than you filtering an array by hand anyway. The thing is really lightweight.

The intention with DataScript is to be a basic building block in client-side applications that needs to track a lot of state during their lifetime. There’s a lot of benefits:

  • Central, uniform approach to manage all application state. Clients working with state become decoupled and independent: rendering, server sync, undo/redo do not interfere with each other.
  • Immutability simplifies things even in a single-threaded browser environment. Keep track of app state evolution, rewind to any point in time, always render consistent state, sync in background without locking anybody.
  • Datalog query engine to answer non-trivial questions about current app state.
  • Structured format to track data coming in and out of DB. Datalog queries can be run against it too.

Latest version Build Status

;; lein
[datascript "1.7.8"]
;; deps.edn
datascript/datascript {:mvn/version "1.7.8"}

Important! If you are using shadow-cljs, add

:compiler-options {:externs ["datascript/externs.js"]}

to your build (see #432 #298 #216)

Resources

Support:

Books:

Docs:

Posts:

Talks:

  • “Frontend with Joy” talk (FPConf, August 2015): video in Russian
  • “Programming Web UI with Database in a Browser” talk (PolyConf, July 2015): slides, video
  • “DataScript for Web Development” talk (Clojure eXchange, Dec 2014): slides, video
  • “Building ToDo list with DataScript” webinar (ClojureScript NYC, Dec 2014): video, app
  • DataScript hangout (May 2014, in Russian): video

Projects using DataScript:

Related projects:

  • DataScript-Transit, transit serialization for database and datoms
  • DataScript SQL Storages, durable storage implementations for popular SQL databases
  • Posh, lib that lets you use a single DataScript db to store Reagent app state
  • re-posh, use re-frame with DataScript storage
  • DataScript-mori, DataScript & Mori wrapper for use from JS
  • DatSync, Datomic ↔︎ DataScript syncing/replication utilities
  • Intension, lib to convert associative structures to in-memory databases for querying them
  • Datamaps, lib designed to leverage datalog queries to query arbitrary maps.

Demo applications:

Usage examples

For more examples, see our acceptance test suite.

(require '[datascript.core :as d])

;; Implicit join, multi-valued attribute

(let [schema {:aka {:db/cardinality :db.cardinality/many}}
      conn   (d/create-conn schema)]
  (d/transact! conn [ { :db/id -1
                        :name  "Maksim"
                        :age   45
                        :aka   ["Max Otto von Stierlitz", "Jack Ryan"] } ])
  (d/q '[ :find  ?n ?a
          :where [?e :aka "Max Otto von Stierlitz"]
                 [?e :name ?n]
                 [?e :age  ?a] ]
       @conn))

;; => #{ ["Maksim" 45] }


;; Destructuring, function call, predicate call, query over collection

(d/q '[ :find  ?k ?x
        :in    [[?k [?min ?max]] ...] ?range
        :where [(?range ?min ?max) [?x ...]]
               [(even? ?x)] ]
      { :a [1 7], :b [2 4] }
      range)

;; => #{ [:a 2] [:a 4] [:a 6] [:b 2] }


;; Recursive rule

(d/q '[ :find  ?u1 ?u2
        :in    $ %
        :where (follows ?u1 ?u2) ]
      [ [1 :follows 2]
        [2 :follows 3]
        [3 :follows 4] ]
     '[ [(follows ?e1 ?e2)
         [?e1 :follows ?e2]]
        [(follows ?e1 ?e2)
         [?e1 :follows ?t]
         (follows ?t ?e2)] ])

;; => #{ [1 2] [1 3] [1 4]
;;       [2 3] [2 4]
;;       [3 4] }


;; Aggregates

(d/q '[ :find ?color (max ?amount ?x) (min ?amount ?x)
        :in   [[?color ?x]] ?amount ]
     [[:red 10]  [:red 20] [:red 30] [:red 40] [:red 50]
      [:blue 7] [:blue 8]]
     3)

;; => [[:red  [30 40 50] [10 20 30]]
;;     [:blue [7 8] [7 8]]]

Using from vanilla JS

DataScript can be used from any JS engine without additional dependencies:

<script src="https://github.com/tonsky/datascript/releases/download/1.7.8/datascript-1.7.8.min.js"></script>

or as a CommonJS module (npm page):

npm install datascript
var ds = require('datascript');

or as a RequireJS module:

require(['datascript'], function(ds) { ... });

Queries:

  • Query and rules should be EDN passed as strings
  • Results of q are returned as regular JS arrays

Entities:

  • Entities returned by entity call are lazy as in Clojure
  • Use e.get("prop"), e.get(":db/id"), e.db to access entity properties
  • Entities implement ECMAScript 6 Map interface (has/get/keys/...)

Transactions:

  • Use strings such as ":db/id", ":db/add", etc. instead of db-namespaced keywords
  • Use regular JS arrays and objects to pass data to transact and db_with

Transaction reports:

  • report.tempids has string keys ("-1" for entity tempid -1), use resolve_tempid to set up a correspondence

Check out test/js/tests.js for usage examples.

Project status

St

View on GitHub
GitHub Stars5.7k
CategoryData
Updated16h ago
Forks316

Languages

Clojure

Security Score

100/100

Audited on Mar 27, 2026

No findings