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`

This comment has been minimized.

Copy link
@pasviegas

pasviegas Aug 27, 2014

Shouldn't transact be changed to 'with'?

This comment has been minimized.

Copy link
@tonsky

tonsky via email Aug 27, 2014

Author Owner

This comment has been minimized.

Copy link
@tonsky

tonsky Aug 27, 2014

Author Owner

Sorry, that line is about JS API. In JS, transact is equivalent to cljs transact! and does not match Datomic’s transact. JS part wasn’t changed, and shouldn’t. transact! is considered to be preferred API (since there’s no nonsence about returning completed future), and cljs transact was added for Datomic compatibility only. That’s why there’s no way to access transact in JS.

This comment has been minimized.

Copy link
@pasviegas

pasviegas Aug 27, 2014

Thanks, my bad.


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.