• Stars
    star
    5,353
  • Rank 7,329 (Top 0.2 %)
  • Language
    Clojure
  • License
    Eclipse Public Li...
  • Created about 10 years ago
  • Updated 11 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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.6.3"]
;; deps.edn
datascript/datascript {:mvn/version "1.6.3"}

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:

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.6.3/datascript-1.6.3.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

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 and seek-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] where f 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

Expected soon:

  • Better error reporting
  • Proper documentation

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 attributes, keywords are literally attribute values, no integer id behind them
  • No schema migrations
  • No cache segments management, no laziness. Entire DB must reside in memory
  • No facilities to persist, transfer over the wire or sync DB with the server
  • No pluggable storage options, no full-text search, no partitions
  • No external dependencies
  • Free

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.

  1. Download datomic-free from https://my.datomic.com/downloads/free
  2. Unzip it
  3. Inside the unzipped folder run ./bin/maven-install

Run compatibility checks:

clj -M:datomic

Benchmark:

cd bench
./bench.clj

License

Copyright © 2014–2021 Nikita Prokopov

Licensed under Eclipse Public License (see LICENSE).

More Repositories

1

FiraCode

Free monospaced font with programming ligatures
Clojure
74,361
star
2

AnyBar

OS X menubar status indicator
Objective-C
5,856
star
3

rum

Simple, decomplected, isomorphic HTML UI library for Clojure and ClojureScript
HTML
1,767
star
4

vscode-theme-alabaster

A light theme for Visual Studio Code
Clojure
386
star
5

Clojure-Sublimed

Clojure support for Sublime Text 4
Clojure
349
star
6

tongue

Do-it-yourself i18n library for Clojure/Script
Clojure
305
star
7

uberdeps

Uberjar builder for deps.edn
Clojure
299
star
8

Universal-Layout

Пакет из английской и русской раскладок, спроектированных для удобного совместного использования
Shell
291
star
9

font-writer

Monospaced font for long-form writing
242
star
10

sublime-scheme-alabaster

Minimalist color scheme for Sublime Text 3
234
star
11

datascript-chat

Sample SPA using DataScript and core.async
Clojure
160
star
12

grumpy

Minimalistic blog engine
Clojure
141
star
13

compact-uuids

Compact 26-char URL-safe representation of UUIDs
Clojure
126
star
14

net.async

Network commucations with clojure.core.async interface
Clojure
123
star
15

sublime-scheme-writer

A color scheme for focused long-form writing
119
star
16

clojure-future-spec

A backport of clojure.spec for Clojure 1.8
Clojure
115
star
17

intellij-alabaster

Alabaster color scheme for IntelliJ IDEA
102
star
18

datascript-transit

Transit handlers for DataScript database and datoms
Clojure
100
star
19

sublime-profiles

Profile Switcher for Sublime Text
Python
81
star
20

datascript-todo

DataScript ToDo Sample Application
Clojure
78
star
21

persistent-sorted-set

Fast B-tree based persistent sorted set for Clojure/Script
Clojure
78
star
22

tonsky.github.io

HTML
65
star
23

clojure-warrior

Visual Studio Code extension for Clojure development
TypeScript
57
star
24

cljs-drag-n-drop

Sane wrapper around Drag-n-Drop DOM API
Clojure
55
star
25

vec

React.js + Immutable.js vector editor
JavaScript
51
star
26

clojure.unicode

Unicode symbols for Clojure
Clojure
48
star
27

clj-simple-router

Simple order-independent Ring router
Clojure
48
star
28

41-socks

Simple match game in cljs+om+react
Clojure
37
star
29

remote-require

Require any Clojure snippet from anywhere in the Internet
Clojure
33
star
30

Sublime-Executor

Run any executable from your working dir in Sublime Text
Python
32
star
31

cljs-skeleton

Skeleton CLJS client/server app with WS, Transit, Rum
Clojure
30
star
32

Heroes

A turn-based tactical game in ClojureScript, DataScript and Rum
Clojure
30
star
33

icfpc2019-rust

Re-implementaion of https://github.com/tonsky/icfpc2019 in Rust to compare performance
Rust
28
star
34

alabaster-lighttable-skin

Light skin & theme for LightTable
CSS
27
star
35

clj-reload

Clojure
27
star
36

openshift-clojure

Clojure/lein openshift cartridge template
Shell
26
star
37

datascript-storage-sql

SQL Storage implementation for DataScript
Clojure
23
star
38

sublime-scheme-commander

Retro color scheme for Sublime Text
23
star
39

sublime-clojure-repl

Basic Clojure REPL for Sublime Text
Python
22
star
40

Levinson-Layout

Keymap & EN/RU layouts for Levinson 40% split ortholinear keyboard
C
21
star
41

boot-anybar

A boot task reporting build status to AnyBar
Clojure
18
star
42

extend-clj

Easily extend clojure.core built-in protocols
Clojure
17
star
43

down-the-rabbit-hole

Entry to Ludum Dare 48
Clojure
17
star
44

bloknote

Fast online notepad
Clojure
16
star
45

sublime-color-schemes

Fun and simple color schemes for Sublime Text
Rust
16
star
46

katybot

Campfire bot written in Clojure
Clojure
15
star
47

toml-clj

Fast TOML parser for Clojure
Java
14
star
48

java-graphics-benchmark

Java Graphics benchmark
Java
13
star
49

Helix-Layout

C
13
star
50

sane-math

Clojure/Script library for infix (normal) math expressions
Clojure
12
star
51

datascript-menu

JavaScript
11
star
52

DarkModeToggle

Statusbar app to quickly toggle between light and dark modes
Swift
11
star
53

icfpc2021

Clojure
11
star
54

humble-ants

Clojure
10
star
55

advent-of-code

https://adventofcode.com/
Clojure
9
star
56

icfpc2019

Clojure
7
star
57

tonsky.me

Clojure
7
star
58

tgadmin

Clojure
7
star
59

jwm

Objective-C++
6
star
60

homm

Clojure
5
star
61

GMTKJam2022

GDScript
5
star
62

advent2018

Solutions to https://adventofcode.com/2018 in Clojure
Clojure
5
star
63

spectre

Fantom
3
star
64

imdbparse

Parser for IMDb text database
Clojure
3
star
65

icfpc2022

Clojure
3
star
66

clojure-bits

Clojure
3
star
67

tonsky

2
star
68

2017-10-Reactive

JavaScript
2
star
69

2017-05-RigaDevDays

JavaScript
2
star
70

clojure-bits-server

Clojure
2
star
71

lein-figwheel-immutant

[tonsky/figwheel-sidecar-immutant "0.5.9"]
Clojure
2
star
72

2018-05-UWDC

http://tonsky.me/2018-05-UWDC/slides/
JavaScript
2
star
73

codingame-fall-2022

Coding Games Fall Challenge 2022
Clojure
2
star
74

datascript-perf

Datasets for DataScript perf testing
Clojure
2
star
75

grumpy_video

1
star
76

datascript_compiler_race

Clojure
1
star
77

roam-calculator

Shell
1
star
78

icfpc2023

Clojure
1
star
79

ldjam53

GDScript
1
star
80

glutin_resize_issue

Rust
1
star