Skip to content

Commit

Permalink
Cosmetic changes to better mimic Datomic API
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Aug 27, 2014
1 parent 1d842bd commit 031e4f7
Show file tree
Hide file tree
Showing 11 changed files with 211 additions and 83 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.4.0

Cosmetic changes to better mimic Datomic API. Useful for sharing code between Datomic and DataScript:

- Added `tempid`, `resolve-tempid`, `db`, `transact`, `transact-async`, `index-range`, `squuid`, `squuid-time-millis`
- [ BREAKING ] renamed `transact` to `with`, `with` to `db-with`

# 0.3.1

- Optimized speed of DB’s `equiv` and `hash`, Datom’s `hash`
Expand All @@ -9,7 +16,7 @@
Proper entities implementation:

- Entities are now lazy and implement usual Map protocols
- When accessing attribute of `:db/valueType :db.type/ref`, its value will be automatically expanded to entites, allowing for recursive exploration of entities graphs (e.g. `(-> (d/entity db 42) :parent :parent :children)`)
- [ BREAKING ] When accessing attribute of `:db/valueType :db.type/ref`, its value will be automatically expanded to entites, allowing for recursive exploration of entities graphs (e.g. `(-> (d/entity db 42) :parent :parent :children)`)
- Entities support backwards navigation (e.g. `(:person/_friends (d/entity db 42))`)

# 0.2.1
Expand Down
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Also check out this blog post about [how DataScript fits into the current webdev
:dependencies [
[org.clojure/clojurescript "0.0-2311"]
...
[datascript "0.3.1"]
[datascript "0.4.0"]
]

;; for advanced optimizations externs are needed
Expand Down Expand Up @@ -103,10 +103,10 @@ Also check out this blog post about [how DataScript fits into the current webdev
DataScript can be used from any JS engine without additional dependencies:

```html
<script src="datascript-0.3.1.min.js"></script>
<script src="datascript-0.4.0.min.js"></script>
```

[Download datascript-0.3.1.min.js](https://github.com/tonsky/datascript/releases/download/0.3.1/datascript-0.3.1.min.js), 43k gzipped.
[Download datascript-0.4.0.min.js](https://github.com/tonsky/datascript/releases/download/0.4.0/datascript-0.4.0.min.js), 43k gzipped.

Queries:

Expand All @@ -122,12 +122,11 @@ Entities:
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 `with_datoms`
* 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`)
* `report.tx_data` is list of objects with `e`, `a`, `v`, `tx` and `added` keys (instead of Datoms)
* `report.tempids` has string keys (`"-1"` for entity tempid `-1`), use `resolve_tempid` set up a correspondence

Check out [test/js/js.html](test/js/js.html) for usage examples.

Expand Down Expand Up @@ -164,12 +163,12 @@ Interface differences:
* 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](https://github.com/thegeez))
* Additional `:db.fn/retractAttribute` shortcut
* `transact!` and `transact` return `TxReport`, `with` returns new DB value

Expected soon:

* Better error reporting
* Support for components in schema
* Support for `get-else`, `get-some`, `ground`, `missing?` query functions
* Proper documentation

## Differences from Datomic
Expand Down
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject datascript "0.3.1"
(defproject datascript "0.4.0"
:description "An implementation of Datomic in-memory database and Datalog query engine in ClojureScript"
:license {:name "Eclipse"
:url "http://www.eclipse.org/legal/epl-v10.html"}
Expand Down
77 changes: 69 additions & 8 deletions src/datascript.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,22 @@
(atom (empty-db schema)
:meta { :listeners (atom {}) }))

(defn transact [db entities]
(dc/transact-entities (dc/TxReport. db db [] {}) entities))
(defn with [db tx-data]
(dc/transact-tx-data (dc/TxReport. db db [] {}) tx-data))

(defn with [db entities]
(:db-after (transact db entities)))
(defn db-with [db tx-data]
(:db-after (with db tx-data)))

(defn -transact! [conn entities]
(defn -transact! [conn tx-data]
(let [report (atom nil)]
(swap! conn (fn [db]
(let [r (transact db entities)]
(let [r (with db tx-data)]
(reset! report r)
(:db-after r))))
@report))

(defn transact! [conn entities]
(let [report (-transact! conn entities)]
(defn transact! [conn tx-data]
(let [report (-transact! conn tx-data)]
(doseq [[_ callback] @(:listeners (meta conn))]
(callback report))
report))
Expand All @@ -67,6 +67,10 @@
(defn seek-datoms [db index & cs]
(btset/slice (get db index) (components->pattern index cs) (dc/Datom. nil nil nil nil nil)))

(defn index-range [db attr start end]
(btset/slice (:avet db) (dc/Datom. nil attr start nil nil)
(dc/Datom. nil attr end nil nil)))

;; printing and reading
;; #datomic/DB {:schema <map>, :datoms <vector of [e a v tx]>}

Expand Down Expand Up @@ -99,3 +103,60 @@
(apply btset/btset-by dc/cmp-datoms-avet datoms)
(reduce max 0 (map :e datoms))
(reduce max tx0 (map :tx datoms)))))


;; Datomic compatibility layer

(def last-tempid (atom -1000000))
(defn tempid
([_part] (swap! last-tempid dec))
([_part x] x))
(defn resolve-tempid [_db tempids tempid]
(get tempids tempid))

(def db deref)

(defn transact [conn tx-data]
(let [res (transact! conn tx-data)]
(reify
IDeref
(-deref [_] res)
IDerefWithTimeout
(-deref-with-timeout [_ _ _] res)
IPending
(-realized? [_] true))))

;; ersatz future without proper blocking
(defn- future-call [f]
(let [res (atom nil)
realized (atom false)]
(js/setTimeout #(do (reset! res (f)) (reset! realized true)) 0)
(reify
IDeref
(-deref [_] @res)
IDerefWithTimeout
(-deref-with-timeout [_ _ timeout-val] (if @realized @res timeout-val))
IPending
(-realized? [_] @realized))))

(defn transact-async [conn tx-data]
(future-call #(transact! conn tx-data)))

(defn- rand-bits [pow]
(rand-int (bit-shift-left 1 pow)))

(defn squuid []
(UUID.
(str
(-> (js/Date.) (.getTime) (/ 1000) (Math/round) (.toString 16))
"-" (-> (rand-bits 16) (.toString 16))
"-" (-> (rand-bits 16) (bit-and 0x0FFF) (bit-or 0x4000) (.toString 16))
"-" (-> (rand-bits 16) (bit-and 0x3FFF) (bit-or 0x8000) (.toString 16))
"-" (-> (rand-bits 16) (.toString 16))
(-> (rand-bits 16) (.toString 16))
(-> (rand-bits 16) (.toString 16)))))

(defn squuid-time-millis [uuid]
(-> (subs (.-uuid uuid) 0 8)
(js/parseInt 16)
(* 1000)))
2 changes: 1 addition & 1 deletion src/datascript/core.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@
(let [tx (current-tx report)]
(transact-report report (Datom. (.-e d) (.-a d) (.-v d) tx false))))

(defn- transact-entities [report [entity & entities :as es]]
(defn- transact-tx-data [report [entity & entities :as es]]
(let [db (:db-after report)]
(cond
(nil? entity)
Expand Down
4 changes: 2 additions & 2 deletions src/datascript/externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ datascript.core.Datom.added = true;
datascript.impl = {};
datascript.impl.entity = {};
datascript.impl.entity.Entity = {};
datascript.impl.entity.Entity.prototype.db = {};
datascript.impl.entity.Entity.prototype.eid = {};
datascript.impl.entity.Entity.db = {};
datascript.impl.entity.Entity.eid = {};

datascript.impl.entity.Entity.prototype.keys = function() {};
datascript.impl.entity.Entity.prototype.entries = function() {};
Expand Down
16 changes: 14 additions & 2 deletions src/datascript/js.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@
(into-array tuple))
(into-array))))

(defn ^:export with_datoms [db entities]
(d/with db (entities->clj entities)))
(defn ^:export db_with [db entities]
(d/db-with db (entities->clj entities)))

(def ^:export entity d/entity)
(def ^:export touch d/touch)
Expand All @@ -77,10 +77,22 @@

(def ^:export unlisten d/unlisten!)

(defn ^:export resolve_tempid [tempids tempid]
(aget tempids (str tempid)))

(defn ^:export datoms [db index & components]
(->> (apply d/datoms db (keywordize index) components)
into-array))

(defn ^:export seek_datoms [db index & components]
(->> (apply d/seek-datoms db (keywordize index) components)
into-array))

(defn ^:export index_range [db attr start end]
(into-array (d/index-range db attr start end)))

(defn ^:export squuid []
(.-uuid (d/squuid)))

(defn ^:export squuid_time_millis [uuid]
(d/squuid-time-millis (UUID. uuid)))
2 changes: 1 addition & 1 deletion src/datascript/preamble.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Datascript v0.3.1
* Datascript v0.4.0
*
* Copyright 2014 Nikita Prokopov
*
Expand Down
19 changes: 10 additions & 9 deletions test/js/js.html
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,11 @@
var d = datascript.js;
var tx0 = 0x20000000;

function test_with_datoms() {
function test_db_with() {
var db = d.empty_db();
var db1 = d.with_datoms(db, [[":db/add", 1, "name", "Ivan"],
var db1 = d.db_with(db, [[":db/add", 1, "name", "Ivan"],
[":db/add", 1, "age", 17]]);
var db2 = d.with_datoms(db1, [{":db/id": 2,
var db2 = d.db_with(db1, [{":db/id": 2,
"name": "Igor",
"age": 35}]);
var q = '[:find ?n ?a :where [?e "name" ?n] [?e "age" ?a]]';
Expand All @@ -122,14 +122,14 @@
return [[":db/add", e, "name", n],
[":db/add", e, "age", a]];
}
var db = d.with_datoms(d.empty_db(), [[":db.fn/call", dbfn, 1, "Ilya", 44]]);
var db = d.db_with(d.empty_db(), [[":db.fn/call", dbfn, 1, "Ilya", 44]]);
var q = '[:find ?n ?a :where [?e "name" ?n] [?e "age" ?a]]';
assert_eq_set([["Ilya", 44]], d.q(q, db));
}

function test_schema() {
var schema = {"aka": {":db/cardinality": ":db.cardinality/many"}};
var db = d.with_datoms(d.empty_db(schema),
var db = d.db_with(d.empty_db(schema),
[[":db/add", -1, "name", "Ivan"],
[":db/add", -1, "aka", "X"],
[":db/add", -1, "aka", "Y"],
Expand All @@ -151,14 +151,15 @@
{e: 1, a: "age", v: 17, tx: 536870913, added: true }],
tx_report.tx_data);
assert_eq({"-1": 1}, tx_report.tempids);
assert_eq(1, d.resolve_tempid(tx_report.tempids, -1));
assert_eq_datoms([{e: 1, a: "name", v: "Ivan", tx: 536870913, added: true },
{e: 1, a: "age", v: 17, tx: 536870913, added: true }],
log[0]);
}

function test_entity() {
var schema = {"aka": {":db/cardinality": ":db.cardinality/many"}};
var db = d.with_datoms(d.empty_db(schema),
var db = d.db_with(d.empty_db(schema),
[{":db/id": 1,
"name": "Ivan",
"aka": ["X", "Y"]},
Expand Down Expand Up @@ -198,7 +199,7 @@
var schema = {"father": {":db/valueType": ":db.type/ref"},
"children": {":db/valueType": ":db.type/ref",
":db/cardinality": ":db.cardinality/many"}};
var db = d.with_datoms(d.empty_db(schema),
var db = d.db_with(d.empty_db(schema),
[{":db/id": 1, "children": [10]},
{":db/id": 10, "father": 1, "children": [100, 101]},
{":db/id": 100, "father": 10}]);
Expand All @@ -224,7 +225,7 @@
assert_eq_refs([1], e(100).get("_children")[0].get("_children"));
}

var people_db = d.with_datoms(d.empty_db(),
var people_db = d.db_with(d.empty_db(),
[{ ":db/id": 1, "name": "Ivan", "age": 15 },
{ ":db/id": 2, "name": "Petr", "age": 37 },
{ ":db/id": 3, "name": "Ivan", "age": 37 }]);
Expand Down Expand Up @@ -287,7 +288,7 @@
}

function run_tests() {
return test_fns([ test_with_datoms,
return test_fns([ test_db_with,
test_dbfn_call,
test_schema,
test_tx_report,
Expand Down
Loading

0 comments on commit 031e4f7

Please sign in to comment.