https://github.com/tonsky/datascript.git
git clone 'https://github.com/tonsky/datascript.git'
(ql:quickload :tonsky.datascript)
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:
[datascript "0.18.4"]
Support:
Books:
Docs:
Posts:
Talks:
Projects using DataScript:
Related projects:
Demo applications:
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]]]
DataScript can be used from any JS engine without additional dependencies:
<script src="https://github.com/tonsky/datascript/releases/download/0.18.4/datascript-0.18.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:
q
are returned as regular JS arraysEntities:
entity
call are lazy as in Clojuree.get("prop")
, e.get(":db/id")
, e.db
to access entity propertiesTransactions:
":db/id"
, ":db/add"
, etc. instead of db-namespaced keywordstransact
and db_with
Transaction reports:
report.tempids
has string keys ("-1"
for entity tempid -1
), use resolve_tempid
to set up a correspondenceCheck out test/js/tests.js for usage examples.
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:
:db/cardinality :db.cardinality/many
:db/valueType :db.type/ref
auto-expansiontransact!
listen!
datoms
and seek-datoms
filter
Query engine features:
:in
clause:in
clauseInterface differences:
@conn
instead of (d/db conn)
#db/id[:db.part/user -100]
just use -100
in place of :db/id
or entity id[:db.fn/call f args]
where f
is a function reference and will take db as first argument (thx @thegeez)resolve
in CLJS):find (aggregate ?myfn ?e) :in $ ?myfn
:db.fn/retractAttribute
shortcut:db/txInstant
Expected soon:
:db/ident
attributes, keywords are literally attribute values, no integer id behind themAimed 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”.
Copyright © 2014–2018 Nikita Prokopov
Licensed under Eclipse Public License (see LICENSE).