datascript
Immutable database and Datalog query engine for Clojure, ClojureScript and JS
Top Related Projects
A fast, immutable, distributed & compositional Datalog engine for everyone.
Quick Overview
Datascript is a Clojure/ClojureScript library that provides a powerful and flexible in-memory database solution. It allows developers to store and query data using a Datalog-inspired query language, making it a great choice for building applications that require efficient data management and querying capabilities.
Pros
- Powerful Query Language: Datascript's Datalog-inspired query language provides a concise and expressive way to interact with data, making it easy to perform complex queries and data transformations.
- Immutable Data Structure: Datascript uses an immutable data structure, which makes it easy to manage and reason about data changes, and supports efficient transactional updates.
- ClojureScript Support: Datascript is available for both Clojure and ClojureScript, allowing developers to use it in both server-side and client-side applications.
- Flexible and Extensible: Datascript is designed to be highly flexible and extensible, with support for custom data types and functions, making it suitable for a wide range of use cases.
Cons
- Learning Curve: The Datalog-inspired query language used by Datascript may have a steeper learning curve for developers who are more familiar with traditional SQL-based databases.
- Limited Persistence Options: Datascript is an in-memory database, which means that data is not persisted by default. Developers may need to integrate Datascript with a separate persistence layer, such as a file-based storage or a traditional database.
- Performance Limitations: As an in-memory database, Datascript may have performance limitations for very large datasets or high-concurrency scenarios, where a traditional database might be more suitable.
- Ecosystem Maturity: Compared to some other database solutions, the Datascript ecosystem may be less mature, with fewer third-party libraries and tools available.
Code Examples
Here are a few examples of how to use Datascript:
- Creating a Database and Inserting Data:
(require '[datascript.core :as d])
;; Create a new database
(def db (d/create-conn))
;; Insert some data
(d/transact! db [{:db/id 1 :name "Alice" :age 30}
{:db/id 2 :name "Bob" :age 25}
{:db/id 3 :name "Charlie" :age 35}])
- Querying the Database:
;; Query the database for people older than 30
(d/q '[:find ?e ?name ?age
:where [?e :name ?name]
[?e :age ?age]
[(> ?age 30)]]
@db)
- Updating Data:
;; Update Bob's age
(d/transact! db [{:db/id 2 :age 26}])
- Reverting Changes:
;; Revert the previous transaction
(d/transact! db [[:db/retract 2 :age 26]])
Getting Started
To get started with Datascript, you can add the following dependency to your project:
[datascript "1.2.9"]
Then, you can start using Datascript in your Clojure or ClojureScript code. Here's a simple example to get you started:
(require '[datascript.core :as d])
;; Create a new database
(def db (d/create-conn))
;; Insert some data
(d/transact! db [{:db/id 1 :name "Alice" :age 30}
{:db/id 2 :name "Bob" :age 25}
{:db/id 3 :name "Charlie" :age 35}])
;; Query the database
(d/q '[:find ?e ?name ?age
:where [?e :name ?name]
[?e :age ?age]
[(> ?age 30)]]
@db)
This example demonstrates how to create a new
Competitor Comparisons
A fast, immutable, distributed & compositional Datalog engine for everyone.
Pros of Datahike
- Supports persistent storage and durability
- Offers ACID transactions
- Provides temporal querying capabilities
Cons of Datahike
- Generally slower performance than Datascript
- More complex setup and configuration
- Smaller community and ecosystem
Code Comparison
Datascript:
(require '[datascript.core :as d])
(def conn (d/create-conn))
(d/transact! conn [{:db/id -1 :name "Alice" :age 30}])
(d/q '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]] @conn)
Datahike:
(require '[datahike.api :as d])
(def cfg {:store {:backend :file :path "/tmp/example"}})
(d/create-database cfg)
(def conn (d/connect cfg))
(d/transact conn [{:db/id -1 :name "Alice" :age 30}])
(d/q '[:find ?n ?a :where [?e :name ?n] [?e :age ?a]] @conn)
Both Datascript and Datahike are Datalog-based databases for Clojure, but they have different focuses. Datascript is designed for in-memory usage and offers excellent performance for client-side applications. Datahike, on the other hand, provides persistence and ACID transactions, making it suitable for server-side applications that require data durability. The code comparison shows that while the basic usage is similar, Datahike requires additional configuration for storage backend.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
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 
;; lein
[datascript "1.7.4"]
;; deps.edn
datascript/datascript {:mvn/version "1.7.4"}
Important! If you are using shadow-cljs, add
:compiler-options {:externs ["datascript/externs.js"]}
to your build (see #432 #298 #216)
Support us
Resources
Support:
- Join #datascript on Clojurians Slack (grab invite here)
Books:
- Learning ClojureScript has a chapter on DataScript
Docs:
- API Docs
- docs/queries.md
- docs/tuples.md
- docs/storage.md
- Getting started
- Tutorials
- Tips & tricks
- Triple Stores overview
- Quick tutorial into Datalog
Posts:
- How DataScript fits into the current webdev ecosystem
- DataScript internals explained
- Sketch of client/server reactive architecture
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:
- Roam Research, a note-taking graph database
- LogSeq, a local-first, non-linear, outliner notebook
- Athens Research, a tool for networked thought
- Hulunote, a tool for networked thought
- Cognician, coaching platform
- LightMesh, datacenter management
- PartsBox, electronic parts management
- I am Fy, accessories e-shop
- Under Whose Command?, an interactive report by Security Force Monitor
- gluino.io, an AI with swagger
- Pinfigurator, a microcontroller search tool
- Precursor, collaborative prototyping tool
- Acha-acha, GitHub achievements
- Showkr, flickr gallery viewer (sources)
- Zetawar, turn-based tactical strategy game
- Lemmings, incubator focused on art & artificial intelligence
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:
- Localisation Demo with Om Next
- ToDo, task manager demo app (persistence via localStorage and transit, filtering, undo/redo): sources, live
- CatChat, chat demo app: sources, code walkthrough, live
- clj-crud, demo CRUD app: sources, blog post
- OmNext TodoMVC
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.4/datascript-1.7.4.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
anddb_with
Transaction reports:
report.tempids
has string keys ("-1"
for entity tempid-1
), useresolve_tempid
to set up a correspondence
Check out test/js/tests.js for usage examples.
Project status
Stable. Most of the features done, expecting non-breaking API additions and performance optimizations. No docs at the moment, use examples & Datomic documentation.
The following features are supported:
- Database as a value: each DB is an immutable value. New DBs are created on top of old ones, but old ones stay perfectly valid too
- Triple store model
- EAVT, AEVT and AVET indexes
- Multi-valued attributes via
:db/cardinality :db.cardinality/many
- Lazy entities and
:db/valueType :db.type/ref
auto-expansion - Database âmutationsâ via
transact!
- Callback-based analogue to txReportQueue via
listen!
- Direct index lookup and iteration via
datoms
andseek-datoms
- Filtered databases via
filter
- Lookup refs
- Unique constraints, upsert
- Pull API (thx David Thomas Hume)
Query engine features:
- Implicit joins
- Query over DB or regular collections
- Parameterized queries via
:in
clause - Tuple, collection, relation binding forms in
:in
clause - Query over multiple DB/collections
- Predicates and user functions in query
- Negation and disjunction
- Rules, recursive rules
- Aggregates
- Find specifications
Interface differences:
- Conn is just an atom storing last DB value, use
@conn
instead of(d/db conn)
- Instead of
#db/id[:db.part/user -100]
just use-100
in place of:db/id
or entity id - Transactor functions can be called as
[:db.fn/call f args]
wheref
is a function reference and will take db as first argument (thx @thegeez) - In ClojureScript, custom query functions and aggregates should be passed as source instead of being referenced by symbol (due to lack of
resolve
in CLJS) - Custom aggregate functions are called via aggregate keyword:
:find (aggregate ?myfn ?e) :in $ ?myfn
- Additional
:db.fn/retractAttribute
shortcut - Transactions are not annotated by default with
:db/txInstant
- When âtransaction functionâ is called, the db that this function receive is a âpartial dbâ relative to it's position in transaction.
Differences from Datomic
- DataScript is built totally from scratch and is not related by any means to the popular Clojure database Datomic
- Runs in a browser and/or in a JVM
- Simplified schema, not queryable
- Attributes do not have to be declared in advance. Put them to schema only when you need special behaviour from them
- Any type can be used for values
- No
:db/ident
for attributes, keywords are literally attribute values, no integer id behind them - No schema migrations
- No full-text search, no partitions
- No external dependencies
Aimed at interactive, long-living browser applications, DataScript DBs operate in constant space. If you do not add new entities, just update existing ones, or clean up database from time to time, memory consumption will be limited. This is unlike Datomic which keeps history of all changes, thus grows monotonically. DataScript does not track history by default, but you can do it via your own code if needed.
Some of the features are omitted intentionally. Different apps have different needs in storing/transfering/keeping track of DB state. DataScript is a foundation to build exactly the right storage solution for your needs without selling too much âvisionâ.
Contributing
Testing
Setup
npm install ws
Running the tests
clj -M:test -m kaocha.runner
Watching tests:
./script/watch.sh
Benchmarking and Datomic compatibility
datomic-free
is a dependency not available on Clojars or Maven Central.
- Download
datomic-free
from https://my.datomic.com/downloads/free - Unzip it
- Inside the unzipped folder run
./bin/maven-install
Run compatibility checks:
clj -M:datomic
Benchmark:
cd bench
./bench.clj
License
Copyright © 2014â2024 Nikita Prokopov
Licensed under Eclipse Public License (see LICENSE).
Top Related Projects
A fast, immutable, distributed & compositional Datalog engine for everyone.
Convert
designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot