From 0847c3386c23bfa8fbcc191e24bcaa1022a1f4a8 Mon Sep 17 00:00:00 2001 From: Timo Kramer Date: Thu, 3 Sep 2020 18:48:41 +0200 Subject: [PATCH] Format with cljfmt --- dev/sandbox.clj | 4 +- src/datahike/api.cljc | 2 - src/datahike/connector.cljc | 96 ++-- src/datahike/core.cljc | 108 ++--- src/datahike/datom.cljc | 67 ++- src/datahike/db.cljc | 458 +++++++++--------- src/datahike/impl/entity.cljc | 19 +- src/datahike/index.cljc | 60 ++- src/datahike/index/hitchhiker_tree.cljc | 62 +-- src/datahike/js.cljs | 48 +- src/datahike/lru.cljc | 46 +- src/datahike/migrate.clj | 14 +- src/datahike/query.cljc | 199 ++++---- src/datahike/query_v3.cljc | 386 +++++++-------- src/datahike/schema.cljc | 2 - src/deps.cljs | 4 +- .../config_record_file_test.clj | 72 +-- test/datahike/test.cljc | 71 ++- test/datahike/test/cljs.cljc | 30 +- test/datahike/test/components.cljc | 54 +-- test/datahike/test/conn.cljc | 30 +- test/datahike/test/core.cljc | 77 ++- test/datahike/test/db.cljc | 1 - test/datahike/test/entity.cljc | 33 +- test/datahike/test/explode.cljc | 124 +++-- test/datahike/test/filter.cljc | 31 +- test/datahike/test/ident.cljc | 8 +- test/datahike/test/index.cljc | 208 ++++---- test/datahike/test/listen.cljc | 15 +- test/datahike/test/lookup_refs.cljc | 213 ++++---- test/datahike/test/lru.cljc | 8 +- test/datahike/test/migrate.clj | 1 - test/datahike/test/pull_api.cljc | 141 +++--- test/datahike/test/purge.cljc | 42 +- test/datahike/test/query.cljc | 165 +++---- test/datahike/test/query_aggregates.cljc | 83 ++-- test/datahike/test/query_find_specs.cljc | 45 +- test/datahike/test/query_fns.cljc | 134 +++-- test/datahike/test/query_interop.cljc | 6 +- test/datahike/test/query_not.cljc | 131 +++-- test/datahike/test/query_or.cljc | 109 +++-- test/datahike/test/query_pull.cljc | 68 +-- test/datahike/test/query_rules.cljc | 151 +++--- test/datahike/test/query_v3.cljc | 26 +- test/datahike/test/schema.cljc | 1 - test/datahike/test/store.cljc | 1 - test/datahike/test/time_variance.cljc | 21 +- test/datahike/test/transact.cljc | 147 +++--- test/datahike/test/upsert.cljc | 58 ++- test/datahike/test/validation.cljc | 20 +- 50 files changed, 1887 insertions(+), 2013 deletions(-) diff --git a/dev/sandbox.clj b/dev/sandbox.clj index d25f06773..688147e8b 100644 --- a/dev/sandbox.clj +++ b/dev/sandbox.clj @@ -38,6 +38,4 @@ :in $ ?a :where [?e :name ?v ?t] [?e :age ?a]] @conn - 35) - - ) + 35)) diff --git a/src/datahike/api.cljc b/src/datahike/api.cljc index 4b18f9230..22f917a31 100644 --- a/src/datahike/api.cljc +++ b/src/datahike/api.cljc @@ -178,7 +178,6 @@ Connect to a database with persistent store: load-entities dc/load-entities) - (def ^{:arglists '([conn]) :doc "Releases a database connection"} release dc/release) @@ -209,7 +208,6 @@ Connect to a database with persistent store: ```"} pull-many dp/pull-many) - (defmulti q "Executes a datalog query. See [docs.datomic.com/on-prem/query.html](https://docs.datomic.com/on-prem/query.html). diff --git a/src/datahike/connector.cljc b/src/datahike/connector.cljc index 934c3a242..fbf864025 100644 --- a/src/datahike/connector.cljc +++ b/src/datahike/connector.cljc @@ -14,7 +14,6 @@ [clojure.core.cache :as cache]) (:import [java.net URI])) - (s/def ::connection #(instance? clojure.lang.Atom %)) (defmulti transact! @@ -40,27 +39,26 @@ temporal-avet-flushed (when keep-history? (di/-flush temporal-avet backend))] (KonserveBackend store)] - (KonserveBackend store)] + (TxReport - {:db-before db - :db-after db - :tx-data [] - :tempids {} - :tx-meta tx-meta}) tx-data)))) + {:db-before db + :db-after db + :tx-data [] + :tempids {} + :tx-meta tx-meta}) tx-data)))) (defn load-entities-with [db entities] (db/transact-entities-directly - (db/map->TxReport {:db-before db - :db-after db - :tx-data [] - :tempids {} - :tx-meta []}) - entities)) + (db/map->TxReport {:db-before db + :db-after db + :tx-data [] + :tempids {} + :tx-meta []}) + entities)) (defn db-with "Applies transaction to an immutable db value, returning new immutable db value. Same as `(:db-after (with db tx-data))`." @@ -255,6 +251,7 @@ ; Index lookups + (defn datoms "Index lookup. Returns a sequence of datoms (lazy iterator over actual DB index) which components (e, a, v) match passed arguments. @@ -332,7 +329,6 @@ ([db index c1 c2 c3] {:pre [(db/db? db)]} (db/-datoms db index [c1 c2 c3])) ([db index c1 c2 c3 c4] {:pre [(db/db? db)]} (db/-datoms db index [c1 c2 c3 c4]))) - (defn seek-datoms "Similar to [[datoms]], but will return datoms starting from specified components and including rest of the database until the end of the index. @@ -370,7 +366,6 @@ ([db index c1 c2 c3] {:pre [(db/db? db)]} (db/-seek-datoms db index [c1 c2 c3])) ([db index c1 c2 c3 c4] {:pre [(db/db? db)]} (db/-seek-datoms db index [c1 c2 c3 c4]))) - (defn rseek-datoms "Same as [[seek-datoms]], but goes backwards until the beginning of the index." ([db index] {:pre [(db/db? db)]} (db/-rseek-datoms db index [])) @@ -379,7 +374,6 @@ ([db index c1 c2 c3] {:pre [(db/db? db)]} (db/-rseek-datoms db index [c1 c2 c3])) ([db index c1 c2 c3 c4] {:pre [(db/db? db)]} (db/-rseek-datoms db index [c1 c2 c3 c4]))) - (defn index-range "Returns part of `:avet` index between `[_ attr start]` and `[_ attr end]` in AVET sort order. @@ -411,6 +405,7 @@ ;; Conn + (defn conn? "Returns `true` if this is a connection to a DataScript db, `false` otherwise." [conn] @@ -418,19 +413,16 @@ :cljs (satisfies? cljs.core/IDeref conn)) (db/db? @conn))) - (defn conn-from-db "Creates a mutable reference to a given immutable database. See [[create-conn]]." [db] (atom db :meta {:listeners (atom {})})) - (defn conn-from-datoms "Creates an empty DB and a mutable reference to it. See [[create-conn]]." ([datoms] (conn-from-db (init-db datoms))) ([datoms schema] (conn-from-db (init-db datoms schema)))) - (defn create-conn "Creates a mutable reference (a “connection”) to an empty immutable database. @@ -547,29 +539,26 @@ (callback report)) report))) - (defn reset-conn! "Forces underlying `conn` value to become `db`. Will generate a tx-report that will remove everything from old value and insert everything from the new one." ([conn db] (reset-conn! conn db nil)) ([conn db tx-meta] (let [report (db/map->TxReport - {:db-before @conn - :db-after db - :tx-data (concat - (map #(assoc % :added false) (datoms @conn :eavt)) - (datoms db :eavt)) - :tx-meta tx-meta})] + {:db-before @conn + :db-after db + :tx-data (concat + (map #(assoc % :added false) (datoms @conn :eavt)) + (datoms db :eavt)) + :tx-meta tx-meta})] (reset! conn db) (doseq [[_ callback] (some-> (:listeners (meta conn)) (deref))] (callback report)) db))) - (defn- atom? [a] #?(:cljs (instance? Atom a) :clj (instance? clojure.lang.IAtom a))) - (defn listen! "Listen for changes on the given connection. Whenever a transaction is applied to the database via [[transact!]], the callback is called with the transaction report. `key` is any opaque unique value. @@ -583,7 +572,6 @@ (swap! (:listeners (meta conn)) assoc key callback) key)) - (defn unlisten! "Removes registered listener from connection. See also [[listen!]]." [conn key] @@ -593,8 +581,8 @@ ;; Datomic compatibility layer -(def ^:private last-tempid (atom -1000000)) +(def ^:private last-tempid (atom -1000000)) (defn tempid "Allocates and returns an unique temporary id (a negative integer). Ignores `part`. Returns `x` if it is specified. @@ -612,6 +600,7 @@ ;; Data Readers + (def ^{:doc "Data readers for EDN readers. In CLJS they’re registered automatically. In CLJ, if `data_readers.clj` do not work, you can always do ``` @@ -624,7 +613,6 @@ #?(:cljs (doseq [[tag cb] data-readers] (cljs.reader/register-tag-parser! tag cb))) - (defn resolve-tempid "Does a lookup in tempids map, returning an entity id that tempid was resolved to. @@ -632,7 +620,6 @@ [_db tempids tempid] (get tempids tempid)) - (defn db "Returns the underlying immutable database value from a connection. @@ -641,7 +628,6 @@ {:pre [(conn? conn)]} @conn) - (defn transact "Same as [[transact!]], but returns an immediately realized future. @@ -692,7 +678,6 @@ IPending (-realized? [_] @realized))))) - (defn transact-async "In CLJ, calls [[transact!]] on a future thread pool, returning immediately. @@ -702,11 +687,9 @@ {:pre [(conn? conn)]} (future-call #(transact! conn tx-data tx-meta)))) - (defn- rand-bits [pow] (rand-int (bit-shift-left 1 pow))) - #?(:cljs (defn- to-hex-string [n l] (let [s (.toString n 16) @@ -716,7 +699,6 @@ (< c l) (str (apply str (repeat (- l c) "0")) s) :else s)))) - (defn squuid "Generates a UUID that grow with time. Such UUIDs will always go to the end of the index and that will minimize insertions in the middle. @@ -735,15 +717,15 @@ (UUID. new-high low)) :cljs (uuid - (str - (-> (int (/ msec 1000)) - (to-hex-string 8)) - "-" (-> (rand-bits 16) (to-hex-string 4)) - "-" (-> (rand-bits 16) (bit-and 0x0FFF) (bit-or 0x4000) (to-hex-string 4)) - "-" (-> (rand-bits 16) (bit-and 0x3FFF) (bit-or 0x8000) (to-hex-string 4)) - "-" (-> (rand-bits 16) (to-hex-string 4)) - (-> (rand-bits 16) (to-hex-string 4)) - (-> (rand-bits 16) (to-hex-string 4))))))) + (str + (-> (int (/ msec 1000)) + (to-hex-string 8)) + "-" (-> (rand-bits 16) (to-hex-string 4)) + "-" (-> (rand-bits 16) (bit-and 0x0FFF) (bit-or 0x4000) (to-hex-string 4)) + "-" (-> (rand-bits 16) (bit-and 0x3FFF) (bit-or 0x8000) (to-hex-string 4)) + "-" (-> (rand-bits 16) (to-hex-string 4)) + (-> (rand-bits 16) (to-hex-string 4)) + (-> (rand-bits 16) (to-hex-string 4))))))) (defn squuid-time-millis "Returns time that was used in [[squuid]] call, in milliseconds, rounded to the closest second." diff --git a/src/datahike/datom.cljc b/src/datahike/datom.cljc index b8347c152..76dbc5fd7 100644 --- a/src/datahike/datom.cljc +++ b/src/datahike/datom.cljc @@ -1,7 +1,7 @@ (ns datahike.datom (:require [clojure.walk] [clojure.data] - [datahike.tools :refer [combine-hashes ]] + [datahike.tools :refer [combine-hashes]] [datahike.constants :refer [tx0]])) (declare hash-datom equiv-datom seq-datom nth-datom assoc-datom val-at-datom) @@ -163,44 +163,41 @@ res (num 0)] (if (not-empty comps) (recur - (next comps) - `(let [c# ~(first comps)] - (if (== 0 c#) - ~res - c#))) + (next comps) + `(let [c# ~(first comps)] + (if (== 0 c#) + ~res + c#))) res)))) - (defn cmp [o1 o2] (if (nil? o1) 0 - (if (nil? o2) 0 - (compare o1 o2)))) + (if (nil? o2) 0 + (compare o1 o2)))) ;; Slower cmp-* fns allows for datom fields to be nil. ;; Such datoms come from slice method where they are used as boundary markers. (defn cmp-datoms-eavt [^Datom d1, ^Datom d2] (combine-cmp - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (cmp (.-a d1) (.-a d2)) - (cmp (.-v d1) (.-v d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) - - + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (cmp (.-a d1) (.-a d2)) + (cmp (.-v d1) (.-v d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) (defn cmp-datoms-aevt [^Datom d1, ^Datom d2] (combine-cmp - (cmp (.-a d1) (.-a d2)) - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (cmp (.-v d1) (.-v d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) + (cmp (.-a d1) (.-a d2)) + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (cmp (.-v d1) (.-v d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) (defn cmp-datoms-avet [^Datom d1, ^Datom d2] (combine-cmp - (cmp (.-a d1) (.-a d2)) - (cmp (.-v d1) (.-v d2)) - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) + (cmp (.-a d1) (.-a d2)) + (cmp (.-v d1) (.-v d2)) + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) ;; fast versions without nil checks @@ -215,24 +212,24 @@ (defn cmp-datoms-eavt-quick [^Datom d1, ^Datom d2] (combine-cmp - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (cmp-attr-quick (.-a d1) (.-a d2)) - (compare (.-v d1) (.-v d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (cmp-attr-quick (.-a d1) (.-a d2)) + (compare (.-v d1) (.-v d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) (defn cmp-datoms-aevt-quick [^Datom d1, ^Datom d2] (combine-cmp - (cmp-attr-quick (.-a d1) (.-a d2)) - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (compare (.-v d1) (.-v d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) + (cmp-attr-quick (.-a d1) (.-a d2)) + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (compare (.-v d1) (.-v d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) (defn cmp-datoms-avet-quick [^Datom d1, ^Datom d2] (combine-cmp - (cmp-attr-quick (.-a d1) (.-a d2)) - (compare (.-v d1) (.-v d2)) - (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) - (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) + (cmp-attr-quick (.-a d1) (.-a d2)) + (compare (.-v d1) (.-v d2)) + (#?(:clj Integer/compare :cljs -) (.-e d1) (.-e d2)) + (#?(:clj Long/compare :cljs -) (datom-tx d1) (datom-tx d2)))) (defn diff-sorted [a b cmp] (loop [only-a [] diff --git a/src/datahike/db.cljc b/src/datahike/db.cljc index 381b09a46..fe450f9df 100644 --- a/src/datahike/db.cljc +++ b/src/datahike/db.cljc @@ -89,15 +89,15 @@ (let [impl-map (->> impls (map (juxt get-sig identity)) (filter first) (into {})) body (macroexpand-1 (list* 'defrecord name fields impls))] (clojure.walk/postwalk - (fn [form] - (if (and (sequential? form) (= 'deftype* (first form))) - (->> form - dedupe-interfaces - (remove (fn [method] - (when-let [impl (-> method get-sig impl-map)] - (not= method impl))))) - form)) - body)))) + (fn [form] + (if (and (sequential? form) (= 'deftype* (first form))) + (->> form + dedupe-interfaces + (remove (fn [method] + (when-let [impl (-> method get-sig impl-map)] + (not= method impl))))) + form)) + body)))) #?(:clj (defn- make-record-updatable-cljs [name fields & impls] @@ -108,8 +108,8 @@ #?(:clj (defmacro defrecord-updatable [name fields & impls] `(if-cljs - ~(apply make-record-updatable-cljs name fields impls) - ~(apply make-record-updatable-clj name fields impls)))) + ~(apply make-record-updatable-cljs name fields impls) + ~(apply make-record-updatable-clj name fields impls)))) ;; ---------------------------------------------------------------------------- @@ -225,47 +225,47 @@ ISearch (-search [db pattern] - (let [[_ a _ _] pattern] - (search-indices eavt aevt avet pattern (indexing? db a) false))) + (let [[_ a _ _] pattern] + (search-indices eavt aevt avet pattern (indexing? db a) false))) IIndexAccess (-datoms [db index-type cs] - (-slice (get db index-type) - (components->pattern db index-type cs e0 tx0) - (components->pattern db index-type cs emax txmax) - index-type)) + (-slice (get db index-type) + (components->pattern db index-type cs e0 tx0) + (components->pattern db index-type cs emax txmax) + index-type)) (-seek-datoms [db index-type cs] - (-slice (get db index-type) - (components->pattern db index-type cs e0 tx0) - (datom emax nil nil txmax) - index-type)) + (-slice (get db index-type) + (components->pattern db index-type cs e0 tx0) + (datom emax nil nil txmax) + index-type)) (-rseek-datoms [db index-type cs] - (-> (-slice (get db index-type) - (components->pattern db index-type cs e0 tx0) - (datom emax nil nil txmax) - index-type) - vec - rseq)) + (-> (-slice (get db index-type) + (components->pattern db index-type cs e0 tx0) + (datom emax nil nil txmax) + index-type) + vec + rseq)) (-index-range [db attr start end] - (when-not (indexing? db attr) - (raise "Attribute" attr "should be marked as :db/index true" {})) - (validate-attr attr (list '-index-range 'db attr start end) db) - (-slice avet - (resolve-datom db nil attr start nil e0 tx0) - (resolve-datom db nil attr end nil emax txmax) - :avet)) + (when-not (indexing? db attr) + (raise "Attribute" attr "should be marked as :db/index true" {})) + (validate-attr attr (list '-index-range 'db attr start end) db) + (-slice avet + (resolve-datom db nil attr start nil e0 tx0) + (resolve-datom db nil attr end nil emax txmax) + :avet)) clojure.data/EqualityPartition (equality-partition [x] :datahike/db) clojure.data/Diff (diff-similar [a b] - (let [datoms-a (-slice (:eavt a) (datom e0 nil nil tx0) (datom emax nil nil txmax) :eavt) - datoms-b (-slice (:eavt b) (datom e0 nil nil tx0) (datom emax nil nil txmax) :eavt)] - (dd/diff-sorted datoms-a datoms-b dd/cmp-datoms-eavt-quick)))) + (let [datoms-a (-slice (:eavt a) (datom e0 nil nil tx0) (datom emax nil nil txmax) :eavt) + datoms-b (-slice (:eavt b) (datom e0 nil nil tx0) (datom emax nil nil txmax) :eavt)] + (dd/diff-sorted datoms-a datoms-b dd/cmp-datoms-eavt-quick)))) (defn db? [x] (and (satisfies? ISearch x) @@ -301,7 +301,7 @@ clojure.lang.ILookup (valAt [db k] (throw (UnsupportedOperationException. "valAt/2 is not supported on FilteredDB"))) (valAt [db k nf] (throw (UnsupportedOperationException. "valAt/3 is not supported on FilteredDB"))) clojure.lang.IKeywordLookup (getLookupThunk [db k] - (throw (UnsupportedOperationException. "getLookupThunk is not supported on FilteredDB"))) + (throw (UnsupportedOperationException. "getLookupThunk is not supported on FilteredDB"))) clojure.lang.Associative (containsKey [e k] (throw (UnsupportedOperationException. "containsKey is not supported on FilteredDB"))) @@ -319,20 +319,20 @@ ISearch (-search [db pattern] - (filter (.-pred db) (-search (.-unfiltered-db db) pattern))) + (filter (.-pred db) (-search (.-unfiltered-db db) pattern))) IIndexAccess (-datoms [db index cs] - (filter (.-pred db) (-datoms (.-unfiltered-db db) index cs))) + (filter (.-pred db) (-datoms (.-unfiltered-db db) index cs))) (-seek-datoms [db index cs] - (filter (.-pred db) (-seek-datoms (.-unfiltered-db db) index cs))) + (filter (.-pred db) (-seek-datoms (.-unfiltered-db db) index cs))) (-rseek-datoms [db index cs] - (filter (.-pred db) (-rseek-datoms (.-unfiltered-db db) index cs))) + (filter (.-pred db) (-rseek-datoms (.-unfiltered-db db) index cs))) (-index-range [db attr start end] - (filter (.-pred db) (-index-range (.-unfiltered-db db) attr start end)))) + (filter (.-pred db) (-index-range (.-unfiltered-db db) attr start end)))) (defn- search-current-indices [^DB db pattern] (let [[_ a _ _] pattern] @@ -382,10 +382,10 @@ from (components->pattern db index-type cs e0 tx0) to (datom emax nil nil txmax)] (concat - (-> (concat (-slice index from to index-type) - (-slice temporal-index from to index-type)) - vec - rseq)))) + (-> (concat (-slice index from to index-type) + (-slice temporal-index from to index-type)) + vec + rseq)))) (defn temporal-index-range [^DB db current-db attr start end] (when-not (indexing? db attr) @@ -394,8 +394,8 @@ (let [from (resolve-datom current-db nil attr start nil e0 tx0) to (resolve-datom current-db nil attr end nil emax txmax)] (concat - (-slice (get db :avet) from to :avet) - (-slice (get db :temporal-avet) from to :avet)))) + (-slice (get db :avet) from to :avet) + (-slice (get db :temporal-avet) from to :avet)))) (defrecord-updatable HistoricalDB [origin-db] #?@(:cljs @@ -424,7 +424,7 @@ clojure.lang.ILookup (valAt [db k] (throw (UnsupportedOperationException. "valAt/2 is not supported on HistoricalDB"))) (valAt [db k nf] (throw (UnsupportedOperationException. "valAt/3 is not supported on HistoricalDB"))) clojure.lang.IKeywordLookup (getLookupThunk [db k] - (throw (UnsupportedOperationException. "getLookupThunk is not supported on HistoricalDB"))) + (throw (UnsupportedOperationException. "getLookupThunk is not supported on HistoricalDB"))) clojure.lang.Associative (containsKey [e k] (throw (UnsupportedOperationException. "containsKey is not supported on HistoricalDB"))) @@ -442,7 +442,7 @@ ISearch (-search [db pattern] - (temporal-search (.-origin-db db) pattern)) + (temporal-search (.-origin-db db) pattern)) IIndexAccess (-datoms [db index-type cs] (temporal-datoms (.-origin-db db) index-type cs)) @@ -469,13 +469,13 @@ (filter datom-added) (group-by (fn [^Datom datom] [(.-e datom) (.-a datom)])) (mapcat - (fn [[[_ a] entities]] - (if (contains? (get-in rschema [:db.cardinality/many]) a) - entities - [(reduce (fn [^Datom datom-0 ^Datom datom-1] - (if (> (datom-tx datom-0) (datom-tx datom-1)) - datom-0 - datom-1)) entities)]))))) + (fn [[[_ a] entities]] + (if (contains? (get-in rschema [:db.cardinality/many]) a) + entities + [(reduce (fn [^Datom datom-0 ^Datom datom-1] + (if (> (datom-tx datom-0) (datom-tx datom-1)) + datom-0 + datom-1)) entities)]))))) (defn- date? [d] #?(:cljs (instance? js/Date d) @@ -519,7 +519,7 @@ clojure.lang.ILookup (valAt [db k] (throw (UnsupportedOperationException. "valAt/2 is not supported on AsOfDB"))) (valAt [db k nf] (throw (UnsupportedOperationException. "valAt/3 is not supported on AsOfDB"))) clojure.lang.IKeywordLookup (getLookupThunk [db k] - (throw (UnsupportedOperationException. "getLookupThunk is not supported on AsOfDB"))) + (throw (UnsupportedOperationException. "getLookupThunk is not supported on AsOfDB"))) clojure.lang.Associative (containsKey [e k] (throw (UnsupportedOperationException. "containsKey is not supported on AsOfDB"))) @@ -537,30 +537,30 @@ ISearch (-search [db pattern] - (let [origin-db (.-origin-db db)] - (-> (temporal-search origin-db pattern) - (filter-as-of-datoms (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-search origin-db pattern) + (filter-as-of-datoms (.-time-point db) origin-db)))) IIndexAccess (-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-datoms origin-db index-type cs) - (filter-as-of-datoms (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-datoms origin-db index-type cs) + (filter-as-of-datoms (.-time-point db) origin-db)))) (-seek-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-seek-datoms origin-db index-type cs) - (filter-as-of-datoms (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-seek-datoms origin-db index-type cs) + (filter-as-of-datoms (.-time-point db) origin-db)))) (-rseek-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-rseek-datoms origin-db index-type cs) - (filter-as-of-datoms (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-rseek-datoms origin-db index-type cs) + (filter-as-of-datoms (.-time-point db) origin-db)))) (-index-range [db attr start end] - (let [origin-db (.-origin-db db)] - (-> (temporal-index-range origin-db db attr start end) - (filter-as-of-datoms (.-time-point db) origin-db))))) + (let [origin-db (.-origin-db db)] + (-> (temporal-index-range origin-db db attr start end) + (filter-as-of-datoms (.-time-point db) origin-db))))) (defn- filter-since [datoms time-point db] (let [since-pred (fn [^Datom d] @@ -608,7 +608,7 @@ clojure.lang.ILookup (valAt [db k] (throw (UnsupportedOperationException. "valAt/2 is not supported on SinceDB"))) (valAt [db k nf] (throw (UnsupportedOperationException. "valAt/3 is not supported on SinceDB"))) clojure.lang.IKeywordLookup (getLookupThunk [db k] - (throw (UnsupportedOperationException. "getLookupThunk is not supported on SinceDB"))) + (throw (UnsupportedOperationException. "getLookupThunk is not supported on SinceDB"))) clojure.lang.Associative (containsKey [e k] (throw (UnsupportedOperationException. "containsKey is not supported on SinceDB"))) @@ -626,30 +626,30 @@ ISearch (-search [db pattern] - (let [origin-db (.-origin-db db)] - (-> (temporal-search origin-db pattern) - (filter-since (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-search origin-db pattern) + (filter-since (.-time-point db) origin-db)))) IIndexAccess (-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-datoms origin-db index-type cs) - (filter-since (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-datoms origin-db index-type cs) + (filter-since (.-time-point db) origin-db)))) (-seek-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-seek-datoms origin-db index-type cs) - (filter-since (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-seek-datoms origin-db index-type cs) + (filter-since (.-time-point db) origin-db)))) (-rseek-datoms [db index-type cs] - (let [origin-db (.-origin-db db)] - (-> (temporal-rseek-datoms origin-db index-type cs) - (filter-since (.-time-point db) origin-db)))) + (let [origin-db (.-origin-db db)] + (-> (temporal-rseek-datoms origin-db index-type cs) + (filter-since (.-time-point db) origin-db)))) (-index-range [db attr start end] - (let [origin-db (.-origin-db db)] - (-> (temporal-index-range origin-db db attr start end) - (filter-since (.-time-point db) origin-db))))) + (let [origin-db (.-origin-db db)] + (-> (temporal-index-range origin-db db attr start end) + (filter-since (.-time-point db) origin-db))))) ;; ---------------------------------------------------------------------------- @@ -670,17 +670,17 @@ (defn- rschema [schema] (reduce-kv - (fn [m attr keys->values] - (if (keyword? keys->values) - m - (reduce-kv - (fn [m key value] - (reduce - (fn [m prop] - (assoc m prop (conj (get m prop #{}) attr))) - m (attr->properties key value))) - (update m :db/ident (fn [coll] (if coll (conj coll attr) #{attr}))) keys->values))) - {} schema)) + (fn [m attr keys->values] + (if (keyword? keys->values) + m + (reduce-kv + (fn [m key value] + (reduce + (fn [m prop] + (assoc m prop (conj (get m prop #{}) attr))) + m (attr->properties key value))) + (update m :db/ident (fn [coll] (if coll (conj coll attr) #{attr}))) keys->values))) + {} schema)) (defn- validate-schema-key [a k v expected] (when-not (or (nil? v) @@ -752,10 +752,10 @@ (defn init-max-eid [eavt] ;; solved with reserse slice first in datascript (if-let [datoms (-slice - eavt - (datom e0 nil nil tx0) - (datom (dec tx0) nil nil txmax) - :eavt)] + eavt + (datom e0 nil nil tx0) + (datom (dec tx0) nil nil txmax) + :eavt)] (-> datoms vec rseq first :e) ;; :e of last datom in slice e0)) @@ -841,10 +841,10 @@ (defn- pp-db [db ^java.io.Writer w] (pp/pprint-logical-block :prefix "#datahike/DB {" :suffix "}" (pp/pprint-logical-block - (pp/write-out :schema) - (.write w " ") - (pp/pprint-newline :linear) - (pp/write-out (:schema db))) + (pp/write-out :schema) + (.write w " ") + (pp/pprint-newline :linear) + (pp/write-out (:schema db))) (.write w ", ") (pp/pprint-newline :linear))) @@ -862,12 +862,12 @@ (defn- resolve-datom [db e a v t default-e default-tx] (when a (validate-attr a (list 'resolve-datom 'db e a v t) db)) (datom - (or (entid-some db e) default-e) ;; e - a ;; a - (if (and (some? v) (ref? db a)) ;; v - (entid-strict db v) - v) - (or (entid-some db t) default-tx))) ;; t + (or (entid-some db e) default-e) ;; e + a ;; a + (if (and (some? v) (ref? db a)) ;; v + (entid-strict db v) + v) + (or (entid-some db t) default-tx))) ;; t (defn components->pattern [db index [c0 c1 c2 c3] default-e default-tx] (case index @@ -1035,9 +1035,9 @@ (defn advance-max-eid [db eid] (cond-> db - (and (> eid (:max-eid db)) - (< eid tx0)) ;; do not trigger advance if transaction id was referenced - (assoc :max-eid eid))) + (and (> eid (:max-eid db)) + (< eid tx0)) ;; do not trigger advance if transaction id was referenced + (assoc :max-eid eid))) (defn advance-max-tid [db tid] (assoc db :max-tx tid)) @@ -1047,12 +1047,12 @@ (update-in report [:db-after] advance-max-eid eid)) ([report e eid] (cond-> report - (tx-id? e) - (assoc-in [:tempids e] eid) - (tempid? e) - (assoc-in [:tempids e] eid) - true - (update-in [:db-after] advance-max-eid eid)))) + (tx-id? e) + (assoc-in [:tempids e] eid) + (tempid? e) + (assoc-in [:tempids e] eid) + true + (update-in [:db-after] advance-max-eid eid)))) (defn update-schema [db ^Datom datom] (let [schema (:schema db) @@ -1102,26 +1102,26 @@ keep-history? (and (-keep-history? db) (not (no-history? db (.-a datom))))] (if (datom-added datom) (cond-> db - true (update-in [:eavt] #(di/-insert % datom :eavt)) - true (update-in [:aevt] #(di/-insert % datom :aevt)) - indexing? (update-in [:avet] #(di/-insert % datom :avet)) - true (advance-max-eid (.-e datom)) - true (update :hash + (hash datom)) - schema? (-> (update-schema datom) - update-rschema)) + true (update-in [:eavt] #(di/-insert % datom :eavt)) + true (update-in [:aevt] #(di/-insert % datom :aevt)) + indexing? (update-in [:avet] #(di/-insert % datom :avet)) + true (advance-max-eid (.-e datom)) + true (update :hash + (hash datom)) + schema? (-> (update-schema datom) + update-rschema)) (if-some [removing ^Datom (first (-search db [(.-e datom) (.-a datom) (.-v datom)]))] (cond-> db - true (update-in [:eavt] #(di/-remove % removing :eavt)) - true (update-in [:aevt] #(di/-remove % removing :aevt)) - indexing? (update-in [:avet] #(di/-remove % removing :avet)) - true (update :hash - (hash removing)) - schema? (-> (remove-schema datom) update-rschema) - keep-history? (update-in [:temporal-eavt] #(di/-insert % removing :eavt)) - keep-history? (update-in [:temporal-eavt] #(di/-insert % datom :eavt)) - keep-history? (update-in [:temporal-aevt] #(di/-insert % removing :aevt)) - keep-history? (update-in [:temporal-aevt] #(di/-insert % datom :aevt)) - (and keep-history? indexing?) (update-in [:temporal-avet] #(di/-insert % removing :avet)) - (and keep-history? indexing?) (update-in [:temporal-avet] #(di/-insert % datom :avet))) + true (update-in [:eavt] #(di/-remove % removing :eavt)) + true (update-in [:aevt] #(di/-remove % removing :aevt)) + indexing? (update-in [:avet] #(di/-remove % removing :avet)) + true (update :hash - (hash removing)) + schema? (-> (remove-schema datom) update-rschema) + keep-history? (update-in [:temporal-eavt] #(di/-insert % removing :eavt)) + keep-history? (update-in [:temporal-eavt] #(di/-insert % datom :eavt)) + keep-history? (update-in [:temporal-aevt] #(di/-insert % removing :aevt)) + keep-history? (update-in [:temporal-aevt] #(di/-insert % datom :aevt)) + (and keep-history? indexing?) (update-in [:temporal-avet] #(di/-insert % removing :avet)) + (and keep-history? indexing?) (update-in [:temporal-avet] #(di/-insert % datom :avet))) db)))) (defn- with-temporal-datom [db ^Datom datom] @@ -1132,14 +1132,14 @@ current? (not (nil? current-datom)) history? (not (nil? history-datom))] (cond-> db - current? (update-in [:eavt] #(di/-remove % current-datom :eavt)) - current? (update-in [:aevt] #(di/-remove % current-datom :aevt)) - (and current? indexing?) (update-in [:avet] #(di/-remove % current-datom :avet)) - current? (update :hash - (hash current-datom)) - (and current? schema?) (-> (remove-schema datom) update-rschema) - history? (update-in [:temporal-eavt] #(di/-remove % history-datom :eavt)) - history? (update-in [:temporal-aevt] #(di/-remove % history-datom :aevt)) - (and history? indexing?) (update-in [:temporal-avet] #(di/-remove % history-datom :avet))))) + current? (update-in [:eavt] #(di/-remove % current-datom :eavt)) + current? (update-in [:aevt] #(di/-remove % current-datom :aevt)) + (and current? indexing?) (update-in [:avet] #(di/-remove % current-datom :avet)) + current? (update :hash - (hash current-datom)) + (and current? schema?) (-> (remove-schema datom) update-rschema) + history? (update-in [:temporal-eavt] #(di/-remove % history-datom :eavt)) + history? (update-in [:temporal-aevt] #(di/-remove % history-datom :aevt)) + (and history? indexing?) (update-in [:temporal-avet] #(di/-remove % history-datom :avet))))) (defn- transact-report [report datom] (-> report @@ -1163,29 +1163,29 @@ (defn- upsert-eid [db entity] (when-let [idents (not-empty (-attrs-by db :db.unique/identity))] (->> - (reduce-kv - (fn [acc a v] ;; acc = [e a v] - (if (contains? idents a) - (do - (validate-val v [nil nil a v nil] db) - (if-some [e (:e (first (-datoms db :avet [a v])))] - (cond - (nil? acc) [e a v] ;; first upsert - (= (get acc 0) e) acc ;; second+ upsert, but does not conflict - :else - (let [[_e _a _v] acc] - (raise "Conflicting upserts: " [_a _v] " resolves to " _e - ", but " [a v] " resolves to " e - {:error :transact/upsert - :entity entity - :assertion [e a v] - :conflict [_e _a _v]}))) - acc)) ;; upsert attr, but resolves to nothing - acc)) ;; non-upsert attr - nil - entity) - (check-upsert-conflict entity) - first))) ;; getting eid from acc + (reduce-kv + (fn [acc a v] ;; acc = [e a v] + (if (contains? idents a) + (do + (validate-val v [nil nil a v nil] db) + (if-some [e (:e (first (-datoms db :avet [a v])))] + (cond + (nil? acc) [e a v] ;; first upsert + (= (get acc 0) e) acc ;; second+ upsert, but does not conflict + :else + (let [[_e _a _v] acc] + (raise "Conflicting upserts: " [_a _v] " resolves to " _e + ", but " [a v] " resolves to " e + {:error :transact/upsert + :entity entity + :assertion [e a v] + :conflict [_e _a _v]}))) + acc)) ;; upsert attr, but resolves to nothing + acc)) ;; non-upsert attr + nil + entity) + (check-upsert-conflict entity) + first))) ;; getting eid from acc ;; multivals/reverse can be specified as coll or as a single value, trying to guess @@ -1254,8 +1254,8 @@ (defn- retract-components [db datoms] (into #{} (comp - (filter (fn [^Datom d] (component? db (.-a d)))) - (map (fn [^Datom d] [:db.fn/retractEntity (.-v d)]))) datoms)) + (filter (fn [^Datom d] (component? db (.-a d)))) + (map (fn [^Datom d] [:db.fn/retractEntity (.-v d)]))) datoms)) (defn- purge-components [db datoms] (let [xf (comp @@ -1331,69 +1331,69 @@ (let [old-eid (:db/id entity)] (cond+ ;; :db/current-tx / "datomic.tx" => tx - (tx-id? old-eid) - (let [id (current-tx report)] - (recur (allocate-eid report old-eid id) - (cons (assoc entity :db/id id) entities))) + (tx-id? old-eid) + (let [id (current-tx report)] + (recur (allocate-eid report old-eid id) + (cons (assoc entity :db/id id) entities))) ;; lookup-ref => resolved | error - (sequential? old-eid) - (let [id (entid-strict db old-eid)] - (recur report - (cons (assoc entity :db/id id) entities))) + (sequential? old-eid) + (let [id (entid-strict db old-eid)] + (recur report + (cons (assoc entity :db/id id) entities))) ;; upserted => explode | error - :let [upserted-eid (upsert-eid db entity)] - - (some? upserted-eid) - (if (and (tempid? old-eid) - (contains? tempids old-eid) - (not= upserted-eid (get tempids old-eid))) - (retry-with-tempid initial-report report initial-es old-eid upserted-eid) - (do + :let [upserted-eid (upsert-eid db entity)] + + (some? upserted-eid) + (if (and (tempid? old-eid) + (contains? tempids old-eid) + (not= upserted-eid (get tempids old-eid))) + (retry-with-tempid initial-report report initial-es old-eid upserted-eid) + (do ;; schema tx - (when (ds/schema-entity? entity) - (if-let [attr-name (get-in db [:schema upserted-eid])] - (when-let [invalid-updates (ds/find-invalid-schema-updates entity (get-in db [:schema attr-name]))] - (when-not (empty? invalid-updates) - (raise "Update not supported for these schema attributes" - {:error :transact/schema :entity entity :invalid-updates invalid-updates}))) - (when (= :write (get-in db [:config :schema-flexibility])) - (when (or (:db/cardinality entity) (:db/valueType entity)) - (when-not (ds/schema? entity) - (raise "Incomplete schema transaction attributes, expected :db/ident, :db/valueType, :db/cardinality" - {:error :transact/schema :entity entity})))))) - (recur (allocate-eid report old-eid upserted-eid) - (concat (explode db (assoc entity :db/id upserted-eid)) entities)))) + (when (ds/schema-entity? entity) + (if-let [attr-name (get-in db [:schema upserted-eid])] + (when-let [invalid-updates (ds/find-invalid-schema-updates entity (get-in db [:schema attr-name]))] + (when-not (empty? invalid-updates) + (raise "Update not supported for these schema attributes" + {:error :transact/schema :entity entity :invalid-updates invalid-updates}))) + (when (= :write (get-in db [:config :schema-flexibility])) + (when (or (:db/cardinality entity) (:db/valueType entity)) + (when-not (ds/schema? entity) + (raise "Incomplete schema transaction attributes, expected :db/ident, :db/valueType, :db/cardinality" + {:error :transact/schema :entity entity})))))) + (recur (allocate-eid report old-eid upserted-eid) + (concat (explode db (assoc entity :db/id upserted-eid)) entities)))) ;; resolved | allocated-tempid | tempid | nil => explode - (or (number? old-eid) - (nil? old-eid) - (string? old-eid)) - (let [new-eid (cond - (nil? old-eid) (next-eid db) - (tempid? old-eid) (or (get tempids old-eid) - (next-eid db)) - :else old-eid) - new-entity (assoc entity :db/id new-eid)] - (when (ds/schema-entity? entity) - (if-let [attr-name (get-in db [:schema new-eid])] - (when-let [invalid-updates (ds/find-invalid-schema-updates entity (get-in db [:schema attr-name]))] - (when-not (empty? invalid-updates) - (raise "Update not supported for these schema attributes" - {:error :transact/schema :entity entity :invalid-updates invalid-updates}))) - (when (= :write (get-in db [:config :schema-flexibility])) - (when (or (:db/cardinality entity) (:db/valueType entity)) - (when-not (ds/schema? entity) - (raise "Incomplete schema transaction attributes, expected :db/ident, :db/valueType, :db/cardinality" - {:error :transact/schema :entity entity})))))) - (recur (allocate-eid report old-eid new-eid) - (concat (explode db new-entity) entities))) + (or (number? old-eid) + (nil? old-eid) + (string? old-eid)) + (let [new-eid (cond + (nil? old-eid) (next-eid db) + (tempid? old-eid) (or (get tempids old-eid) + (next-eid db)) + :else old-eid) + new-entity (assoc entity :db/id new-eid)] + (when (ds/schema-entity? entity) + (if-let [attr-name (get-in db [:schema new-eid])] + (when-let [invalid-updates (ds/find-invalid-schema-updates entity (get-in db [:schema attr-name]))] + (when-not (empty? invalid-updates) + (raise "Update not supported for these schema attributes" + {:error :transact/schema :entity entity :invalid-updates invalid-updates}))) + (when (= :write (get-in db [:config :schema-flexibility])) + (when (or (:db/cardinality entity) (:db/valueType entity)) + (when-not (ds/schema? entity) + (raise "Incomplete schema transaction attributes, expected :db/ident, :db/valueType, :db/cardinality" + {:error :transact/schema :entity entity})))))) + (recur (allocate-eid report old-eid new-eid) + (concat (explode db new-entity) entities))) ;; trash => error - :else - (raise "Expected number, string or lookup ref for :db/id, got " old-eid - {:error :entity-id/syntax, :entity entity}))) + :else + (raise "Expected number, string or lookup ref for :db/id, got " old-eid + {:error :entity-id/syntax, :entity entity}))) (sequential? entity) (let [[op e a v] entity] diff --git a/src/datahike/impl/entity.cljc b/src/datahike/impl/entity.cljc index d073b591b..bad60b712 100644 --- a/src/datahike/impl/entity.cljc +++ b/src/datahike/impl/entity.cljc @@ -143,8 +143,7 @@ clojure.lang.IFn (invoke [e k] (lookup-entity e k)) - (invoke [e k not-found] (lookup-entity e k not-found)) - ])) + (invoke [e k not-found] (lookup-entity e k not-found))])) (defn entity? [x] (instance? Entity x)) @@ -178,18 +177,18 @@ (defn touch-components [db a->v] (reduce-kv (fn [acc a v] (assoc acc a - (if (db/component? db a) - (if (db/multival? db a) - (set (map touch v)) - (touch v)) - v))) + (if (db/component? db a) + (if (db/multival? db a) + (set (map touch v)) + (touch v)) + v))) {} a->v)) (defn- datoms->cache [db datoms] (reduce (fn [acc part] - (let [a (:a (first part))] - (assoc acc a (entity-attr db a part)))) - {} (partition-by :a datoms))) + (let [a (:a (first part))] + (assoc acc a (entity-attr db a part)))) + {} (partition-by :a datoms))) (defn touch [^Entity e] {:pre [(entity? e)]} diff --git a/src/datahike/index.cljc b/src/datahike/index.cljc index 43160c50e..8c3dba2d5 100644 --- a/src/datahike/index.cljc +++ b/src/datahike/index.cljc @@ -16,73 +16,68 @@ (-transient [index]) (-persistent! [index])) - (extend-type DataNode IIndex (-all [eavt-tree] - (dih/-all eavt-tree :eavt)) + (dih/-all eavt-tree :eavt)) (-seq [eavt-tree] - (dih/-seq eavt-tree :eavt)) + (dih/-seq eavt-tree :eavt)) (-count [eavt-tree] - (dih/-count eavt-tree :eavt)) + (dih/-count eavt-tree :eavt)) (-insert [tree datom index-type] - (dih/-insert tree datom index-type)) + (dih/-insert tree datom index-type)) (-remove [tree datom index-type] - (dih/-remove tree datom index-type)) + (dih/-remove tree datom index-type)) (-slice [tree from to index-type] - (dih/-slice tree from to index-type)) + (dih/-slice tree from to index-type)) (-flush [tree backend] - (dih/-flush tree backend)) + (dih/-flush tree backend)) (-transient [tree] - (dih/-transient tree)) + (dih/-transient tree)) (-persistent! [tree] - (dih/-persistent! tree))) - + (dih/-persistent! tree))) (extend-type IndexNode IIndex (-all [eavt-tree] - (dih/-all eavt-tree :eavt)) + (dih/-all eavt-tree :eavt)) (-seq [eavt-tree] - (dih/-seq eavt-tree :eavt)) + (dih/-seq eavt-tree :eavt)) (-count [eavt-tree] - (dih/-count eavt-tree :eavt)) + (dih/-count eavt-tree :eavt)) (-insert [tree datom index-type] - (dih/-insert tree datom index-type)) + (dih/-insert tree datom index-type)) (-remove [tree datom index-type] - (dih/-remove tree datom index-type)) + (dih/-remove tree datom index-type)) (-slice [tree from to index-type] - (dih/-slice tree from to index-type)) + (dih/-slice tree from to index-type)) (-flush [tree backend] - (dih/-flush tree backend)) + (dih/-flush tree backend)) (-transient [tree] - (dih/-transient tree)) + (dih/-transient tree)) (-persistent! [tree] - (dih/-persistent! tree))) - + (dih/-persistent! tree))) (extend-type PersistentSortedSet IIndex (-all [eavt-set] - (dip/-all eavt-set)) + (dip/-all eavt-set)) (-seq [eavt-set] - (dip/-seq eavt-set)) + (dip/-seq eavt-set)) (-count [eavt-set] - (dip/-count eavt-set)) + (dip/-count eavt-set)) (-insert [set datom index-type] - (dip/-insert set datom index-type)) + (dip/-insert set datom index-type)) (-remove [set datom index-type] - (dip/-remove set datom index-type)) + (dip/-remove set datom index-type)) (-slice [set from to _] - (dip/-slice set from to)) + (dip/-slice set from to)) (-flush [set _] - (dip/-flush set)) + (dip/-flush set)) (-transient [set] - (dip/-transient set)) + (dip/-transient set)) (-persistent! [set] - (dip/-persistent! set))) - - + (dip/-persistent! set))) (defmulti empty-index "Creates empty index" @@ -95,7 +90,6 @@ (defmethod empty-index ::persistent-set [_ index-type] (dip/empty-set index-type)) - (defmulti init-index "Initialize index with datoms" {:arglists '([index datoms indexed index-type])} diff --git a/src/datahike/index/hitchhiker_tree.cljc b/src/datahike/index/hitchhiker_tree.cljc index 36950b06c..84d20b14b 100644 --- a/src/datahike/index/hitchhiker_tree.cljc +++ b/src/datahike/index/hitchhiker_tree.cljc @@ -16,10 +16,10 @@ (let [[a b c d] key1 [e f g h] key2] (dd/combine-cmp - (kc/-compare a e) - (kc/-compare b f) - (kc/-compare c g) - (kc/-compare d h))))) + (kc/-compare a e) + (kc/-compare b f) + (kc/-compare c g) + (kc/-compare d h))))) java.lang.String (-compare [key1 key2] (compare key1 key2)) @@ -49,7 +49,7 @@ (defn- from-datom [^Datom datom index-type] (let [datom-seq (case index-type :aevt (list (.-a datom) (.-e datom) (.-v datom) (.-tx datom)) - :avet (list(.-a datom) (.-v datom) (.-e datom) (.-tx datom)) + :avet (list (.-a datom) (.-v datom) (.-e datom) (.-tx datom)) (list (.-e datom) (.-a datom) (.-v datom) (.-tx datom)))] (->> datom-seq (remove #{e0 tx0 emax txmax}) @@ -62,33 +62,33 @@ [a b c d] (from-datom from index-type) [e f g h] (from-datom to index-type) xf (comp - (take-while (fn [^AMapEntry kv] + (take-while (fn [^AMapEntry kv] ;; prefix scan - (let [key (.key kv) - [i j k l] key - new (not (cond (and e f g h) - (or (> (kc/-compare i e) 0) - (> (kc/-compare j f) 0) - (> (kc/-compare k g) 0) - (> (kc/-compare l h) 0)) - - (and e f g) - (or (> (kc/-compare i e) 0) - (> (kc/-compare j f) 0) - (> (kc/-compare k g) 0)) - - (and e f) - (or (> (kc/-compare i e) 0) - (> (kc/-compare j f) 0)) - - e - (> (kc/-compare i e) 0) - - :else false))] - new))) - (map (fn [kv] - (let [[a b c d] (.key ^AMapEntry kv)] - (create-datom a b c d))))) + (let [key (.key kv) + [i j k l] key + new (not (cond (and e f g h) + (or (> (kc/-compare i e) 0) + (> (kc/-compare j f) 0) + (> (kc/-compare k g) 0) + (> (kc/-compare l h) 0)) + + (and e f g) + (or (> (kc/-compare i e) 0) + (> (kc/-compare j f) 0) + (> (kc/-compare k g) 0)) + + (and e f) + (or (> (kc/-compare i e) 0) + (> (kc/-compare j f) 0)) + + e + (> (kc/-compare i e) 0) + + :else false))] + new))) + (map (fn [kv] + (let [[a b c d] (.key ^AMapEntry kv)] + (create-datom a b c d))))) new (->> (sequence xf (hmsg/lookup-fwd-iter tree [a b c d])) seq)] new)) diff --git a/src/datahike/js.cljs b/src/datahike/js.cljs index 130a1cb7e..861c5860e 100644 --- a/src/datahike/js.cljs +++ b/src/datahike/js.cljs @@ -1,10 +1,10 @@ (ns ^:no-doc datahike.js (:refer-clojure :exclude [filter]) (:require - [goog.object :as go] - [datahike.core :as d] - [clojure.walk :as walk] - [cljs.reader])) + [goog.object :as go] + [datahike.core :as d] + [clojure.walk :as walk] + [cljs.reader])) ;; Conversions @@ -16,19 +16,19 @@ (defn- schema->clj [schema] (->> (js->clj schema) (reduce-kv - (fn [m k v] (assoc m k (walk/postwalk keywordize v))) {}))) + (fn [m k v] (assoc m k (walk/postwalk keywordize v))) {}))) (declare entities->clj) (defn- entity-map->clj [e] (walk/postwalk - (fn [form] - (if (and (map? form) (contains? form ":db/id")) - (-> form - (dissoc ":db/id") - (assoc :db/id (get form ":db/id"))) - form)) - e)) + (fn [form] + (if (and (map? form) (contains? form ":db/id")) + (-> form + (dissoc ":db/id") + (assoc :db/id (get form ":db/id"))) + form)) + e)) (defn- entity->clj [e] (cond @@ -54,11 +54,11 @@ obj)) (defn- tx-report->js [report] - #js { :db_before (:db-before report) - :db_after (:db-after report) - :tx_data (->> (:tx-data report) into-array) - :tempids (tempids->js (:tempids report)) - :tx_meta (:tx-meta report) }) + #js {:db_before (:db-before report) + :db_after (:db-after report) + :tx_data (->> (:tx-data report) into-array) + :tempids (tempids->js (:tempids report)) + :tx_meta (:tx-meta report)}) (defn js->Datom [d] (if (array? d) @@ -127,13 +127,13 @@ report)) (defn ^:export reset_conn [conn db & [tx-meta]] - (let [report #js { :db_before @conn - :db_after db - :tx_data (into-array - (concat - (map #(assoc % :added false) (d/datoms @conn :eavt)) - (d/datoms db :eavt))) - :tx_meta tx-meta }] + (let [report #js {:db_before @conn + :db_after db + :tx_data (into-array + (concat + (map #(assoc % :added false) (d/datoms @conn :eavt)) + (d/datoms db :eavt))) + :tx_meta tx-meta}] (reset! conn db) (doseq [[_ callback] @(:listeners (meta conn))] (callback report)) diff --git a/src/datahike/lru.cljc b/src/datahike/lru.cljc index 3f8c0f646..6ad2451ef 100644 --- a/src/datahike/lru.cljc +++ b/src/datahike/lru.cljc @@ -3,25 +3,25 @@ (declare assoc-lru cleanup-lru) #?(:cljs - (deftype LRU [key-value gen-key key-gen gen limit] - IAssociative - (-assoc [this k v] (assoc-lru this k v)) - (-contains-key? [_ k] (-contains-key? key-value k)) - ILookup - (-lookup [_ k] (-lookup key-value k nil)) - (-lookup [_ k nf] (-lookup key-value k nf)) - IPrintWithWriter - (-pr-writer [_ writer opts] - (-pr-writer (persistent! key-value) writer opts))) + (deftype LRU [key-value gen-key key-gen gen limit] + IAssociative + (-assoc [this k v] (assoc-lru this k v)) + (-contains-key? [_ k] (-contains-key? key-value k)) + ILookup + (-lookup [_ k] (-lookup key-value k nil)) + (-lookup [_ k nf] (-lookup key-value k nf)) + IPrintWithWriter + (-pr-writer [_ writer opts] + (-pr-writer (persistent! key-value) writer opts))) :clj - (deftype LRU [^clojure.lang.Associative key-value gen-key key-gen gen limit] - clojure.lang.ILookup - (valAt [_ k] (.valAt key-value k)) - (valAt [_ k not-found] (.valAt key-value k not-found)) - clojure.lang.Associative - (containsKey [_ k] (.containsKey key-value k)) - (entryAt [_ k] (.entryAt key-value k)) - (assoc [this k v] (assoc-lru this k v)))) + (deftype LRU [^clojure.lang.Associative key-value gen-key key-gen gen limit] + clojure.lang.ILookup + (valAt [_ k] (.valAt key-value k)) + (valAt [_ k not-found] (.valAt key-value k not-found)) + clojure.lang.Associative + (containsKey [_ k] (.containsKey key-value k)) + (entryAt [_ k] (.entryAt key-value k)) + (assoc [this k v] (assoc-lru this k v)))) (defn assoc-lru [^LRU lru k v] (let [key-value (.-key-value lru) @@ -38,11 +38,11 @@ (inc gen) limit) (cleanup-lru - (->LRU (assoc key-value k v) - (assoc gen-key gen k) - (assoc key-gen k gen) - (inc gen) - limit))))) + (->LRU (assoc key-value k v) + (assoc gen-key gen k) + (assoc key-gen k gen) + (inc gen) + limit))))) (defn cleanup-lru [^LRU lru] (if (> (count (.-key-value lru)) (.-limit lru)) diff --git a/src/datahike/migrate.clj b/src/datahike/migrate.clj index 02e87705a..f8f7f467e 100644 --- a/src/datahike/migrate.clj +++ b/src/datahike/migrate.clj @@ -13,12 +13,10 @@ (when (not= (:a d) :db/txInstant) (prn d)))))) - (defn import-db - "Import a flat-file of datoms at path into your database." - [conn path] - (doseq [datoms (->> (line-seq (io/reader path)) - (map read-string) - (partition 1000 1000 nil) - )] - (api/transact conn (vec datoms)))) + "Import a flat-file of datoms at path into your database." + [conn path] + (doseq [datoms (->> (line-seq (io/reader path)) + (map read-string) + (partition 1000 1000 nil))] + (api/transact conn (vec datoms)))) diff --git a/src/datahike/query.cljc b/src/datahike/query.cljc index 2df49c548..837b06988 100644 --- a/src/datahike/query.cljc +++ b/src/datahike/query.cljc @@ -1,26 +1,26 @@ (ns datahike.query (:require - [#?(:cljs cljs.reader :clj clojure.edn) :as edn] - [clojure.set :as set] - [clojure.string :as str] - [clojure.walk :as walk] - [datahike.db :as db] - [datahike.tools #?(:cljs :refer-macros :clj :refer) [raise]] - [me.tonsky.persistent-sorted-set.arrays :as da] - [datahike.lru] - [datahike.impl.entity :as de] - #?@(:cljs [datalog.parser.type :refer [BindColl BindIgnore BindScalar BindTuple Constant - FindColl FindRel FindScalar FindTuple PlainSymbol - RulesVar SrcVar Variable]]) - [datalog.parser.impl :as dpi] - [datalog.parser.impl.proto :as dpip] - [datahike.pull-api :as dpa] - [datalog.parser :refer [parse]] - [datalog.parser.pull :as dpp]) + [#?(:cljs cljs.reader :clj clojure.edn) :as edn] + [clojure.set :as set] + [clojure.string :as str] + [clojure.walk :as walk] + [datahike.db :as db] + [datahike.tools #?(:cljs :refer-macros :clj :refer) [raise]] + [me.tonsky.persistent-sorted-set.arrays :as da] + [datahike.lru] + [datahike.impl.entity :as de] + #?@(:cljs [datalog.parser.type :refer [BindColl BindIgnore BindScalar BindTuple Constant + FindColl FindRel FindScalar FindTuple PlainSymbol + RulesVar SrcVar Variable]]) + [datalog.parser.impl :as dpi] + [datalog.parser.impl.proto :as dpip] + [datahike.pull-api :as dpa] + [datalog.parser :refer [parse]] + [datalog.parser.pull :as dpp]) #?(:clj (:import [clojure.lang Reflector] [datalog.parser.type Aggregate BindColl BindIgnore BindScalar BindTuple - Constant FindColl FindRel FindScalar FindTuple PlainSymbol Pull - RulesVar SrcVar Variable] + Constant FindColl FindRel FindScalar FindTuple PlainSymbol Pull + RulesVar SrcVar Variable] [datahike.datom Datom] [java.lang.reflect Method] [java.util Date]))) @@ -28,6 +28,7 @@ ;; ---------------------------------------------------------------------------- + (def ^:const lru-cache-size 100) (declare -collect -resolve-clause resolve-clause) @@ -46,6 +47,7 @@ ;; Utilities + (defn single [coll] (assert (nil? (next coll)) "Expected single element") (first coll)) @@ -130,14 +132,14 @@ [idx-b (attrs-a sym)])) tlen (->> (vals attrs-a) (reduce max) (inc)) tuples' (persistent! - (reduce - (fn [acc tuple-b] - (let [tuple' (da/make-array tlen)] - (doseq [[idx-b idx-a] idxb->idxa] - (aset tuple' idx-a (#?(:cljs da/aget :clj get) tuple-b idx-b))) - (conj! acc tuple'))) - (transient (vec tuples-a)) - tuples-b))] + (reduce + (fn [acc tuple-b] + (let [tuple' (da/make-array tlen)] + (doseq [[idx-b idx-a] idxb->idxa] + (aset tuple' idx-a (#?(:cljs da/aget :clj get) tuple-b idx-b))) + (conj! acc tuple'))) + (transient (vec tuples-a)) + tuples-b))] (Relation. attrs-a tuples')) :else @@ -154,18 +156,19 @@ idxs1 (to-array (map (:attrs rel1) attrs1)) idxs2 (to-array (map (:attrs rel2) attrs2))] (Relation. - (zipmap (concat attrs1 attrs2) (range)) - (persistent! - (reduce - (fn [acc t1] - (reduce (fn [acc t2] - (conj! acc (join-tuples t1 idxs1 t2 idxs2))) - acc (:tuples rel2))) - (transient []) (:tuples rel1))))))) + (zipmap (concat attrs1 attrs2) (range)) + (persistent! + (reduce + (fn [acc t1] + (reduce (fn [acc t2] + (conj! acc (join-tuples t1 idxs1 t2 idxs2))) + acc (:tuples rel2))) + (transient []) (:tuples rel1))))))) ;; built-ins + (defn- -differ? [& xs] (let [l (count xs)] (not= (take (/ l 2) xs) (drop (/ l 2) xs)))) @@ -181,11 +184,11 @@ (defn- -get-some [db e & as] (reduce - (fn [_ a] - (when-some [datom (first (db/-search db [e a]))] - (reduced [(:a datom) (:v datom)]))) - nil - as)) + (fn [_ a] + (when-some [datom (first (db/-search db [e a]))] + (reduced [(:a datom) (:v datom)]))) + nil + as)) (defn- -missing? [db e a] @@ -200,8 +203,8 @@ (if b (reduced b) b)) nil args)) (defmulti -lesser? - {:arglists '([value & more])} - (fn [value & more] (class value))) + {:arglists '([value & more])} + (fn [value & more] (class value))) (defmethod -lesser? java.util.Date [^Date d0 ^Date d1] #?(:clj (.before ^Date d0 ^Date d1) @@ -228,8 +231,7 @@ (or (apply = value more) (apply -greater? value more))) -(def built-ins { - '= =, '== ==, 'not= not=, '!= not=, '< -lesser?, '> -greater?, '<= -lesser-equal?, '>= -greater-equal?, '+ +, '- -, +(def built-ins {'= =, '== ==, 'not= not=, '!= not=, '< -lesser?, '> -greater?, '<= -lesser-equal?, '>= -greater-equal?, '+ +, '- -, '* *, '/ /, 'quot quot, 'rem rem, 'mod mod, 'inc inc, 'dec dec, 'max max, 'min min, 'zero? zero?, 'pos? pos?, 'neg? neg?, 'even? even?, 'odd? odd?, 'compare compare, 'rand rand, 'rand-int rand-int, @@ -251,9 +253,9 @@ size (count coll) med (bit-shift-right size 1)] (cond-> (nth terms med) - (even? size) - (-> (+ (nth terms (dec med))) - (/ 2))))) + (even? size) + (-> (+ (nth terms (dec med))) + (/ 2))))) (variance [coll] (let [mean (avg coll) @@ -276,14 +278,14 @@ (first coll) (next coll))) ([n coll] (vec - (reduce (fn [acc x] - (cond - (< (count acc) n) - (sort compare (conj acc x)) - (neg? (compare x (last acc))) - (sort compare (conj (butlast acc) x)) - :else acc)) - [] coll)))) + (reduce (fn [acc x] + (cond + (< (count acc) n) + (sort compare (conj acc x)) + (neg? (compare x (last acc))) + (sort compare (conj (butlast acc) x)) + :else acc)) + [] coll)))) 'max (fn ([coll] (reduce (fn [acc x] (if (pos? (compare x acc)) @@ -291,14 +293,14 @@ (first coll) (next coll))) ([n coll] (vec - (reduce (fn [acc x] - (cond - (< (count acc) n) - (sort compare (conj acc x)) - (pos? (compare x (first acc))) - (sort compare (conj (next acc) x)) - :else acc)) - [] coll)))) + (reduce (fn [acc x] + (cond + (< (count acc) n) + (sort compare (conj acc x)) + (pos? (compare x (first acc))) + (sort compare (conj (next acc) x)) + :else acc)) + [] coll)))) 'sum sum 'rand (fn ([coll] (rand-nth coll)) @@ -311,6 +313,7 @@ ;; + (defn parse-rules [rules] (let [rules (if (string? rules) (edn/read-string rules) rules)] ;; for datahike.js interop (group-by ffirst rules))) @@ -450,7 +453,7 @@ getters-a (map #(getter-fn attrs-a %) attrs) key-fn-a (tuple-key-fn getters-a)] (assoc a - :tuples (filterv #(nil? (hash (key-fn-a %))) tuples-a)))) + :tuples (filterv #(nil? (hash (key-fn-a %))) tuples-a)))) (defn lookup-pattern-db [db pattern] ;; TODO optimize with bound attrs min/max values here @@ -565,7 +568,6 @@ :method-name method-name :args-classes args-classes}))))))) - (defn- resolve-method [method-sym] #?(:cljs nil :clj (let [method-str (name method-sym)] @@ -634,12 +636,12 @@ :let [[[_ & rule-args] & clauses] branch replacements (zipmap rule-args call-args)]] (walk/postwalk - #(if (free-var? %) - (db/some-of - (replacements %) - (symbol (str (name %) "__auto__" seqid))) - %) - clauses)))) + #(if (free-var? %) + (db/some-of + (replacements %) + (symbol (str (name %) "__auto__" seqid))) + %) + clauses)))) (defn remove-pairs [xs ys] (let [pairs (->> (map vector xs ys) @@ -711,16 +713,16 @@ ;; need to expand rule to branches (let [used-args (assoc (:used-args frame) rule - (conj (get (:used-args frame) rule []) call-args)) + (conj (get (:used-args frame) rule []) call-args)) branches (expand-rule rule-clause context used-args)] (recur (concat - (for [branch branches] - {:prefix-clauses prefix-clauses - :prefix-context prefix-context - :clauses (concatv branch next-clauses) - :used-args used-args - :pending-guards pending-gs}) - (next stack)) + (for [branch branches] + {:prefix-clauses prefix-clauses + :prefix-context prefix-context + :clauses (concatv branch next-clauses) + :used-args used-args + :pending-guards pending-gs}) + (next stack)) rel)))))))) rel)))) @@ -728,23 +730,23 @@ (if (satisfies? db/IDB source) (let [[e a v tx added] pattern] (-> - [(if (or (lookup-ref? e) (attr? e)) (db/entid-strict source e) e) - a - (if (and v (attr? a) (db/ref? source a) (or (lookup-ref? v) (attr? v))) (db/entid-strict source v) v) - (if (lookup-ref? tx) (db/entid-strict source tx) tx) - added] - (subvec 0 (count pattern)))) + [(if (or (lookup-ref? e) (attr? e)) (db/entid-strict source e) e) + a + (if (and v (attr? a) (db/ref? source a) (or (lookup-ref? v) (attr? v))) (db/entid-strict source v) v) + (if (lookup-ref? tx) (db/entid-strict source tx) tx) + added] + (subvec 0 (count pattern)))) pattern)) (defn dynamic-lookup-attrs [source pattern] (let [[e a v tx] pattern] (cond-> #{} - (free-var? e) (conj e) - (free-var? tx) (conj tx) - (and - (free-var? v) - (not (free-var? a)) - (db/ref? source a)) (conj v)))) + (free-var? e) (conj e) + (free-var? tx) (conj tx) + (and + (free-var? v) + (not (free-var? a)) + (db/ref? source a)) (conj v)))) (defn limit-rel [rel vars] (when-some [attrs' (not-empty (select-keys (:attrs rel) vars))] @@ -752,8 +754,8 @@ (defn limit-context [context vars] (assoc context - :rels (->> (:rels context) - (keep #(limit-rel % vars))))) + :rels (->> (:rels context) + (keep #(limit-rel % vars))))) (defn check-bound [context vars form] (let [bound (into #{} (mapcat #(keys (:attrs %)) (:rels context)))] @@ -816,8 +818,8 @@ context' (assoc context :rels [(reduce hash-join (:rels context))]) negation-context (reduce resolve-clause context' clauses) negation (subtract-rel - (single (:rels context')) - (reduce hash-join (:rels negation-context)))] + (single (:rels context')) + (reduce hash-join (:rels negation-context)))] (assoc context' :rels [negation])) '[not-join [*] *] ;; (not-join [vars] ...) @@ -828,8 +830,8 @@ negation-context (-> (reduce resolve-clause join-context clauses) (limit-context vars)) negation (subtract-rel - (single (:rels context')) - (reduce hash-join (:rels negation-context)))] + (single (:rels context')) + (reduce hash-join (:rels negation-context)))] (assoc context' :rels [negation])) '[*] ;; pattern @@ -941,7 +943,7 @@ (when (instance? Pull find) [(-context-resolve (:source find) context) (dpp/parse-pull - (-context-resolve (:pattern find) context))]))] + (-context-resolve (:pattern find) context))]))] (for [tuple resultset] (mapv (fn [env el] (if env @@ -976,7 +978,6 @@ #{})) resultset)) - (defn convert-to-return-maps [{:keys [mapping-type mapping-keys]} resultset] (let [mapping-keys (map #(get % :mapping-key) mapping-keys) convert-fn (fn [mkeys] diff --git a/src/datahike/query_v3.cljc b/src/datahike/query_v3.cljc index 3a3298a12..fd8687ea7 100644 --- a/src/datahike/query_v3.cljc +++ b/src/datahike/query_v3.cljc @@ -15,15 +15,14 @@ [datalog.parser.impl.util :as dpiu] [datalog.parser.impl :as dpi]) #?(:clj - (:import + (:import [datalog.parser.type - BindColl BindIgnore BindScalar BindTuple - Constant DefaultSrc Pattern RulesVar SrcVar Variable - Not Or And Predicate PlainSymbol] + BindColl BindIgnore BindScalar BindTuple + Constant DefaultSrc Pattern RulesVar SrcVar Variable + Not Or And Predicate PlainSymbol] [clojure.lang IReduceInit Counted] [datahike.datom Datom]))) - (declare resolve-clauses collect-rel-xf collect-to) (def ^:const lru-cache-size 100) @@ -61,20 +60,20 @@ (reify NativeColl (-native-coll [_] m) - + clojure.lang.IEditableCollection (asTransient [this] this) - + clojure.lang.ITransientAssociative (assoc [this k v] (.put m k v) this) - + clojure.lang.ITransientCollection (persistent [this] this) - + clojure.lang.IPersistentCollection clojure.lang.Counted (count [_] (.size m)) - + clojure.lang.ILookup (valAt [_ k] (.get m k)) (valAt [_ k nf] (or (.get m k) nf)))) @@ -86,18 +85,18 @@ (reify NativeColl (-native-coll [_] l) - + clojure.lang.IEditableCollection (asTransient [this] this) - + clojure.lang.ITransientCollection (conj [this v] (.add l v) this) (persistent [this] this) - + clojure.lang.IPersistentCollection clojure.lang.Counted (count [_] (.size l)) - + clojure.lang.IReduceInit (reduce [_ f s] (loop [i 0 @@ -110,17 +109,17 @@ (reify NativeColl (-native-coll [_] arr) - + IEditableCollection (-as-transient [this] this) - + ITransientCollection (-conj! [this v] (.push arr v) this) (-persistent! [this] this) - + ICounted (-count [_] (alength arr)) - + IReduce (-reduce [_ f s] (loop [i 0 @@ -135,21 +134,21 @@ (reify NativeColl (-native-coll [_] set) - + clojure.lang.IEditableCollection (asTransient [this] this) - + clojure.lang.ITransientCollection (conj [this v] (.add set v) this) (persistent [this] this) - + clojure.lang.IPersistentCollection clojure.lang.IPersistentSet (contains [_ k] (.contains set k)) clojure.lang.Counted (count [_] (.size set)) - + clojure.lang.IReduceInit (reduce [_ f s] (let [iter (.iterator set)] @@ -230,7 +229,7 @@ :cljs (str (type rel)))) (.write "{:symbols ") (.write (pr-str (-symbols rel))) - (.write ", :coll " ) + (.write ", :coll ") (.write (pr-str (persistent! (-fold rel #(conj! %1 (seq %2)) (transient []))))) (.write "}"))) @@ -268,6 +267,7 @@ ;;; CollRelation + (deftype CollRelation [offset-map coll] IRelation (-project [_ syms] @@ -294,18 +294,18 @@ (defn coll-rel [symbols coll] (let [offset-map (reduce-kv - (fn [acc e i] - (cond - (instance? BindScalar e) - (assoc acc (get-in e [:variable :symbol]) i) - (instance? Variable e) - (assoc acc (:symbol e) i) - (and (symbol? e) - (not= '_ e)) - (assoc acc e i) - :else acc)) - {} - (zipmap symbols (range)))] + (fn [acc e i] + (cond + (instance? BindScalar e) + (assoc acc (get-in e [:variable :symbol]) i) + (instance? Variable e) + (assoc acc (:symbol e) i) + (and (symbol? e) + (not= '_ e)) + (assoc acc e i) + :else acc)) + {} + (zipmap symbols (range)))] (->CollRelation offset-map coll))) #?(:clj @@ -356,6 +356,7 @@ ;;; SingletonRelation + (deftype SingletonRelation [] IRelation (-symbols [_] []) @@ -389,18 +390,17 @@ coll (-fold rel1 (fn [acc t1] (-fold rel2 - (fn [acc t2] - (conj! acc (join-tuples rel1 t1 idxs1 - rel2 t2 idxs2 - arity - target-idxs1 - target-idxs2))) - acc)) + (fn [acc t2] + (conj! acc (join-tuples rel1 t1 idxs1 + rel2 t2 idxs2 + arity + target-idxs1 + target-idxs2))) + acc)) (fast-arr))] (array-rel - (concatv (-symbols rel1) (-symbols rel2)) - (persistent! coll)))) - + (concatv (-symbols rel1) (-symbols rel2)) + (persistent! coll)))) (defn product-all [rels] (reduce product rels)) ;; TODO check for empty rels @@ -420,21 +420,19 @@ (-copy-tuple rel t idxs arr target-idxs) (vec arr))))))) - (defn hash-map-rel [rel syms] (let [key-fn (key-fn rel syms)] (->> - (-fold rel - (fn [hash t] - (let [key (key-fn t) - old (get hash key)] - (if (nil? old) - (assoc! hash key (conj! (fast-arr) t)) - (do (conj! old t) hash)))) - (transient (fast-map))) + (-fold rel + (fn [hash t] + (let [key (key-fn t) + old (get hash key)] + (if (nil? old) + (assoc! hash key (conj! (fast-arr) t)) + (do (conj! old t) hash)))) + (transient (fast-map))) (persistent!)))) - (defn hash-join [rel1 hash1 join-syms rel2] (let [syms1 (-symbols rel1) syms2 (-symbols rel2) @@ -448,46 +446,47 @@ arity (+ arity1 arity2) target-idxs1 (arange 0 arity1) target-idxs2 (arange arity1 arity) - + coll (-fold rel2 ;; iterate over rel2 - (fn [acc t2] - (let [tuples1 (get hash1 (key-fn2 t2))] - (if (nil? tuples1) - acc - (reduce (fn [acc t1] - (conj! acc (join-tuples rel1 t1 idxs1 - rel2 t2 idxs2 - arity - target-idxs1 - target-idxs2))) - acc (persistent! tuples1))))) - (fast-arr))] + (fn [acc t2] + (let [tuples1 (get hash1 (key-fn2 t2))] + (if (nil? tuples1) + acc + (reduce (fn [acc t1] + (conj! acc (join-tuples rel1 t1 idxs1 + rel2 t2 idxs2 + arity + target-idxs1 + target-idxs2))) + acc (persistent! tuples1))))) + (fast-arr))] (array-rel full-syms (persistent! coll)))) ;; Bindings + (defn- bind! [tuples binding source indexes] (condp instance? binding BindIgnore - tuples + tuples BindScalar - (let [symbol (get-in binding [:variable :symbol]) - idx (get indexes symbol)] - (run! #(da/aset % idx source) tuples) - tuples) + (let [symbol (get-in binding [:variable :symbol]) + idx (get indexes symbol)] + (run! #(da/aset % idx source) tuples) + tuples) BindColl - (if (not (db/seqable? source)) - (raise "Cannot bind value " source " to collection " (dpi/get-source binding) - {:error :query/binding, :value source, :binding (dpi/get-source binding)}) - (let [inner-binding (:binding binding)] - (case (count source) - 0 [] - 1 (bind! tuples inner-binding (first source) indexes) - (into [] ;; TODO fast-arr + (if (not (db/seqable? source)) + (raise "Cannot bind value " source " to collection " (dpi/get-source binding) + {:error :query/binding, :value source, :binding (dpi/get-source binding)}) + (let [inner-binding (:binding binding)] + (case (count source) + 0 [] + 1 (bind! tuples inner-binding (first source) indexes) + (into [] ;; TODO fast-arr (comp (map #(bind! tuples inner-binding % indexes)) cat (map da/aclone)) @@ -497,19 +496,18 @@ (let [bindings (:bindings binding)] (when-not (db/seqable? source) (raise "Cannot bind value " source " to tuple " (dpi/get-source binding) - {:error :query/binding, :value source, :binding (dpi/get-source binding)})) + {:error :query/binding, :value source, :binding (dpi/get-source binding)})) (when (< (count source) (count bindings)) (raise "Not enough elements in a collection " source " to bind tuple " (dpi/get-source binding) - {:error :query/binding, :value source, :binding (dpi/get-source binding)})) + {:error :query/binding, :value source, :binding (dpi/get-source binding)})) (reduce (fn [ts [b s]] (bind! ts b s indexes)) tuples (zip bindings source))) - - :else - (raise "Unknown binding form " (dpi/get-source binding) - {:error :query/binding, :value source, :binding (dpi/get-source binding)}))) + :else + (raise "Unknown binding form " (dpi/get-source binding) + {:error :query/binding, :value source, :binding (dpi/get-source binding)}))) (defn bind [binding source] (let [syms (map :symbol (dpi/collect-vars-distinct binding)) @@ -517,26 +515,25 @@ tuples (bind! [(da/make-array (count syms))] binding source indexes)] (array-rel syms tuples))) - (defn- rel->consts [rel] {:pre [(== (-size rel) 1)]} (let [tuple (-fold rel (fn [_ t] t) nil)] (into {} - (map #(vector % ((-getter rel %) tuple)) (-symbols rel))))) + (map #(vector % ((-getter rel %) tuple)) (-symbols rel))))) (defn- resolve-in [context [binding value]] (cond (and (instance? BindScalar binding) (instance? SrcVar (:variable binding))) - (update-in context [:sources] assoc (get-in binding [:variable :symbol]) value) + (update-in context [:sources] assoc (get-in binding [:variable :symbol]) value) ;; (and (instance? BindScalar binding) ;; (instance? RulesVar (:variable binding))) ;; (assoc context :rules (parse-rules value)) :else - (let [rel (bind binding value)] - (if (== 1 (-size rel)) - (update-in context [:consts] merge (rel->consts rel)) - (update-in context [:rels] conj rel))))) + (let [rel (bind binding value)] + (if (== 1 (-size rel)) + (update-in context [:consts] merge (rel->consts rel)) + (update-in context [:rels] conj rel))))) (defn resolve-ins [context bindings values] (when (not= (count bindings) (count values)) @@ -552,7 +549,6 @@ (defprotocol IClause (-resolve-clause [clause context])) - (defn get-source [context source] (let [symbol (cond (instance? SrcVar source) (:symbol source) @@ -561,10 +557,11 @@ (or (get (:sources context) symbol) (raise "Source " symbol " is not defined" {:error :query/where, :symbol symbol})))) - + ;; Patterns + (defn resolve-pattern-db [db clause] ;; TODO optimize with bound attrs min/max values here (let [pattern (:pattern clause) @@ -572,7 +569,6 @@ datoms (db/-search db search-pattern)] (coll-rel (:pattern clause) datoms))) - (defn- matches-pattern? [idxs tuple] ;; TODO handle repeated vars ;; (when-not (db/seqable? tuple) ;; (raise "Cannot match pattern " (dpi/get-source clause) " because tuple is not a collection: " tuple @@ -581,18 +577,17 @@ ;; (raise "Not enough elements in a relation tuple " tuple " to match " (dpi/get-source clause) ;; {:error :query/where, :value tuple, :binding (dpi/get-source clause)})) (reduce-kv - (fn [_ i v] - (if (not= (nth tuple i) v) ;; nth? - (reduced false) - true)) - true - idxs)) - + (fn [_ i v] + (if (not= (nth tuple i) v) ;; nth? + (reduced false) + true)) + true + idxs)) (defn resolve-pattern-coll [coll clause] (when-not (db/seqable? coll) (raise "Cannot match by pattern " (dpi/get-source clause) " because source is not a collection: " coll - {:error :query/where, :value coll, :binding (dpi/get-source clause)})) + {:error :query/where, :value coll, :binding (dpi/get-source clause)})) (let [pattern (:pattern clause) idxs (->> (map #(when (instance? Constant %1) [%2 (:value %1)]) pattern (range)) (remove nil?) @@ -600,24 +595,22 @@ data (filter #(matches-pattern? idxs %) coll)] (coll-rel pattern data))) - (defn clause-syms [clause] (into #{} (map :symbol) (dpiu/collect #(instance? Variable %) clause #{}))) - (defn substitute-constants [clause context] (let [syms (clause-syms clause) consts (:consts context)] (if (some #(contains? consts %) syms) (dpu/postwalk - clause - (fn [form] - (if (instance? Variable form) - (let [sym (:symbol form)] - (if-let [subs (get (:consts context) (:symbol form))] - (Constant. subs) - form)) - form))) + clause + (fn [form] + (if (instance? Variable form) + (let [sym (:symbol form)] + (if-let [subs (get (:consts context) (:symbol form))] + (Constant. subs) + form)) + form))) clause))) (defn related-rels [context syms] @@ -625,7 +618,6 @@ (->> (:rels context) (filter #(some syms (-symbols %)))))) - (defn extract-rels [context syms] (let [syms (set syms) rels (:rels context) @@ -636,14 +628,12 @@ (let [unrelated (remove related? rels)] [related (assoc context :rels unrelated)])))) - (defn join-unrelated [context rel] (case (long (-size rel)) 0 empty-context 1 (update context :consts merge (rel->consts rel)) (update context :rels conj rel))) - (defn hash-join-rel [context rel] (if (== 0 (-size rel)) empty-context @@ -658,7 +648,6 @@ rel* (hash-join related-rel hash join-syms rel)] (join-unrelated context* rel*)))))) - (defn resolve-pattern [context clause] (let [clause* (substitute-constants clause context) rel (let [source (get-source context (:source clause))] @@ -667,7 +656,6 @@ (resolve-pattern-coll source clause*)))] (hash-join-rel context rel))) - (defn project-rel [rel syms] (let [rel-syms (set (-symbols rel)) syms (set syms)] @@ -676,15 +664,13 @@ (empty? (set/intersection rel-syms syms)) nil :else (-project rel syms)))) - (defn project-context [context syms] (assoc context - :consts (select-keys (:consts context) syms) - :rels (into [] - (comp (map #(project-rel % syms)) - (remove nil?)) - (:rels context)))) - + :consts (select-keys (:consts context) syms) + :rels (into [] + (comp (map #(project-rel % syms)) + (remove nil?)) + (:rels context)))) (defn collect-opt "Collects values if only one symbol, vecs if many (compatible with key-fn)" @@ -703,13 +689,11 @@ (persistent!))))) (collect-to context syms (fast-set) [(map vec)]))) - (defn subtract-from-rel [rel syms exclude-key-set] (let [key-fn1 (key-fn rel syms) pred (fn [t1] (contains? exclude-key-set (key-fn1 t1)))] (-alter-coll rel #(into (fast-arr) (remove pred) %)))) - (defn subtract-contexts [context1 context2 syms] (if (:empty? context2) ;; empty context2 means there’s nothing to subtract context1 @@ -724,14 +708,12 @@ rel1* (subtract-from-rel rel1 syms* set2)] (join-unrelated context1* rel1*)))))) - (defn upd-default-source [context clause] (let [source (:source clause)] (if (instance? SrcVar source) (assoc context :default-source-symbol (:symbol source)) context))) - (defn check-bound [context syms form] (let [context-syms (-> #{} (into (keys (:consts context))) @@ -743,30 +725,28 @@ {:error :query/where :form form :vars missing})))))) - (defn resolve-not [context clause] (let [{:keys [source vars clauses]} clause syms (into #{} (map :symbol) vars) _ (check-bound context syms (dpi/get-source clause)) context* (-> context - (project-context syms) ;; sub-context will only see symbols Not is joined by - (upd-default-source clause) - (resolve-clauses clauses))] + (project-context syms) ;; sub-context will only see symbols Not is joined by + (upd-default-source clause) + (resolve-clauses clauses))] (subtract-contexts context context* syms))) - (defn resolve-or [context clause] (let [{:keys [source rule-vars clauses]} clause {:keys [required free]} rule-vars _ (check-bound context (map :symbol required) (dpi/get-source clause)) syms (into #{} (map :symbol) (concat required free)) context* (-> context - (project-context syms) - (upd-default-source clause)) + (project-context syms) + (upd-default-source clause)) contexts (->> clauses - (map #(-resolve-clause % context*)) - (remove :empty?))] + (map #(-resolve-clause % context*)) + (remove :empty?))] (if (empty? contexts) empty-context ;; everything resolved to empty rel, short-circuit (let [non-consts (set/difference syms (set (keys (:consts context))))] @@ -776,7 +756,6 @@ rel (array-rel non-consts (into (first arrays) cat (next arrays)))] (hash-join-rel context rel))))))) - (defn collect-args! [context args target form] (let [consts (:consts context) sources (:sources context)] @@ -784,16 +763,15 @@ :let [sym (:symbol arg)]] (cond (instance? Variable arg) - (when (contains? consts sym) - (da/aset target i (get consts sym))) + (when (contains? consts sym) + (da/aset target i (get consts sym))) (instance? SrcVar arg) - (if (contains? sources sym) - (da/aset target i (get sources sym)) - (throw (ex-info (str "Unbound source variable: " sym " in " form) - { :error :query/where, :form form, :var sym }))) + (if (contains? sources sym) + (da/aset target i (get sources sym)) + (throw (ex-info (str "Unbound source variable: " sym " in " form) + {:error :query/where, :form form, :var sym}))) (instance? Constant arg) - (da/aset target i (:value arg)))))) - + (da/aset target i (:value arg)))))) (defn get-f [context fun form] (let [sym (:symbol fun)] @@ -805,7 +783,6 @@ (throw (ex-info (str "Unknown function " sym " in " form) {:error :query/where, :form form, :var sym})))))) - (defn resolve-predicate [context clause] (let [{fun :fn, args :args} clause form (dpi/get-source clause) @@ -830,8 +807,8 @@ (let [rel (first rels) idxs (-indexes rel args-syms) pred (fn [tuple] - (-copy-tuple rel tuple idxs args-arr args-idxs) - (apply f (vec args-arr))) + (-copy-tuple rel tuple idxs args-arr args-idxs) + (apply f (vec args-arr))) rel* (-alter-coll rel #(filterv pred %))] (join-unrelated context* rel*)) (let [prod-syms (mapcat -symbols rels) @@ -841,15 +818,14 @@ idxs (-indexes prod-rel args-syms) pred (fn [tuple] - (-copy-tuple prod-rel tuple idxs args-arr args-idxs) - (apply f (vec args-arr))) + (-copy-tuple prod-rel tuple idxs args-arr args-idxs) + (apply f (vec args-arr))) array (into (fast-arr) - (apply comp (concat xfs [(filter pred)])) - [(da/make-array (count prod-syms))]) + (apply comp (concat xfs [(filter pred)])) + [(da/make-array (count prod-syms))]) prod-rel* (array-rel prod-syms array)] - (join-unrelated context* prod-rel*))))))) - + (join-unrelated context* prod-rel*))))))) (extend-protocol IClause Pattern @@ -868,7 +844,6 @@ (-resolve-clause [clause context] (resolve-predicate context clause))) - (defn println-context [context] (print "{:rels") (if (empty? (:rels context)) @@ -876,7 +851,6 @@ (doseq [rel (:rels context)] (println " " rel))) (println " :consts" (:consts context) "}")) - (defn resolve-clauses [context clauses] (reduce (fn [context clause] @@ -889,14 +863,12 @@ context*)))) context clauses)) - (defn collect-consts [syms-indexed specimen consts] (doseq [[sym i] syms-indexed] (when (contains? consts sym) (let [val (get consts sym)] (da/aset specimen i val))))) - (defn collect-rel-xf [syms-indexed rel] (let [sym+idx (for [[sym i] syms-indexed :when (has? (-symbols rel) sym)] @@ -908,13 +880,12 @@ ([] (rf)) ([result] (rf result)) ([result specimen] - (-fold rel - (fn [acc tuple] - (let [t (da/aclone specimen)] - (-copy-tuple rel tuple idxs t target-idxs) - (rf acc t))) - result)))))) - + (-fold rel + (fn [acc tuple] + (let [t (da/aclone specimen)] + (-copy-tuple rel tuple idxs t target-idxs) + (rf acc t))) + result)))))) (defn collect-to ([context syms acc] @@ -923,14 +894,14 @@ (collect-to context syms acc xfs (da/make-array (count syms)))) ([context syms acc xfs specimen] ;; TODO don't collect if array-rel and matches symbols - (if (:empty? context) - acc - (let [syms-indexed (vec (zip syms (range))) - _ (collect-consts syms-indexed specimen (:consts context)) - related-rels (related-rels context syms) - xfs (-> (map #(collect-rel-xf syms-indexed %) related-rels) - (concat xfs))] - (into acc (apply comp xfs) [specimen]))))) + (if (:empty? context) + acc + (let [syms-indexed (vec (zip syms (range))) + _ (collect-consts syms-indexed specimen (:consts context)) + related-rels (related-rels context syms) + xfs (-> (map #(collect-rel-xf syms-indexed %) related-rels) + (concat xfs))] + (into acc (apply comp xfs) [specimen]))))) ;; Query @@ -938,7 +909,6 @@ (def query-cache (volatile! (datahike.lru/lru lru-cache-size))) - (defn parse-query [q] (if-let [cached (get @query-cache q nil)] cached @@ -946,46 +916,44 @@ (vswap! query-cache assoc q qp) qp))) - (defn q [q & inputs] (let [parsed-q (parse-query q) - context { :rels [] - :consts {} - :sources {} - :rules {} - :default-source-symbol '$ } + context {:rels [] + :consts {} + :sources {} + :rules {} + :default-source-symbol '$} context (resolve-ins context (:qin parsed-q) inputs) context (resolve-clauses context (:qwhere parsed-q)) syms (concat (dpi/find-vars (:qfind parsed-q)) - (map :symbol (:qwith parsed-q)))] + (map :symbol (:qwith parsed-q)))] (native-coll (collect-to context syms (fast-set) [(map vec)])))) - (comment (t/test-ns 'datahike.test.query-v3) - (let [query '[ :find ?lid ?status ?starttime ?endtime (min ?paid) (distinct ?studentinfo) ?lgid + (let [query '[:find ?lid ?status ?starttime ?endtime (min ?paid) (distinct ?studentinfo) ?lgid :in $ ?tid ?week ?list :where [?lid :lesson/teacherid ?tid] - [?lid :lesson/week ?week] - [?lid :lesson/lessongroupid ?lgid] - [?eid :enrollment/lessongroup_id ?lgid] - [?eid :enrollment/student_id ?sid] - [?iid :invoice/enrollment_id ?eid] - [?sid :student/firstname ?fname] - [?sid :student/lastname ?lname] - [?iid :invoice/paid ?paid] - [?lid :lesson/status ?status] - [?lid :lesson/starttime ?starttime] - [?lid :lesson/endtime ?endtime] - [(?list ?sid ?fname ?lname) ?studentinfo]] + [?lid :lesson/week ?week] + [?lid :lesson/lessongroupid ?lgid] + [?eid :enrollment/lessongroup_id ?lgid] + [?eid :enrollment/student_id ?sid] + [?iid :invoice/enrollment_id ?eid] + [?sid :student/firstname ?fname] + [?sid :student/lastname ?lname] + [?iid :invoice/paid ?paid] + [?lid :lesson/status ?status] + [?lid :lesson/starttime ?starttime] + [?lid :lesson/endtime ?endtime] + [(?list ?sid ?fname ?lname) ?studentinfo]] parsed (parse query)] (perf/minibench "postwalk" - (dpu/postwalk parsed identity)) + (dpu/postwalk parsed identity)) (perf/minibench "parse-query" - (parse query)) + (parse query)) (perf/minibench "substitute-constants" - (substitute-constants (first (:where parsed)) {:consts {'?tid 7}}))) + (substitute-constants (first (:where parsed)) {:consts {'?tid 7}}))) (defn random-man [] {:name (rand-nth ["Ivan" "Petr" "Sergei" "Oleg" "Yuri" "Dmitry" "Fedor" "Denis"]) @@ -1000,7 +968,7 @@ (perf/minibench (str "NEW " name) (apply datahike.query-v3/q q args)) nil) - (do + (do #_(require '[datahike.query-v3 :as q] :reload) (def db (d/db-with (d/empty-db) (repeatedly 10000 random-man))) @@ -1008,12 +976,12 @@ (bench "q2 const" '[:find ?e :where [?e :name "Ivan"] - [?e :age 1]] + [?e :age 1]] db) #_(bench "q2 const in" - '[:find ?e - :in $ ?n - :where [?e :name "Ivan"] - [?e :age ?n]] - db 1))) + '[:find ?e + :in $ ?n + :where [?e :name "Ivan"] + [?e :age ?n]] + db 1))) diff --git a/src/datahike/schema.cljc b/src/datahike/schema.cljc index bcc5133c1..968290621 100644 --- a/src/datahike/schema.cljc +++ b/src/datahike/schema.cljc @@ -91,11 +91,9 @@ (def schema-keys #{:db/ident :db/isComponent :db/noHistory :db/valueType :db/cardinality :db/unique :db/index :db.install/_attribute :db/doc}) - (s/def ::old-schema-val (s/keys :req [:db/valueType :db/cardinality] :opt [:db/ident :db/unique :db/index :db.install/_attribute :db/doc :db/noHistory])) - (s/def ::old-schema-key keyword?) (s/def ::old-schema (s/nilable (s/map-of ::old-schema-key ::old-schema-val))) diff --git a/src/deps.cljs b/src/deps.cljs index 60c52a325..63df8bb72 100644 --- a/src/deps.cljs +++ b/src/deps.cljs @@ -1,3 +1 @@ -{ - :externs ["datahike/externs.js"] -} +{:externs ["datahike/externs.js"]} diff --git a/test/datahike/integration_test/config_record_file_test.clj b/test/datahike/integration_test/config_record_file_test.clj index d1a207c0f..b3d55cd87 100644 --- a/test/datahike/integration_test/config_record_file_test.clj +++ b/test/datahike/integration_test/config_record_file_test.clj @@ -6,59 +6,59 @@ (def config {:store {:backend :file :path "/tmp/file-test-1"}}) (defn config-record-file-test-fixture [f] - (d/delete-database config) - (d/create-database config) - (def conn (d/connect config)) + (d/delete-database config) + (d/create-database config) + (def conn (d/connect config)) ;; the first transaction will be the schema we are using - (d/transact conn [{:db/ident :name - :db/valueType :db.type/string - :db/cardinality :db.cardinality/one} - {:db/ident :age - :db/valueType :db.type/long - :db/cardinality :db.cardinality/one}]) + (d/transact conn [{:db/ident :name + :db/valueType :db.type/string + :db/cardinality :db.cardinality/one} + {:db/ident :age + :db/valueType :db.type/long + :db/cardinality :db.cardinality/one}]) ;; lets add some data and wait for the transaction - (d/transact conn [{:name "Alice", :age 20} - {:name "Bob", :age 30} - {:name "Charlie", :age 40} - {:age 15}]) + (d/transact conn [{:name "Alice", :age 20} + {:name "Bob", :age 30} + {:name "Charlie", :age 40} + {:age 15}]) - (f)) + (f)) (use-fixtures :once config-record-file-test-fixture) (deftest config-record-file-test - (is (= #{[3 "Alice" 20] [4 "Bob" 30] [5 "Charlie" 40]} - (d/q '[:find ?e ?n ?a - :where - [?e :name ?n] - [?e :age ?a]] - @conn))) + (is (= #{[3 "Alice" 20] [4 "Bob" 30] [5 "Charlie" 40]} + (d/q '[:find ?e ?n ?a + :where + [?e :name ?n] + [?e :age ?a]] + @conn))) ;; add new entity data using a hash map - (d/transact conn {:tx-data [{:db/id 3 :age 25}]}) + (d/transact conn {:tx-data [{:db/id 3 :age 25}]}) ;; if you want to work with queries like in ;; https://grishaev.me/en/datomic-query/, ;; you may use a hashmap - (is (= #{[5 "Charlie" 40] [4 "Bob" 30] [3 "Alice" 25]} - (d/q {:query '{:find [?e ?n ?a] - :where [[?e :name ?n] - [?e :age ?a]]} - :args [@conn]}))) + (is (= #{[5 "Charlie" 40] [4 "Bob" 30] [3 "Alice" 25]} + (d/q {:query '{:find [?e ?n ?a] + :where [[?e :name ?n] + [?e :age ?a]]} + :args [@conn]}))) ;; query the history of the data - (is (= #{[20] [25]} - (d/q '[:find ?a - :where - [?e :name "Alice"] - [?e :age ?a]] - (d/history @conn)))) + (is (= #{[20] [25]} + (d/q '[:find ?a + :where + [?e :name "Alice"] + [?e :age ?a]] + (d/history @conn)))) - (d/release conn) + (d/release conn) - (is (d/database-exists? config)) + (is (d/database-exists? config)) - (d/delete-database config) + (d/delete-database config) - (is (not (d/database-exists? config)))) + (is (not (d/database-exists? config)))) diff --git a/test/datahike/test.cljc b/test/datahike/test.cljc index f66eb79bf..e91d0c7e4 100644 --- a/test/datahike/test.cljc +++ b/test/datahike/test.cljc @@ -1,41 +1,40 @@ (ns datahike.test (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - #?(:clj [clojure.java.shell :as sh]) - datahike.test.core - datahike.test.components - datahike.test.config - datahike.test.conn - datahike.test.db - datahike.test.entity - datahike.test.explode - datahike.test.filter - datahike.test.ident - datahike.test.index - datahike.test.listen - datahike.test.lookup-refs - datahike.test.lru - datahike.test.migrate - datahike.test.pull-api - datahike.test.purge - datahike.test.query - datahike.test.query-aggregates - datahike.test.query-find-specs - datahike.test.query-fns - datahike.test.query-interop - datahike.test.query-not - datahike.test.query-or - datahike.test.query-pull - datahike.test.query-rules - datahike.test.query-v3 - datahike.test.schema - datahike.test.store - datahike.test.time-variance - datahike.test.transact - datahike.test.validation - datahike.test.upsert)) - + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + #?(:clj [clojure.java.shell :as sh]) + datahike.test.core + datahike.test.components + datahike.test.config + datahike.test.conn + datahike.test.db + datahike.test.entity + datahike.test.explode + datahike.test.filter + datahike.test.ident + datahike.test.index + datahike.test.listen + datahike.test.lookup-refs + datahike.test.lru + datahike.test.migrate + datahike.test.pull-api + datahike.test.purge + datahike.test.query + datahike.test.query-aggregates + datahike.test.query-find-specs + datahike.test.query-fns + datahike.test.query-interop + datahike.test.query-not + datahike.test.query-or + datahike.test.query-pull + datahike.test.query-rules + datahike.test.query-v3 + datahike.test.schema + datahike.test.store + datahike.test.time-variance + datahike.test.transact + datahike.test.validation + datahike.test.upsert)) (defn ^:export test-clj [] (datahike.test.core/wrap-res #(t/run-all-tests #"datahike\..*"))) diff --git a/test/datahike/test/cljs.cljc b/test/datahike/test/cljs.cljc index 7df7c9c6d..86381f962 100644 --- a/test/datahike/test/cljs.cljc +++ b/test/datahike/test/cljs.cljc @@ -1,10 +1,10 @@ (ns datahike.test.cljs (:require - [clojure.string :as str] - [cljs.test :as t]) + [clojure.string :as str] + [cljs.test :as t]) #?(:cljs - (:require-macros - [datahike.test.cljs]))) + (:require-macros + [datahike.test.cljs]))) ; The datahike.test.cljs namespace exists only for the side ; effect of extending the cljs.test/assert-expr multimethod. @@ -16,14 +16,14 @@ ; cljs compilation. #?(:clj -(defmethod t/assert-expr 'thrown-msg? [menv msg form] - (let [[_ match & body] form] - `(try - ~@body - (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) - (catch :default e# - (let [m# (.-message e#)] - (if (= ~match m#) - (t/do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) - (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual e#})) - e#)))))) \ No newline at end of file + (defmethod t/assert-expr 'thrown-msg? [menv msg form] + (let [[_ match & body] form] + `(try + ~@body + (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (catch :default e# + (let [m# (.-message e#)] + (if (= ~match m#) + (t/do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) + (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual e#})) + e#)))))) \ No newline at end of file diff --git a/test/datahike/test/components.cljc b/test/datahike/test/components.cljc index 14302189b..074605bce 100644 --- a/test/datahike/test/components.cljc +++ b/test/datahike/test/components.cljc @@ -1,11 +1,11 @@ (ns datahike.test.components (:require - [#?(:cljs cljs.reader :clj clojure.edn) :as edn] - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + [#?(:cljs cljs.reader :clj clojure.edn) :as edn] + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (t/use-fixtures :once tdc/no-namespace-maps) @@ -14,19 +14,19 @@ (deftest test-components (is (thrown-msg? "Bad attribute specification for :profile: {:db/isComponent true} should also have {:db/valueType :db.type/ref}" - (d/empty-db {:profile {:db/isComponent true}}))) + (d/empty-db {:profile {:db/isComponent true}}))) (is (thrown-msg? "Bad attribute specification for {:profile {:db/isComponent \"aaa\"}}, expected one of #{true false}" - (d/empty-db {:profile {:db/isComponent "aaa" :db/valueType :db.type/ref}}))) - + (d/empty-db {:profile {:db/isComponent "aaa" :db/valueType :db.type/ref}}))) + (let [db (d/db-with - (d/empty-db {:profile {:db/valueType :db.type/ref - :db/isComponent true}}) - [{:db/id 1 :name "Ivan" :profile 3} - {:db/id 3 :email "@3"} - {:db/id 4 :email "@4"}]) + (d/empty-db {:profile {:db/valueType :db.type/ref + :db/isComponent true}}) + [{:db/id 1 :name "Ivan" :profile 3} + {:db/id 3 :email "@3"} + {:db/id 4 :email "@4"}]) visible #(edn/read-string (pr-str %)) touched #(visible (d/touch %))] - + (testing "touch" (is (= (touched (d/entity db 1)) {:db/id 1 @@ -46,44 +46,44 @@ #{})) (is (= (d/q '[:find ?a ?v :where [3 ?a ?v]] db) #{})))) - + (testing "retractAttribute" (let [db (d/db-with db [[:db.fn/retractAttribute 1 :profile]])] (is (= (d/q '[:find ?a ?v :where [3 ?a ?v]] db) #{})))) - + (testing "reverse navigation" (is (= (visible (:_profile (d/entity db 3))) {:db/id 1}))))) (deftest test-components-multival (let [db (d/db-with - (d/empty-db {:profile {:db/valueType :db.type/ref - :db/cardinality :db.cardinality/many - :db/isComponent true}}) - [{:db/id 1 :name "Ivan" :profile [3 4]} - {:db/id 3 :email "@3"} - {:db/id 4 :email "@4"}]) + (d/empty-db {:profile {:db/valueType :db.type/ref + :db/cardinality :db.cardinality/many + :db/isComponent true}}) + [{:db/id 1 :name "Ivan" :profile [3 4]} + {:db/id 3 :email "@3"} + {:db/id 4 :email "@4"}]) visible #(edn/read-string (pr-str %)) touched #(visible (d/touch %))] - + (testing "touch" (is (= (touched (d/entity db 1)) {:db/id 1 :name "Ivan" :profile #{{:db/id 3 :email "@3"} {:db/id 4 :email "@4"}}}))) - + (testing "retractEntity" (let [db (d/db-with db [[:db.fn/retractEntity 1]])] (is (= (d/q '[:find ?a ?v :in $ [?e ...] :where [?e ?a ?v]] db [1 3 4]) #{})))) - + (testing "retractAttribute" (let [db (d/db-with db [[:db.fn/retractAttribute 1 :profile]])] (is (= (d/q '[:find ?a ?v :in $ [?e ...] :where [?e ?a ?v]] db [3 4]) #{})))) - + (testing "reverse navigation" (is (= (visible (:_profile (d/entity db 3))) {:db/id 1}))))) diff --git a/test/datahike/test/conn.cljc b/test/datahike/test/conn.cljc index 468653ca8..195929220 100644 --- a/test/datahike/test/conn.cljc +++ b/test/datahike/test/conn.cljc @@ -1,13 +1,13 @@ (ns datahike.test.conn (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (def schema (merge db/implicit-schema - { :aka { :db/cardinality :db.cardinality/many }})) + {:aka {:db/cardinality :db.cardinality/many}})) (def datoms #{(d/datom 1 :age 17) (d/datom 1 :name "Ivan")}) @@ -16,23 +16,23 @@ (let [conn (d/create-conn)] (is (= #{} (set (d/datoms @conn :eavt)))) (is (= db/implicit-schema (:schema @conn)))) - + (let [conn (d/create-conn schema)] (is (= #{} (set (d/datoms @conn :eavt)))) (is (= schema (:schema @conn)))) - + (let [conn (d/conn-from-datoms datoms)] (is (= datoms (set (d/datoms @conn :eavt)))) (is (= db/implicit-schema (:schema @conn)))) - + (let [conn (d/conn-from-datoms datoms schema)] (is (= datoms (set (d/datoms @conn :eavt)))) (is (= schema (:schema @conn)))) - + (let [conn (d/conn-from-db (d/init-db datoms))] (is (= datoms (set (d/datoms @conn :eavt)))) (is (= db/implicit-schema (:schema @conn)))) - + (let [conn (d/conn-from-db (d/init-db datoms schema))] (is (= datoms (set (d/datoms @conn :eavt)))) (is (= schema (:schema @conn))))) @@ -43,12 +43,12 @@ _ (d/listen! conn #(reset! report %)) datoms' #{(d/datom 1 :age 20) (d/datom 1 :sex :male)} - schema' (merge db/implicit-schema { :email { :db/unique :db.unique/identity }}) + schema' (merge db/implicit-schema {:email {:db/unique :db.unique/identity}}) db' (d/init-db datoms' schema')] (d/reset-conn! conn db' :meta) (is (= datoms' (set (d/datoms @conn :eavt)))) (is (= schema' (:schema @conn))) - + (let [{:keys [db-before db-after tx-data tx-meta]} @report] (is (= datoms (set (d/datoms db-before :eavt)))) (is (= schema (:schema db-before))) @@ -60,5 +60,5 @@ [1 :age 20 true] [1 :sex :male true]] (map (juxt :e :a :v :added) tx-data)))))) - - + + diff --git a/test/datahike/test/core.cljc b/test/datahike/test/core.cljc index 07a6519b3..130de94a3 100644 --- a/test/datahike/test/core.cljc +++ b/test/datahike/test/core.cljc @@ -1,37 +1,37 @@ (ns datahike.test.core (:require - [#?(:cljs cljs.reader :clj clojure.edn) :as edn] - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [clojure.string :as str] - #?(:clj [kaocha.stacktrace]) - [datahike.core :as d] - [datahike.impl.entity :as de] - [datahike.db :as db #?@(:cljs [:refer-macros [defrecord-updatable]] - :clj [:refer [defrecord-updatable]])] - #?(:cljs [datahike.test.cljs]))) + [#?(:cljs cljs.reader :clj clojure.edn) :as edn] + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [clojure.string :as str] + #?(:clj [kaocha.stacktrace]) + [datahike.core :as d] + [datahike.impl.entity :as de] + [datahike.db :as db #?@(:cljs [:refer-macros [defrecord-updatable]] + :clj [:refer [defrecord-updatable]])] + #?(:cljs [datahike.test.cljs]))) #?(:cljs (enable-console-print!)) ;; Added special case for printing ex-data of ExceptionInfo #?(:cljs - (defmethod t/report [::t/default :error] [m] - (t/inc-report-counter! :error) - (println "\nERROR in" (t/testing-vars-str m)) - (when (seq (:testing-contexts (t/get-current-env))) - (println (t/testing-contexts-str))) - (when-let [message (:message m)] (println message)) - (println "expected:" (pr-str (:expected m))) - (print " actual: ") - (let [actual (:actual m)] - (cond - (instance? ExceptionInfo actual) - (println (.-stack actual) "\n" (pr-str (ex-data actual))) - (instance? js/Error actual) - (println (.-stack actual)) - :else - (prn actual))))) + (defmethod t/report [::t/default :error] [m] + (t/inc-report-counter! :error) + (println "\nERROR in" (t/testing-vars-str m)) + (when (seq (:testing-contexts (t/get-current-env))) + (println (t/testing-contexts-str))) + (when-let [message (:message m)] (println message)) + (println "expected:" (pr-str (:expected m))) + (print " actual: ") + (let [actual (:actual m)] + (cond + (instance? ExceptionInfo actual) + (println (.-stack actual) "\n" (pr-str (ex-data actual))) + (instance? js/Error actual) + (println (.-stack actual)) + :else + (prn actual))))) #?(:cljs (def test-summary (atom nil))) #?(:cljs (defmethod t/report [::t/default :end-run-tests] [m] @@ -45,16 +45,16 @@ ;; utils #?(:clj -(defmethod t/assert-expr 'thrown-msg? [msg form] - (let [[_ match & body] form] - `(try ~@body - (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) - (catch Throwable e# - (let [m# (.getMessage e#)] - (if (= ~match m#) - (t/do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) - (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual e#}))) - e#))))) + (defmethod t/assert-expr 'thrown-msg? [msg form] + (let [[_ match & body] form] + `(try ~@body + (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual nil}) + (catch Throwable e# + (let [m# (.getMessage e#)] + (if (= ~match m#) + (t/do-report {:type :pass, :message ~msg, :expected '~form, :actual e#}) + (t/do-report {:type :fail, :message ~msg, :expected '~form, :actual e#}))) + e#))))) (defn entity-map [db e] (when-let [entity (d/entity db e)] @@ -68,7 +68,7 @@ (defn no-namespace-maps [t] (binding [*print-namespace-maps* false] - (t))) + (t))) ;; Filter Kaocha frames from exceptions @@ -91,5 +91,4 @@ (d/datom 1 :name "Ivan") (d/datom 2 :age 37) (d/datom 2 :name "Petr") - (d/datom 2 :huh? false)})) - )) + (d/datom 2 :huh? false)})))) diff --git a/test/datahike/test/db.cljc b/test/datahike/test/db.cljc index 81f410034..ccfe5fddd 100644 --- a/test/datahike/test/db.cljc +++ b/test/datahike/test/db.cljc @@ -56,7 +56,6 @@ r1 (d/db-with db [[:db.fn/retractEntity 1]])] (is (= (hash (d/empty-db)) (hash r1))))) - (deftest empty-db-with-schema (testing "Test old write schema" (is (thrown-msg? diff --git a/test/datahike/test/entity.cljc b/test/datahike/test/entity.cljc index fd5241d04..65d5817b8 100644 --- a/test/datahike/test/entity.cljc +++ b/test/datahike/test/entity.cljc @@ -1,13 +1,13 @@ (ns datahike.test.entity (:require - [#?(:cljs cljs.reader :clj clojure.edn) :as edn] - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc]) - #?(:clj - (:import [clojure.lang ExceptionInfo]))) + [#?(:cljs cljs.reader :clj clojure.edn) :as edn] + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (t/use-fixtures :once tdc/no-namespace-maps) @@ -46,10 +46,10 @@ :children {:db/valueType :db.type/ref :db/cardinality :db.cardinality/many}}) (d/db-with - [{:db/id 1, :children [10]} - {:db/id 10, :father 1, :children [100 101]} - {:db/id 100, :father 10} - {:db/id 101, :father 10}])) + [{:db/id 1, :children [10]} + {:db/id 10, :father 1, :children [100 101]} + {:db/id 100, :father 10} + {:db/id 101, :father 10}])) e #(d/entity db %)] (is (= (:children (e 1)) #{(e 10)})) @@ -77,17 +77,16 @@ (is (= (:_father (e 1)) #{(e 10)})) (is (= (:_children (e 10)) #{(e 1)})) (is (= (:_father (e 10)) #{(e 100) (e 101)})) - (is (= (-> (e 100) :_children first :_children) #{(e 1)})) - ))) + (is (= (-> (e 100) :_children first :_children) #{(e 1)}))))) (deftest test-entity-misses (let [db (-> (d/empty-db {:name {:db/unique :db.unique/identity}}) - (d/db-with [{:db/id 1, :name "Ivan"} - {:db/id 2, :name "Oleg"}]))] + (d/db-with [{:db/id 1, :name "Ivan"} + {:db/id 2, :name "Oleg"}]))] (is (nil? (d/entity db nil))) (is (nil? (d/entity db "abc"))) (is (nil? (d/entity db :keyword))) (is (nil? (d/entity db [:name "Petr"]))) (is (= 777 (:db/id (d/entity db 777)))) (is (thrown-msg? "Lookup ref attribute should be marked as :db/unique: [:not-an-attr 777]" - (d/entity db [:not-an-attr 777]))))) + (d/entity db [:not-an-attr 777]))))) diff --git a/test/datahike/test/explode.cljc b/test/datahike/test/explode.cljc index 1974b8c69..c62aef667 100644 --- a/test/datahike/test/explode.cljc +++ b/test/datahike/test/explode.cljc @@ -1,10 +1,10 @@ (ns datahike.test.explode (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) #?(:cljs (def Throwable js/Error)) @@ -15,8 +15,8 @@ '("Devil" "Tupen") (to-array ["Devil" "Tupen"])]] (testing coll - (let [conn (d/create-conn { :aka { :db/cardinality :db.cardinality/many } - :also { :db/cardinality :db.cardinality/many} })] + (let [conn (d/create-conn {:aka {:db/cardinality :db.cardinality/many} + :also {:db/cardinality :db.cardinality/many}})] (d/transact! conn [{:db/id -1 :name "Ivan" :age 16 @@ -34,102 +34,100 @@ #{["Devil"] ["Tupen"]})))))) (deftest test-explode-ref - (let [db0 (d/empty-db { :children { :db/valueType :db.type/ref - :db/cardinality :db.cardinality/many } })] + (let [db0 (d/empty-db {:children {:db/valueType :db.type/ref + :db/cardinality :db.cardinality/many}})] (let [db (d/db-with db0 [{:db/id -1, :name "Ivan", :children [-2 -3]} - {:db/id -2, :name "Petr"} + {:db/id -2, :name "Petr"} {:db/id -3, :name "Evgeny"}])] (is (= (d/q '[:find ?n :where [_ :children ?e] - [?e :name ?n]] db) + [?e :name ?n]] db) #{["Petr"] ["Evgeny"]}))) - + (let [db (d/db-with db0 [{:db/id -1, :name "Ivan"} - {:db/id -2, :name "Petr", :_children -1} + {:db/id -2, :name "Petr", :_children -1} {:db/id -3, :name "Evgeny", :_children -1}])] (is (= (d/q '[:find ?n :where [_ :children ?e] - [?e :name ?n]] db) + [?e :name ?n]] db) #{["Petr"] ["Evgeny"]}))) - + (is (thrown-msg? "Bad attribute :_parent: reverse attribute name requires {:db/valueType :db.type/ref} in schema" - (d/db-with db0 [{:name "Sergey" :_parent 1}]))))) + (d/db-with db0 [{:name "Sergey" :_parent 1}]))))) (deftest test-explode-nested-maps - (let [schema { :profile { :db/valueType :db.type/ref }} + (let [schema {:profile {:db/valueType :db.type/ref}} db (d/empty-db schema)] (are [tx res] (= (d/q '[:find ?e ?a ?v :where [?e ?a ?v]] (d/db-with db tx)) res) - [ {:db/id 5 :name "Ivan" :profile {:db/id 7 :email "@2"}} ] - #{ [5 :name "Ivan"] [5 :profile 7] [7 :email "@2"] } - - [ {:name "Ivan" :profile {:email "@2"}} ] - #{ [1 :name "Ivan"] [1 :profile 2] [2 :email "@2"] } - - [ {:profile {:email "@2"}} ] ;; issue #59 - #{ [1 :profile 2] [2 :email "@2"] } - - [ {:email "@2" :_profile {:name "Ivan"}} ] - #{ [1 :email "@2"] [2 :name "Ivan"] [2 :profile 1] } - )) - + [{:db/id 5 :name "Ivan" :profile {:db/id 7 :email "@2"}}] + #{[5 :name "Ivan"] [5 :profile 7] [7 :email "@2"]} + + [{:name "Ivan" :profile {:email "@2"}}] + #{[1 :name "Ivan"] [1 :profile 2] [2 :email "@2"]} + + [{:profile {:email "@2"}}] ;; issue #59 + #{[1 :profile 2] [2 :email "@2"]} + + [{:email "@2" :_profile {:name "Ivan"}}] + #{[1 :email "@2"] [2 :name "Ivan"] [2 :profile 1]})) + (testing "multi-valued" - (let [schema { :profile { :db/valueType :db.type/ref - :db/cardinality :db.cardinality/many }} + (let [schema {:profile {:db/valueType :db.type/ref + :db/cardinality :db.cardinality/many}} db (d/empty-db schema)] (are [tx res] (= (d/q '[:find ?e ?a ?v :where [?e ?a ?v]] (d/db-with db tx)) res) - [ {:db/id 5 :name "Ivan" :profile {:db/id 7 :email "@2"}} ] - #{ [5 :name "Ivan"] [5 :profile 7] [7 :email "@2"] } - - [ {:db/id 5 :name "Ivan" :profile [{:db/id 7 :email "@2"} {:db/id 8 :email "@3"}]} ] - #{ [5 :name "Ivan"] [5 :profile 7] [7 :email "@2"] [5 :profile 8] [8 :email "@3"] } + [{:db/id 5 :name "Ivan" :profile {:db/id 7 :email "@2"}}] + #{[5 :name "Ivan"] [5 :profile 7] [7 :email "@2"]} - [ {:name "Ivan" :profile {:email "@2"}} ] - #{ [1 :name "Ivan"] [1 :profile 2] [2 :email "@2"] } + [{:db/id 5 :name "Ivan" :profile [{:db/id 7 :email "@2"} {:db/id 8 :email "@3"}]}] + #{[5 :name "Ivan"] [5 :profile 7] [7 :email "@2"] [5 :profile 8] [8 :email "@3"]} - [ {:name "Ivan" :profile [{:email "@2"} {:email "@3"}]} ] - #{ [1 :name "Ivan"] [1 :profile 2] [2 :email "@2"] [1 :profile 3] [3 :email "@3"] } - - [ {:email "@2" :_profile {:name "Ivan"}} ] - #{ [1 :email "@2"] [2 :name "Ivan"] [2 :profile 1] } + [{:name "Ivan" :profile {:email "@2"}}] + #{[1 :name "Ivan"] [1 :profile 2] [2 :email "@2"]} - [ {:email "@2" :_profile [{:name "Ivan"} {:name "Petr"} ]} ] - #{ [1 :email "@2"] [2 :name "Ivan"] [2 :profile 1] [3 :name "Petr"] [3 :profile 1] } - )))) + [{:name "Ivan" :profile [{:email "@2"} {:email "@3"}]}] + #{[1 :name "Ivan"] [1 :profile 2] [2 :email "@2"] [1 :profile 3] [3 :email "@3"]} + + [{:email "@2" :_profile {:name "Ivan"}}] + #{[1 :email "@2"] [2 :name "Ivan"] [2 :profile 1]} + + [{:email "@2" :_profile [{:name "Ivan"} {:name "Petr"}]}] + #{[1 :email "@2"] [2 :name "Ivan"] [2 :profile 1] [3 :name "Petr"] [3 :profile 1]})))) (deftest test-circular-refs (let [schema {:comp {:db/valueType :db.type/ref :db/cardinality :db.cardinality/many :db/isComponent true}} db (d/db-with (d/empty-db schema) - [{:db/id 1, :comp [{:name "C"}]}])] + [{:db/id 1, :comp [{:name "C"}]}])] (is (= (mapv (juxt :e :a :v) (d/datoms db :eavt)) - [ [ 1 :comp 2 ] - [ 2 :name "C"] ]))) - + [[1 :comp 2] + [2 :name "C"]]))) + (let [schema {:comp {:db/valueType :db.type/ref :db/cardinality :db.cardinality/many}} db (d/db-with (d/empty-db schema) - [{:db/id 1, :comp [{:name "C"}]}])] + [{:db/id 1, :comp [{:name "C"}]}])] (is (= (mapv (juxt :e :a :v) (d/datoms db :eavt)) - [ [ 1 :comp 2 ] - [ 2 :name "C"] ]))) - + [[1 :comp 2] + [2 :name "C"]]))) + (let [schema {:comp {:db/valueType :db.type/ref :db/isComponent true}} db (d/db-with (d/empty-db schema) - [{:db/id 1, :comp {:name "C"}}])] + [{:db/id 1, :comp {:name "C"}}])] (is (= (mapv (juxt :e :a :v) (d/datoms db :eavt)) - [ [ 1 :comp 2 ] - [ 2 :name "C"] ]))) - + [[1 :comp 2] + [2 :name "C"]]))) + (let [schema {:comp {:db/valueType :db.type/ref}} db (d/db-with (d/empty-db schema) - [{:db/id 1, :comp {:name "C"}}])] + [{:db/id 1, :comp {:name "C"}}])] (is (= (mapv (juxt :e :a :v) (d/datoms db :eavt)) - [ [ 1 :comp 2 ] - [ 2 :name "C"] ])))) - + [[1 :comp 2] + [2 :name "C"]])))) + diff --git a/test/datahike/test/filter.cljc b/test/datahike/test/filter.cljc index 642dcac08..5d8c61682 100644 --- a/test/datahike/test/filter.cljc +++ b/test/datahike/test/filter.cljc @@ -1,13 +1,13 @@ (ns datahike.test.filter (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (deftest test-filter-db - (let [empty-db (d/empty-db {:aka { :db/cardinality :db.cardinality/many }}) + (let [empty-db (d/empty-db {:aka {:db/cardinality :db.cardinality/many}}) db (-> empty-db (d/db-with [{:db/id 1 :name "Petr" @@ -21,8 +21,7 @@ {:db/id 3 :name "Nikolai" :aka ["II"] - :password ""} - ])) + :password ""}])) remove-pass (fn [_ datom] (not= :password (:a datom))) remove-ivan (fn [_ datom] (not= 2 (:e datom))) long-akas (fn [udb datom] (or (not= :aka (:a datom)) @@ -30,7 +29,7 @@ (<= (count (:aka (d/entity udb (:e datom)))) 1) ;; or aka longer that 4 chars (>= (count (:v datom)) 4)))] - + (are [_db _res] (= (d/q '[:find ?v :where [_ :password ?v]] _db) _res) db #{[""] [""] [""]} (d/filter db remove-pass) #{} @@ -44,31 +43,31 @@ (d/filter db long-akas) #{["Great"] ["Terrible"] ["II"]} (-> db (d/filter remove-ivan) (d/filter long-akas)) #{["Great"] ["II"]} (-> db (d/filter long-akas) (d/filter remove-ivan)) #{["Great"] ["II"]}) - + (testing "Entities" (is (= (:password (d/entity db 1)) "")) (is (= (:password (d/entity (d/filter db remove-pass) 1) ::not-found) ::not-found)) (is (= (:aka (d/entity db 2)) #{"Terrible" "IV"})) (is (= (:aka (d/entity (d/filter db long-akas) 2)) #{"Terrible"}))) - + (testing "Index access" (is (= (map :v (d/datoms db :aevt :password)) ["" "" ""])) (is (= (map :v (d/datoms (d/filter db remove-pass) :aevt :password)) []))) - + (testing "equiv" (is (= (d/db-with db [[:db.fn/retractEntity 2]]) (d/filter db remove-ivan))) (is (= empty-db (d/filter empty-db (constantly true)) (d/filter db (constantly false)))))) - + (testing "double filtering" (let [db (d/db-with (d/empty-db {}) - [{ :db/id 1, :name "Petr", :age 32} - { :db/id 2, :name "Oleg"} - { :db/id 3, :name "Ivan", :age 12}]) + [{:db/id 1, :name "Petr", :age 32} + {:db/id 2, :name "Oleg"} + {:db/id 3, :name "Ivan", :age 12}]) has-age? (fn [db datom] (some? (:age (d/entity db (:e datom))))) adult? (fn [db datom] (>= (:age (d/entity db (:e datom))) 18)) names (fn [db] (map :v (d/datoms db :aevt :name)))] diff --git a/test/datahike/test/ident.cljc b/test/datahike/test/ident.cljc index 3d661f010..efab2d8c7 100644 --- a/test/datahike/test/ident.cljc +++ b/test/datahike/test/ident.cljc @@ -1,7 +1,7 @@ (ns datahike.test.ident (:require - [clojure.test :as t :refer [is are deftest testing]] - [datahike.core :as d])) + [clojure.test :as t :refer [is are deftest testing]] + [datahike.core :as d])) (def db (-> (d/empty-db {:ref {:db/valueType :db.type/ref}}) @@ -15,21 +15,17 @@ (is (= 2 (d/q '[:find ?f . :where [?f :ref :ent1]] db)))) - (deftest test-transact! (let [db' (d/db-with db [[:db/add :ent1 :ref :ent2]])] (is (= 2 (-> (d/entity db' :ent1) :ref :db/id))))) - (deftest test-entity (is (= {:db/ident :ent1} (into {} (d/entity db :ent1))))) - (deftest test-pull (is (= {:db/id 1, :db/ident :ent1} (d/pull db '[*] :ent1)))) - #_(user/test-var #'test-transact!) #_(t/test-ns 'datahike.test.ident) diff --git a/test/datahike/test/index.cljc b/test/datahike/test/index.cljc index 616df0255..a30ea2c0d 100644 --- a/test/datahike/test/index.cljc +++ b/test/datahike/test/index.cljc @@ -1,59 +1,59 @@ (ns datahike.test.index (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (deftest test-datoms (let [dvec #(vector (:e %) (:a %) (:v %)) db (-> (d/empty-db {:age {:db/index true}}) - (d/db-with [ [:db/add 1 :name "Petr"] - [:db/add 1 :age 44] - [:db/add 2 :name "Ivan"] - [:db/add 2 :age 25] - [:db/add 3 :name "Sergey"] - [:db/add 3 :age 11] ]))] + (d/db-with [[:db/add 1 :name "Petr"] + [:db/add 1 :age 44] + [:db/add 2 :name "Ivan"] + [:db/add 2 :age 25] + [:db/add 3 :name "Sergey"] + [:db/add 3 :age 11]]))] (testing "Main indexes, sort order" - (is (= [ [1 :age 44] - [2 :age 25] - [3 :age 11] - [1 :name "Petr"] - [2 :name "Ivan"] - [3 :name "Sergey"] ] + (is (= [[1 :age 44] + [2 :age 25] + [3 :age 11] + [1 :name "Petr"] + [2 :name "Ivan"] + [3 :name "Sergey"]] (map dvec (d/datoms db :aevt)))) - (is (= [ [1 :age 44] - [1 :name "Petr"] - [2 :age 25] - [2 :name "Ivan"] - [3 :age 11] - [3 :name "Sergey"] ] + (is (= [[1 :age 44] + [1 :name "Petr"] + [2 :age 25] + [2 :name "Ivan"] + [3 :age 11] + [3 :name "Sergey"]] (map dvec (d/datoms db :eavt)))) - (is (= [ [3 :age 11] - [2 :age 25] - [1 :age 44] ] + (is (= [[3 :age 11] + [2 :age 25] + [1 :age 44]] (map dvec (d/datoms db :avet))))) ;; name non-indexed, excluded from avet (testing "Components filtration" - (is (= [ [1 :age 44] - [1 :name "Petr"] ] + (is (= [[1 :age 44] + [1 :name "Petr"]] (map dvec (d/datoms db :eavt 1)))) - (is (= [ [1 :age 44] ] + (is (= [[1 :age 44]] (map dvec (d/datoms db :eavt 1 :age)))) - (is (= [ [3 :age 11] - [2 :age 25] - [1 :age 44] ] + (is (= [[3 :age 11] + [2 :age 25] + [1 :age 44]] (map dvec (d/datoms db :avet :age))))))) (deftest test-seek-datoms (let [dvec #(vector (:e %) (:a %) (:v %)) - db (-> (d/empty-db { :name { :db/index true } - :age { :db/index true } }) + db (-> (d/empty-db {:name {:db/index true} + :age {:db/index true}}) (d/db-with [[:db/add 1 :name "Petr"] [:db/add 1 :age 44] [:db/add 2 :name "Ivan"] @@ -63,95 +63,95 @@ (testing "Non-termination" (is (= (map dvec (d/seek-datoms db :avet :age 10)) - [ [3 :age 11] - [2 :age 25] - [1 :age 44] - [2 :name "Ivan"] - [1 :name "Petr"] - [3 :name "Sergey"] ]))) + [[3 :age 11] + [2 :age 25] + [1 :age 44] + [2 :name "Ivan"] + [1 :name "Petr"] + [3 :name "Sergey"]]))) (testing "Closest value lookup" (is (= (map dvec (d/seek-datoms db :avet :name "P")) - [ [1 :name "Petr"] - [3 :name "Sergey"] ]))) + [[1 :name "Petr"] + [3 :name "Sergey"]]))) (testing "Exact value lookup" (is (= (map dvec (d/seek-datoms db :avet :name "Petr")) - [ [1 :name "Petr"] - [3 :name "Sergey"] ]))))) + [[1 :name "Petr"] + [3 :name "Sergey"]]))))) #_(deftest test-rseek-datoms ;; TODO: implement rseek within hitchhiker tree - (let [dvec #(vector (:e %) (:a %) (:v %)) - db (-> (d/empty-db { :name { :db/index true } - :age { :db/index true } }) - (d/db-with [[:db/add 1 :name "Petr"] - [:db/add 1 :age 44] - [:db/add 2 :name "Ivan"] - [:db/add 2 :age 25] - [:db/add 3 :name "Sergey"] - [:db/add 3 :age 11]]))] - - (testing "Non-termination" - (is (= (map dvec (d/rseek-datoms db :avet :name "Petr")) - [ [1 :name "Petr"] - [2 :name "Ivan"] - [1 :age 44] - [2 :age 25] - [3 :age 11]]))) - - (testing "Closest value lookup" - (is (= (map dvec (d/rseek-datoms db :avet :age 26)) - [ [2 :age 25] - [3 :age 11]]))) - - (testing "Exact value lookup" - (is (= (map dvec (d/rseek-datoms db :avet :age 25)) - [ [2 :age 25] - [3 :age 11]]))))) + (let [dvec #(vector (:e %) (:a %) (:v %)) + db (-> (d/empty-db {:name {:db/index true} + :age {:db/index true}}) + (d/db-with [[:db/add 1 :name "Petr"] + [:db/add 1 :age 44] + [:db/add 2 :name "Ivan"] + [:db/add 2 :age 25] + [:db/add 3 :name "Sergey"] + [:db/add 3 :age 11]]))] + + (testing "Non-termination" + (is (= (map dvec (d/rseek-datoms db :avet :name "Petr")) + [[1 :name "Petr"] + [2 :name "Ivan"] + [1 :age 44] + [2 :age 25] + [3 :age 11]]))) + + (testing "Closest value lookup" + (is (= (map dvec (d/rseek-datoms db :avet :age 26)) + [[2 :age 25] + [3 :age 11]]))) + + (testing "Exact value lookup" + (is (= (map dvec (d/rseek-datoms db :avet :age 25)) + [[2 :age 25] + [3 :age 11]]))))) (deftest test-index-range (let [dvec #(vector (:e %) (:a %) (:v %)) db (d/db-with - (d/empty-db { :name { :db/index true} - :age { :db/index true} }) - [ { :db/id 1 :name "Ivan" :age 15 } - { :db/id 2 :name "Oleg" :age 20 } - { :db/id 3 :name "Sergey" :age 7 } - { :db/id 4 :name "Pavel" :age 45 } - { :db/id 5 :name "Petr" :age 20 } ])] + (d/empty-db {:name {:db/index true} + :age {:db/index true}}) + [{:db/id 1 :name "Ivan" :age 15} + {:db/id 2 :name "Oleg" :age 20} + {:db/id 3 :name "Sergey" :age 7} + {:db/id 4 :name "Pavel" :age 45} + {:db/id 5 :name "Petr" :age 20}])] (is (= (map dvec (d/index-range db :name "Pe" "S")) - [ [5 :name "Petr"] ])) + [[5 :name "Petr"]])) (is (= (map dvec (d/index-range db :name "O" "Sergey")) - [ [2 :name "Oleg"] - [4 :name "Pavel"] - [5 :name "Petr"] - [3 :name "Sergey"] ])) + [[2 :name "Oleg"] + [4 :name "Pavel"] + [5 :name "Petr"] + [3 :name "Sergey"]])) (is (= (map dvec (d/index-range db :name nil "P")) - [ [1 :name "Ivan"] - [2 :name "Oleg"] ])) + [[1 :name "Ivan"] + [2 :name "Oleg"]])) (is (= (map dvec (d/index-range db :name "R" nil)) - [ [3 :name "Sergey"] ])) + [[3 :name "Sergey"]])) (is (= (map dvec (d/index-range db :name nil nil)) - [ [1 :name "Ivan"] - [2 :name "Oleg"] - [4 :name "Pavel"] - [5 :name "Petr"] - [3 :name "Sergey"] ])) + [[1 :name "Ivan"] + [2 :name "Oleg"] + [4 :name "Pavel"] + [5 :name "Petr"] + [3 :name "Sergey"]])) (is (= (map dvec (d/index-range db :age 15 20)) - [ [1 :age 15] - [2 :age 20] - [5 :age 20]])) + [[1 :age 15] + [2 :age 20] + [5 :age 20]])) (is (= (map dvec (d/index-range db :age 7 45)) - [ [3 :age 7] - [1 :age 15] - [2 :age 20] - [5 :age 20] - [4 :age 45] ])) + [[3 :age 7] + [1 :age 15] + [2 :age 20] + [5 :age 20] + [4 :age 45]])) (is (= (map dvec (d/index-range db :age 0 100)) - [ [3 :age 7] - [1 :age 15] - [2 :age 20] - [5 :age 20] - [4 :age 45] ])))) + [[3 :age 7] + [1 :age 15] + [2 :age 20] + [5 :age 20] + [4 :age 45]])))) diff --git a/test/datahike/test/listen.cljc b/test/datahike/test/listen.cljc index e3ef80ae9..d64231283 100644 --- a/test/datahike/test/listen.cljc +++ b/test/datahike/test/listen.cljc @@ -1,10 +1,10 @@ (ns datahike.test.listen (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.datom :as dd] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.datom :as dd] + [datahike.test.core :as tdc])) (deftest test-listen! (let [conn (d/create-conn) @@ -21,7 +21,7 @@ [:db/retract 4 :name "Evgeny"]]) (d/unlisten! conn :test) (d/transact! conn [[:db/add -1 :name "Geogry"]]) - + (is (= (:tx-data (first @reports)) [(dd/datom 3 :name "Dima" (+ d/tx0 2) true) (dd/datom 3 :age 19 (+ d/tx0 2) true) @@ -34,5 +34,4 @@ (dd/datom 1 :name "Alex2" (+ d/tx0 3) true) ;; + add (dd/datom 4 :name "Evgeny" (+ d/tx0 3) false)])) (is (= (:tx-meta (second @reports)) - nil)) - )) + nil)))) diff --git a/test/datahike/test/lookup_refs.cljc b/test/datahike/test/lookup_refs.cljc index ae8c9714d..ccdb0f6fa 100644 --- a/test/datahike/test/lookup_refs.cljc +++ b/test/datahike/test/lookup_refs.cljc @@ -1,102 +1,99 @@ (ns datahike.test.lookup-refs (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc]) - #?(:clj - (:import [clojure.lang ExceptionInfo]))) - - + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (deftest test-lookup-refs - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity } - :email { :db/unique :db.unique/value }}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity} + :email {:db/unique :db.unique/value}}) [{:db/id 1 :name "Ivan" :email "@1" :age 35} {:db/id 2 :name "Petr" :email "@2" :age 22}])] - + (are [eid res] (= (tdc/entity-map db eid) res) [:name "Ivan"] {:db/id 1 :name "Ivan" :email "@1" :age 35} [:email "@1"] {:db/id 1 :name "Ivan" :email "@1" :age 35} [:name "Sergey"] nil [:name nil] nil) - + (are [eid msg] (thrown-msg? msg (d/entity db eid)) [:name] "Lookup ref should contain 2 elements: [:name]" [:name 1 2] "Lookup ref should contain 2 elements: [:name 1 2]" [:age 10] "Lookup ref attribute should be marked as :db/unique: [:age 10]"))) (deftest test-lookup-refs-transact - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity } - :friend { :db/valueType :db.type/ref }}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity} + :friend {:db/valueType :db.type/ref}}) [{:db/id 1 :name "Ivan"} {:db/id 2 :name "Petr"}])] (are [tx res] (= res (tdc/entity-map (d/db-with db tx) 1)) ;; Additions [[:db/add [:name "Ivan"] :age 35]] {:db/id 1 :name "Ivan" :age 35} - + [{:db/id [:name "Ivan"] :age 35}] {:db/id 1 :name "Ivan" :age 35} - + [[:db/add 1 :friend [:name "Petr"]]] {:db/id 1 :name "Ivan" :friend {:db/id 2}} [[:db/add 1 :friend [:name "Petr"]]] {:db/id 1 :name "Ivan" :friend {:db/id 2}} - + [{:db/id 1 :friend [:name "Petr"]}] {:db/id 1 :name "Ivan" :friend {:db/id 2}} - + [{:db/id 2 :_friend [:name "Ivan"]}] {:db/id 1 :name "Ivan" :friend {:db/id 2}} - + ;; lookup refs are resolved at intermediate DB value [[:db/add 3 :name "Oleg"] [:db/add 1 :friend [:name "Oleg"]]] {:db/id 1 :name "Ivan" :friend {:db/id 3}} - + ;; CAS [[:db.fn/cas [:name "Ivan"] :name "Ivan" "Oleg"]] {:db/id 1 :name "Oleg"} - + [[:db/add 1 :friend 1] [:db.fn/cas 1 :friend [:name "Ivan"] 2]] {:db/id 1 :name "Ivan" :friend {:db/id 2}} - + [[:db/add 1 :friend 1] [:db.fn/cas 1 :friend 1 [:name "Petr"]]] {:db/id 1 :name "Ivan" :friend {:db/id 2}} - + ;; Retractions [[:db/add 1 :age 35] [:db/retract [:name "Ivan"] :age 35]] {:db/id 1 :name "Ivan"} - + [[:db/add 1 :friend 2] [:db/retract 1 :friend [:name "Petr"]]] {:db/id 1 :name "Ivan"} - + [[:db/add 1 :age 35] [:db.fn/retractAttribute [:name "Ivan"] :age]] {:db/id 1 :name "Ivan"} - + [[:db.fn/retractEntity [:name "Ivan"]]] {:db/id 1}) - + (are [tx msg] (thrown-msg? msg (d/db-with db tx)) [{:db/id [:name "Oleg"], :age 10}] "Nothing found for entity id [:name \"Oleg\"]" - + [[:db/add [:name "Oleg"] :age 10]] - "Nothing found for entity id [:name \"Oleg\"]") - )) + "Nothing found for entity id [:name \"Oleg\"]"))) (deftest test-lookup-refs-transact-multi - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity } - :friends { :db/valueType :db.type/ref - :db/cardinality :db.cardinality/many }}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity} + :friends {:db/valueType :db.type/ref + :db/cardinality :db.cardinality/many}}) [{:db/id 1 :name "Ivan"} {:db/id 2 :name "Petr"} {:db/id 3 :name "Oleg"} @@ -109,13 +106,13 @@ [[:db/add 1 :friends [:name "Petr"]] [:db/add 1 :friends [:name "Oleg"]]] {:db/id 1 :name "Ivan" :friends #{{:db/id 2} {:db/id 3}}} - + [{:db/id 1 :friends [:name "Petr"]}] {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}} [{:db/id 1 :friends [[:name "Petr"]]}] {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}} - + [{:db/id 1 :friends [[:name "Petr"] [:name "Oleg"]]}] {:db/id 1 :name "Ivan" :friends #{{:db/id 2} {:db/id 3}}} @@ -124,7 +121,7 @@ [{:db/id 1 :friends [[:name "Petr"] 3]}] {:db/id 1 :name "Ivan" :friends #{{:db/id 2} {:db/id 3}}} - + ;; reverse refs [{:db/id 2 :_friends [:name "Ivan"]}] {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}} @@ -133,104 +130,101 @@ {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}} [{:db/id 2 :_friends [[:name "Ivan"] [:name "Oleg"]]}] - {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}} - ))) + {:db/id 1 :name "Ivan" :friends #{{:db/id 2}}}))) (deftest lookup-refs-index-access - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity } - :friends { :db/valueType :db.type/ref - :db/cardinality :db.cardinality/many}}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity} + :friends {:db/valueType :db.type/ref + :db/cardinality :db.cardinality/many}}) [{:db/id 1 :name "Ivan" :friends [2 3]} {:db/id 2 :name "Petr" :friends 3} {:db/id 3 :name "Oleg"}])] - (are [index attrs datoms] (= (map (juxt :e :a :v) (apply d/datoms db index attrs)) datoms) - :eavt [[:name "Ivan"]] - [[1 :friends 2] [1 :friends 3] [1 :name "Ivan"]] - - :eavt [[:name "Ivan"] :friends] - [[1 :friends 2] [1 :friends 3]] - - :eavt [[:name "Ivan"] :friends [:name "Petr"]] - [[1 :friends 2]] - - :aevt [:friends [:name "Ivan"]] - [[1 :friends 2] [1 :friends 3]] - - :aevt [:friends [:name "Ivan"] [:name "Petr"]] - [[1 :friends 2]] - - :avet [:friends [:name "Oleg"]] - [[1 :friends 3] [2 :friends 3]] - - :avet [:friends [:name "Oleg"] [:name "Ivan"]] - [[1 :friends 3]]) - - (are [index attrs resolved-attrs] (= (vec (apply d/seek-datoms db index attrs)) - (vec (apply d/seek-datoms db index resolved-attrs))) - :eavt [[:name "Ivan"]] [1] - :eavt [[:name "Ivan"] :name] [1 :name] - :eavt [[:name "Ivan"] :friends [:name "Oleg"]] [1 :friends 3] - - :aevt [:friends [:name "Petr"]] [:friends 2] - :aevt [:friends [:name "Ivan"] [:name "Oleg"]] [:friends 1 3] - - :avet [:friends [:name "Oleg"]] [:friends 3] - :avet [:friends [:name "Oleg"] [:name "Petr"]] [:friends 3 2] - ) - + (are [index attrs datoms] (= (map (juxt :e :a :v) (apply d/datoms db index attrs)) datoms) + :eavt [[:name "Ivan"]] + [[1 :friends 2] [1 :friends 3] [1 :name "Ivan"]] + + :eavt [[:name "Ivan"] :friends] + [[1 :friends 2] [1 :friends 3]] + + :eavt [[:name "Ivan"] :friends [:name "Petr"]] + [[1 :friends 2]] + + :aevt [:friends [:name "Ivan"]] + [[1 :friends 2] [1 :friends 3]] + + :aevt [:friends [:name "Ivan"] [:name "Petr"]] + [[1 :friends 2]] + + :avet [:friends [:name "Oleg"]] + [[1 :friends 3] [2 :friends 3]] + + :avet [:friends [:name "Oleg"] [:name "Ivan"]] + [[1 :friends 3]]) + + (are [index attrs resolved-attrs] (= (vec (apply d/seek-datoms db index attrs)) + (vec (apply d/seek-datoms db index resolved-attrs))) + :eavt [[:name "Ivan"]] [1] + :eavt [[:name "Ivan"] :name] [1 :name] + :eavt [[:name "Ivan"] :friends [:name "Oleg"]] [1 :friends 3] + + :aevt [:friends [:name "Petr"]] [:friends 2] + :aevt [:friends [:name "Ivan"] [:name "Oleg"]] [:friends 1 3] + + :avet [:friends [:name "Oleg"]] [:friends 3] + :avet [:friends [:name "Oleg"] [:name "Petr"]] [:friends 3 2]) + (are [attr start end datoms] (= (map (juxt :e :a :v) (d/index-range db attr start end)) datoms) - :friends [:name "Oleg"] [:name "Oleg"] - [[1 :friends 3] [2 :friends 3]] - - :friends [:name "Petr"] [:name "Petr"] - [[1 :friends 2]] - - :friends [:name "Petr"] [:name "Oleg"] - [[1 :friends 2] [1 :friends 3] [2 :friends 3]]) -)) + :friends [:name "Oleg"] [:name "Oleg"] + [[1 :friends 3] [2 :friends 3]] + + :friends [:name "Petr"] [:name "Petr"] + [[1 :friends 2]] + + :friends [:name "Petr"] [:name "Oleg"] + [[1 :friends 2] [1 :friends 3] [2 :friends 3]]))) (deftest test-lookup-refs-query - (let [schema {:name { :db/unique :db.unique/identity } - :friend { :db/valueType :db.type/ref }} + (let [schema {:name {:db/unique :db.unique/identity} + :friend {:db/valueType :db.type/ref}} db (d/db-with (d/empty-db schema) - [{:db/id 1 :id 1 :name "Ivan" :age 11 :friend 2} - {:db/id 2 :id 2 :name "Petr" :age 22 :friend 3} - {:db/id 3 :id 3 :name "Oleg" :age 33 }])] + [{:db/id 1 :id 1 :name "Ivan" :age 11 :friend 2} + {:db/id 2 :id 2 :name "Petr" :age 22 :friend 3} + {:db/id 3 :id 3 :name "Oleg" :age 33}])] (is (= (set (d/q '[:find ?e ?v :in $ ?e :where [?e :age ?v]] db [:name "Ivan"])) #{[[:name "Ivan"] 11]})) - + (is (= (set (d/q '[:find [?v ...] :in $ [?e ...] :where [?e :age ?v]] db [[:name "Ivan"] [:name "Petr"]])) #{11 22})) - + (is (= (set (d/q '[:find [?e ...] :in $ ?v :where [?e :friend ?v]] db [:name "Petr"])) #{1})) - + (is (= (set (d/q '[:find [?e ...] :in $ [?v ...] :where [?e :friend ?v]] db [[:name "Petr"] [:name "Oleg"]])) #{1 2})) - + (is (= (d/q '[:find ?e ?v :in $ ?e ?v :where [?e :friend ?v]] db [:name "Ivan"] [:name "Petr"]) #{[[:name "Ivan"] [:name "Petr"]]})) - + (is (= (d/q '[:find ?e ?v :in $ [?e ...] [?v ...] :where [?e :friend ?v]] db [[:name "Ivan"] [:name "Petr"] [:name "Oleg"]] - [[:name "Ivan"] [:name "Petr"] [:name "Oleg"]]) + [[:name "Ivan"] [:name "Petr"] [:name "Oleg"]]) #{[[:name "Ivan"] [:name "Petr"]] [[:name "Petr"] [:name "Oleg"]]})) @@ -240,35 +234,32 @@ :where [?e :friend 3]] db [1 2 3 "A"]) #{[2]})) - + (let [db2 (d/db-with (d/empty-db schema) - [{:db/id 3 :name "Ivan" :id 3} - {:db/id 1 :name "Petr" :id 1} - {:db/id 2 :name "Oleg" :id 2}])] + [{:db/id 3 :name "Ivan" :id 3} + {:db/id 1 :name "Petr" :id 1} + {:db/id 2 :name "Oleg" :id 2}])] (is (= (d/q '[:find ?e ?e1 ?e2 :in $1 $2 [?e ...] :where [$1 ?e :id ?e1] - [$2 ?e :id ?e2]] + [$2 ?e :id ?e2]] db db2 [[:name "Ivan"] [:name "Petr"] [:name "Oleg"]]) #{[[:name "Ivan"] 1 3] [[:name "Petr"] 2 1] [[:name "Oleg"] 3 2]}))) - + (testing "inline refs" (is (= (d/q '[:find ?v :where [[:name "Ivan"] :friend ?v]] db) #{[2]})) - + (is (= (d/q '[:find ?e :where [?e :friend [:name "Petr"]]] db) #{[1]})) - - (is (thrown-msg? "Nothing found for entity id [:name \"Valery\"]" - (d/q '[:find ?e - :where [[:name "Valery"] :friend ?e]] - db))) - ) -)) \ No newline at end of file + (is (thrown-msg? "Nothing found for entity id [:name \"Valery\"]" + (d/q '[:find ?e + :where [[:name "Valery"] :friend ?e]] + db)))))) \ No newline at end of file diff --git a/test/datahike/test/lru.cljc b/test/datahike/test/lru.cljc index bf3976d95..7ccc1084b 100644 --- a/test/datahike/test/lru.cljc +++ b/test/datahike/test/lru.cljc @@ -1,8 +1,8 @@ (ns datahike.test.lru (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.lru :as lru])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.lru :as lru])) (deftest test-lru (let [l0 (lru/lru 2) @@ -24,4 +24,4 @@ l5 :b 2 ;; :b remains l5 :c nil ;; :c gets evicted as the oldest one l5 :d 5))) - + diff --git a/test/datahike/test/migrate.clj b/test/datahike/test/migrate.clj index aa2bf4133..793fa50d5 100644 --- a/test/datahike/test/migrate.clj +++ b/test/datahike/test/migrate.clj @@ -57,7 +57,6 @@ #{[3] [4]})) (d/delete-database cfg))))) - (deftest load-entities-test (testing "Test migrate simple datoms" (let [source-datoms (->> tx-data diff --git a/test/datahike/test/pull_api.cljc b/test/datahike/test/pull_api.cljc index c239a7c3c..bc71aa1d6 100644 --- a/test/datahike/test/pull_api.cljc +++ b/test/datahike/test/pull_api.cljc @@ -1,66 +1,65 @@ (ns datahike.test.pull-api (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.constants :refer [tx0]] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.constants :refer [tx0]] + [datahike.test.core :as tdc])) (def test-schema - {:aka { :db/cardinality :db.cardinality/many } - :child { :db/cardinality :db.cardinality/many - :db/valueType :db.type/ref } - :friend { :db/cardinality :db.cardinality/many - :db/valueType :db.type/ref } - :enemy { :db/cardinality :db.cardinality/many - :db/valueType :db.type/ref } - :father { :db/valueType :db.type/ref } - - :part { :db/valueType :db.type/ref - :db/isComponent true - :db/cardinality :db.cardinality/many } - :spec { :db/valueType :db.type/ref - :db/isComponent true - :db/cardinality :db.cardinality/one }}) - + {:aka {:db/cardinality :db.cardinality/many} + :child {:db/cardinality :db.cardinality/many + :db/valueType :db.type/ref} + :friend {:db/cardinality :db.cardinality/many + :db/valueType :db.type/ref} + :enemy {:db/cardinality :db.cardinality/many + :db/valueType :db.type/ref} + :father {:db/valueType :db.type/ref} + + :part {:db/valueType :db.type/ref + :db/isComponent true + :db/cardinality :db.cardinality/many} + :spec {:db/valueType :db.type/ref + :db/isComponent true + :db/cardinality :db.cardinality/one}}) (def test-datoms (->> - [[1 :name "Petr"] - [1 :aka "Devil"] - [1 :aka "Tupen"] - [2 :name "David"] - [3 :name "Thomas"] - [4 :name "Lucy"] - [5 :name "Elizabeth"] - [6 :name "Matthew"] - [7 :name "Eunan"] - [8 :name "Kerri"] - [9 :name "Rebecca"] - [1 :child 2] - [1 :child 3] - [2 :father 1] - [3 :father 1] - [6 :father 3] - [10 :name "Part A"] - [11 :name "Part A.A"] - [10 :part 11] - [12 :name "Part A.A.A"] - [11 :part 12] - [13 :name "Part A.A.A.A"] - [12 :part 13] - [14 :name "Part A.A.A.B"] - [12 :part 14] - [15 :name "Part A.B"] - [10 :part 15] - [16 :name "Part A.B.A"] - [15 :part 16] - [17 :name "Part A.B.A.A"] - [16 :part 17] - [18 :name "Part A.B.A.B"] - [16 :part 18]] - (map (fn [[e a v]] (d/datom e a v tx0))))) + [[1 :name "Petr"] + [1 :aka "Devil"] + [1 :aka "Tupen"] + [2 :name "David"] + [3 :name "Thomas"] + [4 :name "Lucy"] + [5 :name "Elizabeth"] + [6 :name "Matthew"] + [7 :name "Eunan"] + [8 :name "Kerri"] + [9 :name "Rebecca"] + [1 :child 2] + [1 :child 3] + [2 :father 1] + [3 :father 1] + [6 :father 3] + [10 :name "Part A"] + [11 :name "Part A.A"] + [10 :part 11] + [12 :name "Part A.A.A"] + [11 :part 12] + [13 :name "Part A.A.A.A"] + [12 :part 13] + [14 :name "Part A.A.A.B"] + [12 :part 14] + [15 :name "Part A.B"] + [10 :part 15] + [16 :name "Part A.B.A"] + [15 :part 16] + [17 :name "Part A.B.A.A"] + [16 :part 17] + [18 :name "Part A.B.A.B"] + [16 :part 18]] + (map (fn [[e a v]] (d/datom e a v tx0))))) (def test-db (d/init-db test-datoms test-schema)) @@ -126,7 +125,7 @@ (d/datom 10 :spec 13) (d/datom 13 :spec 12)]) test-schema)] - + (testing "Component entities are expanded recursively" (is (= parts (d/pull test-db '[:name :part] 10)))) @@ -150,15 +149,15 @@ (deftest test-pull-limit (let [db (d/init-db - (concat - test-datoms - [(d/datom 4 :friend 5) - (d/datom 4 :friend 6) - (d/datom 4 :friend 7) - (d/datom 4 :friend 8)] - (for [idx (range 2000)] - (d/datom 8 :aka (str "aka-" idx)))) - test-schema)] + (concat + test-datoms + [(d/datom 4 :friend 5) + (d/datom 4 :friend 6) + (d/datom 4 :friend 7) + (d/datom 4 :friend 8)] + (for [idx (range 2000)] + (d/datom 8 :aka (str "aka-" idx)))) + test-schema)] (testing "Without an explicit limit, the default is 1000" (is (= 1000 (->> (d/pull db '[:aka] 8) :aka count)))) @@ -283,8 +282,8 @@ (d/pull db '[:db/id :name {:friend ...}] 4))))))) (deftest test-dual-recursion - (let [empty (d/empty-db {:part { :db/valueType :db.type/ref } - :spec { :db/valueType :db.type/ref }})] + (let [empty (d/empty-db {:part {:db/valueType :db.type/ref} + :spec {:db/valueType :db.type/ref}})] (let [db (d/db-with empty [[:db/add 1 :part 2] [:db/add 2 :part 3] [:db/add 3 :part 1] @@ -315,9 +314,9 @@ (d/datom (dec idx) :friend idx)]) (range (inc start) depth)) db (d/init-db (concat - test-datoms - [(d/datom start :name (str "Person-" start))] - txd) + test-datoms + [(d/datom start :name (str "Person-" start))] + txd) test-schema) pulled (d/pull db '[:name {:friend ...}] start) path (->> [:friend 0] diff --git a/test/datahike/test/purge.cljc b/test/datahike/test/purge.cljc index 7e461ba97..80abc89bc 100644 --- a/test/datahike/test/purge.cljc +++ b/test/datahike/test/purge.cljc @@ -1,9 +1,9 @@ (ns datahike.test.purge (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing use-fixtures]]) - [datahike.test.core :as tdc] - [datahike.api :as d])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing use-fixtures]]) + [datahike.test.core :as tdc] + [datahike.api :as d])) (def schema-tx [{:db/ident :name :db/valueType :db.type/string @@ -39,20 +39,20 @@ (let [name "Alice"] (d/transact conn [[:db/retract [:name "Alice"] :age 25]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - 25 (find-age (d/history @conn) name)))) + true (nil? (find-age @conn name)) + 25 (find-age (d/history @conn) name)))) (testing "purge datom from current index and from history" (let [name "Bob"] (d/transact conn [[:db/purge [:name "Bob"] :age 35]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - true (nil? (find-age (d/history @conn) name))))) + true (nil? (find-age @conn name)) + true (nil? (find-age (d/history @conn) name))))) (testing "purge retracted datom" (let [name "Alice"] (d/transact conn [[:db/purge [:name name] :age 25]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - true (nil? (find-age (d/history @conn) name))))))) + true (nil? (find-age @conn name)) + true (nil? (find-age (d/history @conn) name))))))) (deftest test-purge-attribute (let [conn (create-test-db "datahike:mem://test-purge-attribute")] @@ -60,21 +60,21 @@ (let [name "Alice"] (d/transact conn [[:db.purge/attribute [:name "Alice"] :age]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - true (nil? (find-age (d/history @conn) name)) - #{["Alice"] ["Bob"]} (d/q '[:find ?n :where [_ :name ?n]] @conn)))) + true (nil? (find-age @conn name)) + true (nil? (find-age (d/history @conn) name)) + #{["Alice"] ["Bob"]} (d/q '[:find ?n :where [_ :name ?n]] @conn)))) (testing "retract attribute from current index and purge from history" (let [name "Bob"] (testing "retracting from current index" (d/transact conn [[:db.fn/retractAttribute [:name name] :age]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - 35 (find-age (d/history @conn) name))) + true (nil? (find-age @conn name)) + 35 (find-age (d/history @conn) name))) (testing "purging from history" (d/transact conn [[:db.purge/entity [:name name] :age]]) (are [x y] (= x y) - true (nil? (find-age @conn name)) - true (nil? (find-age (d/history @conn) name)))))))) + true (nil? (find-age @conn name)) + true (nil? (find-age (d/history @conn) name)))))))) (deftest test-purge-entity (let [conn (create-test-db "datahike:mem://test-purge-entity")] @@ -95,8 +95,8 @@ (is (= #{} (find-entities (d/history @conn))))))) (testing "purge something that is not present in the database" (is (thrown-msg? - "Can't find entity with ID [:name \"Alice\"] to be purged" - (d/transact conn [[:db.purge/entity [:name "Alice"]]])))))) + "Can't find entity with ID [:name \"Alice\"] to be purged" + (d/transact conn [[:db.purge/entity [:name "Alice"]]])))))) (deftest test-purge-non-temporal-database (let [uri "datahike:mem://purge-non-temporal" @@ -105,8 +105,8 @@ conn (d/connect uri)] (testing "purge data in non temporal database" (is (thrown-msg? - "Purge entity is only available in temporal databases." - (d/transact conn [[:db.purge/entity [:name "Alice"]]])))))) + "Purge entity is only available in temporal databases." + (d/transact conn [[:db.purge/entity [:name "Alice"]]])))))) (defn find-ages [db name] (d/q '[:find ?a ?op diff --git a/test/datahike/test/query.cljc b/test/datahike/test/query.cljc index 603db23f2..f9ee9041a 100644 --- a/test/datahike/test/query.cljc +++ b/test/datahike/test/query.cljc @@ -1,82 +1,79 @@ (ns datahike.test.query (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.query :as dq] - [datahike.db :as db] - [datahike.test.core :as tdc]) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.query :as dq] + [datahike.db :as db] + [datahike.test.core :as tdc]) #?(:clj (:import [clojure.lang ExceptionInfo]))) (deftest test-joins (let [db (-> (d/empty-db) - (d/db-with [ {:db/id 1, :name "Ivan", :age 15} - {:db/id 2, :name "Petr", :age 37} - {:db/id 3, :name "Ivan", :age 37} - {:db/id 4, :age 15}]))] + (d/db-with [{:db/id 1, :name "Ivan", :age 15} + {:db/id 2, :name "Petr", :age 37} + {:db/id 3, :name "Ivan", :age 37} + {:db/id 4, :age 15}]))] (is (= (d/q '[:find ?e :where [?e :name]] db) #{[1] [2] [3]})) (is (= (d/q '[:find ?e ?v :where [?e :name "Ivan"] - [?e :age ?v]] db) + [?e :age ?v]] db) #{[1 15] [3 37]})) (is (= (d/q '[:find ?e1 ?e2 :where [?e1 :name ?n] - [?e2 :name ?n]] db) + [?e2 :name ?n]] db) #{[1 1] [2 2] [3 3] [1 3] [3 1]})) (is (= (d/q '[:find ?e ?e2 ?n :where [?e :name "Ivan"] - [?e :age ?a] - [?e2 :age ?a] - [?e2 :name ?n]] db) + [?e :age ?a] + [?e2 :age ?a] + [?e2 :name ?n]] db) #{[1 1 "Ivan"] [3 3 "Ivan"] [3 2 "Petr"]})))) - (deftest test-q-many (let [db (-> (d/empty-db {:aka {:db/cardinality :db.cardinality/many}}) - (d/db-with [ [:db/add 1 :name "Ivan"] - [:db/add 1 :aka "ivolga"] - [:db/add 1 :aka "pi"] - [:db/add 2 :name "Petr"] - [:db/add 2 :aka "porosenok"] - [:db/add 2 :aka "pi"]]))] + (d/db-with [[:db/add 1 :name "Ivan"] + [:db/add 1 :aka "ivolga"] + [:db/add 1 :aka "pi"] + [:db/add 2 :name "Petr"] + [:db/add 2 :aka "porosenok"] + [:db/add 2 :aka "pi"]]))] (is (= (d/q '[:find ?n1 ?n2 :where [?e1 :aka ?x] - [?e2 :aka ?x] - [?e1 :name ?n1] - [?e2 :name ?n2]] db) + [?e2 :aka ?x] + [?e1 :name ?n1] + [?e2 :name ?n2]] db) #{["Ivan" "Ivan"] ["Petr" "Petr"] ["Ivan" "Petr"] ["Petr" "Ivan"]})))) - (deftest test-q-coll - (let [db [ [1 :name "Ivan"] - [1 :age 19] - [1 :aka "dragon_killer_94"] - [1 :aka "-=autobot=-"]]] - (is (= (d/q '[ :find ?n ?a - :where [?e :aka "dragon_killer_94"] - [?e :name ?n] - [?e :age ?a]] db) + (let [db [[1 :name "Ivan"] + [1 :age 19] + [1 :aka "dragon_killer_94"] + [1 :aka "-=autobot=-"]]] + (is (= (d/q '[:find ?n ?a + :where [?e :aka "dragon_killer_94"] + [?e :name ?n] + [?e :age ?a]] db) #{["Ivan" 19]}))) (testing "Query over long tuples" - (let [db [ [1 :name "Ivan" 945 :db/add] - [1 :age 39 999 :db/retract]]] - (is (= (d/q '[ :find ?e ?v - :where [?e :name ?v]] db) + (let [db [[1 :name "Ivan" 945 :db/add] + [1 :age 39 999 :db/retract]]] + (is (= (d/q '[:find ?e ?v + :where [?e :name ?v]] db) #{[1 "Ivan"]})) - (is (= (d/q '[ :find ?e ?a ?v ?t - :where [?e ?a ?v ?t :db/retract]] db) + (is (= (d/q '[:find ?e ?a ?v ?t + :where [?e ?a ?v ?t :db/retract]] db) #{[1 :age 39 999]}))))) - (deftest test-q-in (let [db (-> (d/empty-db) (d/db-with [{:db/id 1, :name "Ivan", :age 15} @@ -101,7 +98,7 @@ (is (= (d/q '[:find ?e ?email :in $ $b :where [?e :name ?n] - [$b ?n ?email]] + [$b ?n ?email]] db [["Ivan" "ivan@mail.ru"] ["Petr" "petr@gmail.com"]]) @@ -117,9 +114,9 @@ (deftest test-bindings (let [db (-> (d/empty-db) - (d/db-with [{:db/id 1, :name "Ivan", :age 15} - {:db/id 2, :name "Petr", :age 37} - {:db/id 3, :name "Ivan", :age 37}]))] + (d/db-with [{:db/id 1, :name "Ivan", :age 15} + {:db/id 2, :name "Petr", :age 37} + {:db/id 3, :name "Ivan", :age 37}]))] (testing "Relation binding" (is (= (d/q '[:find ?e ?email :in $ [[?n ?email]] @@ -135,7 +132,7 @@ (is (= (d/q '[:find ?e :in $ [?name ?age] :where [?e :name ?name] - [?e :age ?age]] + [?e :age ?age]] db ["Ivan" 37]) #{[3]}))) @@ -150,16 +147,16 @@ (is (= (d/q '[:find ?id :in $ [?id ...] :where [?id :age _]] - [[1 :name "Ivan"] - [2 :name "Petr"]] - []) + [[1 :name "Ivan"] + [2 :name "Petr"]] + []) #{})) (is (= (d/q '[:find ?id :in $ [[?id]] :where [?id :age _]] - [[1 :name "Ivan"] - [2 :name "Petr"]] - []) + [[1 :name "Ivan"] + [2 :name "Petr"]] + []) #{}))) (testing "Placeholders" @@ -174,13 +171,11 @@ (testing "Error reporting" (is (thrown-with-msg? ExceptionInfo #"Cannot bind value :a to tuple \[\?a \?b\]" - (d/q '[:find ?a ?b :in [?a ?b]] :a))) + (d/q '[:find ?a ?b :in [?a ?b]] :a))) (is (thrown-with-msg? ExceptionInfo #"Cannot bind value :a to collection \[\?a \.\.\.\]" - (d/q '[:find ?a :in [?a ...]] :a))) + (d/q '[:find ?a :in [?a ...]] :a))) (is (thrown-with-msg? ExceptionInfo #"Not enough elements in a collection \[:a\] to bind tuple \[\?a \?b\]" - (d/q '[:find ?a ?b :in [?a ?b]] [:a])))))) - - + (d/q '[:find ?a ?b :in [?a ?b]] [:a])))))) (deftest test-nested-bindings (is (= (d/q '[:find ?k ?v @@ -192,7 +187,7 @@ (is (= (d/q '[:find ?k ?min ?max :in [[?k ?v] ...] ?minmax :where [(?minmax ?v) [?min ?max]] - [(> ?max ?min)]] + [(> ?max ?min)]] {:a [1 2 3 4] :b [5 6 7] :c [3]} @@ -202,7 +197,7 @@ (is (= (d/q '[:find ?k ?x :in [[?k [?min ?max]] ...] ?range :where [(?range ?min ?max) [?x ...]] - [(even? ?x)]] + [(even? ?x)]] {:a [1 7] :b [2 4]} range) @@ -244,16 +239,16 @@ :offset 1 :limit 2})))) (is (= (d/q {:query '[:find ?e :where [?e :name _]] - :args [db] + :args [db] :offset 4}) #{})) (is (= (d/q {:query '[:find ?e :where [?e :name _]] - :args [db] + :args [db] :offset 10 :limit 5}) #{})) (is (= (d/q {:query '[:find ?e :where [?e :name _]] - :args [db] + :args [db] :offset 1 :limit 0}) #{})))) @@ -263,29 +258,29 @@ (d/db-with [{:db/id 1, :name "Alice", :age 15} {:db/id 2, :name "Bob", :age 37} {:db/id 3, :name "Charlie", :age 37}]))] - (testing "returns map" - (is (map? (first (d/q {:query '[:find ?e :keys name :where [?e :name _]] - :args [db]}))))) - (testing "returns set without return-map" - (is (= #{["Charlie"] ["Alice"] ["Bob"]} - (d/q {:query '[:find ?name :where [_ :name ?name]] - :args [db]})))) - (testing "returns map with key return-map" - (is (= [{:foo 3} {:foo 2} {:foo 1}] - (d/q {:query '[:find ?e :keys foo :where [?e :name _]] - :args [db]})))) - (testing "returns map with string return-map" - (is (= [{"foo" "Charlie"} {"foo" "Alice"} {"foo" "Bob"}] - (d/q {:query '[:find ?name :strs foo :where [?e :name ?name]] - :args [db]})))) - (testing "return map with keys using multiple find vars" - (is (= #{["Bob" {:age 37 :db/id 2}] - ["Charlie" {:age 37 :db/id 3}] - ["Alice" {:age 15 :db/id 1}]} - (into #{} (d/q {:find '[?name (pull ?e ?p)] - :args [db '[:age :db/id]] - :in '[$ ?p] - :where '[[?e :name ?name]]}))))))) + (testing "returns map" + (is (map? (first (d/q {:query '[:find ?e :keys name :where [?e :name _]] + :args [db]}))))) + (testing "returns set without return-map" + (is (= #{["Charlie"] ["Alice"] ["Bob"]} + (d/q {:query '[:find ?name :where [_ :name ?name]] + :args [db]})))) + (testing "returns map with key return-map" + (is (= [{:foo 3} {:foo 2} {:foo 1}] + (d/q {:query '[:find ?e :keys foo :where [?e :name _]] + :args [db]})))) + (testing "returns map with string return-map" + (is (= [{"foo" "Charlie"} {"foo" "Alice"} {"foo" "Bob"}] + (d/q {:query '[:find ?name :strs foo :where [?e :name ?name]] + :args [db]})))) + (testing "return map with keys using multiple find vars" + (is (= #{["Bob" {:age 37 :db/id 2}] + ["Charlie" {:age 37 :db/id 3}] + ["Alice" {:age 15 :db/id 1}]} + (into #{} (d/q {:find '[?name (pull ?e ?p)] + :args [db '[:age :db/id]] + :in '[$ ?p] + :where '[[?e :name ?name]]}))))))) (deftest test-memoized-parse-query (testing "no map return" diff --git a/test/datahike/test/query_aggregates.cljc b/test/datahike/test/query_aggregates.cljc index 51895832a..edf89da33 100644 --- a/test/datahike/test/query_aggregates.cljc +++ b/test/datahike/test/query_aggregates.cljc @@ -1,43 +1,41 @@ (ns datahike.test.query-aggregates (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) - + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (defn sort-reverse [xs] (reverse (sort xs))) - (deftest test-aggregates - (let [monsters [ ["Cerberus" 3] - ["Medusa" 1] - ["Cyclops" 1] - ["Chimera" 1] ]] + (let [monsters [["Cerberus" 3] + ["Medusa" 1] + ["Cyclops" 1] + ["Chimera" 1]]] (testing "with" - (is (= (d/q '[ :find ?heads - :with ?monster - :in [[?monster ?heads]] ] - [ ["Medusa" 1] - ["Cyclops" 1] - ["Chimera" 1] ]) + (is (= (d/q '[:find ?heads + :with ?monster + :in [[?monster ?heads]]] + [["Medusa" 1] + ["Cyclops" 1] + ["Chimera" 1]]) [[1] [1] [1]]))) (testing "Wrong grouping without :with" - (is (= (d/q '[ :find (sum ?heads) - :in [[?monster ?heads]] ] + (is (= (d/q '[:find (sum ?heads) + :in [[?monster ?heads]]] monsters) [[4]]))) (testing "Multiple aggregates, correct grouping with :with" - (is (= (d/q '[ :find (sum ?heads) (min ?heads) (max ?heads) (count ?heads) (count-distinct ?heads) - :with ?monster - :in [[?monster ?heads]] ] + (is (= (d/q '[:find (sum ?heads) (min ?heads) (max ?heads) (count ?heads) (count-distinct ?heads) + :with ?monster + :in [[?monster ?heads]]] monsters) [[6 1 3 4 2]]))) - + (testing "Min and max are using comparator instead of default compare" ;; Wrong: using js '<' operator ;; (apply min [:a/b :a-/b :a/c]) => :a-/b @@ -55,52 +53,51 @@ [[[:a/b :a/c] [:a/c :a-/b]]]))) (testing "Grouping and parameter passing" - (is (= (set (d/q '[ :find ?color (max ?amount ?x) (min ?amount ?x) - :in [[?color ?x]] ?amount ] + (is (= (set (d/q '[:find ?color (max ?amount ?x) (min ?amount ?x) + :in [[?color ?x]] ?amount] [[:red 1] [:red 2] [:red 3] [:red 4] [:red 5] [:blue 7] [:blue 8]] 3)) #{[:red [3 4 5] [1 2 3]] [:blue [7 8] [7 8]]}))) - (testing "avg aggregate" - (is (= (ffirst (d/q '[:find (avg ?x) + (testing "avg aggregate" + (is (= (ffirst (d/q '[:find (avg ?x) :in [?x ...]] - [10 15 20 35 75])) + [10 15 20 35 75])) 31))) (testing "median aggregate" (is (= (ffirst (d/q '[:find (median ?x) :in [?x ...]] - [10 15 20 35 75])) + [10 15 20 35 75])) 20))) - + (testing "variance aggregate" (is (= (ffirst (d/q '[:find (variance ?x) :in [?x ...]] - [10 15 20 35 75])) - 554))) + [10 15 20 35 75])) + 554))) (testing "stddev aggregate" - (is (= (ffirst (d/q '[:find (stddev ?x) + (is (= (ffirst (d/q '[:find (stddev ?x) :in [?x ...]] [10 15 20 35 75])) - 23.53720459187964))) + 23.53720459187964))) (testing "Custom aggregates" (let [data [[:red 1] [:red 2] [:red 3] [:red 4] [:red 5] [:blue 7] [:blue 8]] result #{[:red [5 4 3 2 1]] [:blue [8 7]]}] - - (is (= (set (d/q '[ :find ?color (aggregate ?agg ?x) - :in [[?color ?x]] ?agg ] + + (is (= (set (d/q '[:find ?color (aggregate ?agg ?x) + :in [[?color ?x]] ?agg] data sort-reverse)) result)) - + #?(:clj - (is (= (set (d/q '[ :find ?color (datahike.test.query-aggregates/sort-reverse ?x) - :in [[?color ?x]]] - data)) - result))) - )))) + (is (= (set (d/q '[:find ?color (datahike.test.query-aggregates/sort-reverse ?x) + :in [[?color ?x]]] + data)) + result))))))) diff --git a/test/datahike/test/query_find_specs.cljc b/test/datahike/test/query_find_specs.cljc index 06413c807..ff2602157 100644 --- a/test/datahike/test/query_find_specs.cljc +++ b/test/datahike/test/query_find_specs.cljc @@ -1,27 +1,27 @@ (ns datahike.test.query-find-specs (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (def test-db (d/db-with - (d/empty-db) - [[:db/add 1 :name "Petr"] - [:db/add 1 :age 44] - [:db/add 2 :name "Ivan"] - [:db/add 2 :age 25] - [:db/add 3 :name "Sergey"] - [:db/add 3 :age 11]])) + (d/empty-db) + [[:db/add 1 :name "Petr"] + [:db/add 1 :age 44] + [:db/add 2 :name "Ivan"] + [:db/add 2 :age 25] + [:db/add 3 :name "Sergey"] + [:db/add 3 :age 11]])) (deftest test-find-specs (is (= (set (d/q '[:find [?name ...] - :where [_ :name ?name]] test-db)) + :where [_ :name ?name]] test-db)) #{"Ivan" "Petr" "Sergey"})) (is (= (d/q '[:find [?name ?age] :where [1 :name ?name] - [1 :age ?age]] test-db) + [1 :age ?age]] test-db) ["Petr" 44])) (is (= (d/q '[:find ?name . :where [1 :name ?name]] test-db) @@ -29,14 +29,14 @@ (testing "Multiple results get cut" (is (contains? - #{["Petr" 44] ["Ivan" 25] ["Sergey" 11]} - (d/q '[:find [?name ?age] - :where [?e :name ?name] - [?e :age ?age]] test-db))) + #{["Petr" 44] ["Ivan" 25] ["Sergey" 11]} + (d/q '[:find [?name ?age] + :where [?e :name ?name] + [?e :age ?age]] test-db))) (is (contains? - #{"Ivan" "Petr" "Sergey"} - (d/q '[:find ?name . - :where [_ :name ?name]] test-db)))) + #{"Ivan" "Petr" "Sergey"} + (d/q '[:find ?name . + :where [_ :name ?name]] test-db)))) (testing "Aggregates work with find specs" (is (= (d/q '[:find [(count ?name) ...] @@ -47,7 +47,6 @@ [3])) (is (= (d/q '[:find (count ?name) . :where [_ :name ?name]] test-db) - 3))) -) + 3)))) #_(t/test-ns 'datahike.test.query-find-specs) diff --git a/test/datahike/test/query_fns.cljc b/test/datahike/test/query_fns.cljc index 7be744953..83264ed5c 100644 --- a/test/datahike/test/query_fns.cljc +++ b/test/datahike/test/query_fns.cljc @@ -1,12 +1,12 @@ (ns datahike.test.query-fns (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc]) -#?(:clj - (:import [clojure.lang ExceptionInfo]))) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (deftest test-query-fns (testing "predicate without free variables" @@ -16,9 +16,9 @@ #{[:a] [:b] [:c]}))) (let [db (-> (d/empty-db {:parent {:db/valueType :db.type/ref}}) - (d/db-with [ { :db/id 1, :name "Ivan", :age 15 } - { :db/id 2, :name "Petr", :age 22, :height 240, :parent 1} - { :db/id 3, :name "Slava", :age 37, :parent 2}]))] + (d/db-with [{:db/id 1, :name "Ivan", :age 15} + {:db/id 2, :name "Petr", :age 22, :height 240, :parent 1} + {:db/id 3, :name "Slava", :age 37, :parent 2}]))] (testing "ground" (is (= (d/q '[:find ?vowel @@ -28,18 +28,18 @@ (testing "get-else" (is (= (d/q '[:find ?e ?age ?height :where [?e :age ?age] - [(get-else $ ?e :height 300) ?height]] db) + [(get-else $ ?e :height 300) ?height]] db) #{[1 15 300] [2 22 240] [3 37 300]})) - + (is (thrown-with-msg? ExceptionInfo #"get-else: nil default value is not supported" - (d/q '[:find ?e ?height - :where [?e :age] - [(get-else $ ?e :height nil) ?height]] db)))) + (d/q '[:find ?e ?height + :where [?e :age] + [(get-else $ ?e :height nil) ?height]] db)))) (testing "get-some" (is (= (d/q '[:find ?e ?a ?v :where [?e :name _] - [(get-some $ ?e :height :age) [?a ?v]]] db) + [(get-some $ ?e :height :age) [?a ?v]]] db) #{[1 :age 15] [2 :height 240] [3 :age 37]}))) @@ -48,7 +48,7 @@ (is (= (d/q '[:find ?e ?age :in $ :where [?e :age ?age] - [(missing? $ ?e :height)]] db) + [(missing? $ ?e :height)]] db) #{[1 15] [3 37]}))) (testing "missing? back-ref" @@ -61,14 +61,14 @@ (testing "Built-ins" (is (= (d/q '[:find ?e1 ?e2 :where [?e1 :age ?a1] - [?e2 :age ?a2] - [(< ?a1 18 ?a2)]] db) + [?e2 :age ?a2] + [(< ?a1 18 ?a2)]] db) #{[1 2] [1 3]})) - + (is (= (d/q '[:find ?x ?c :in [?x ...] :where [(count ?x) ?c]] - ["a" "abc"]) + ["a" "abc"]) #{["a" 1] ["abc" 3]}))) (testing "Built-in vector, hashmap" @@ -83,12 +83,11 @@ [(hash-map :db/id -1 :age 92 :name "Aaron") ?tx-data]]) [{:db/id -1 :age 92 :name "Aaron"}]))) - (testing "Passing predicate as source" (is (= (d/q '[:find ?e :in $ ?adult :where [?e :age ?a] - [(?adult ?a)]] + [(?adult ?a)]] db #(> % 18)) #{[2] [3]}))) @@ -96,24 +95,24 @@ (testing "Calling a function" (is (= (d/q '[:find ?e1 ?e2 ?e3 :where [?e1 :age ?a1] - [?e2 :age ?a2] - [?e3 :age ?a3] - [(+ ?a1 ?a2) ?a12] - [(= ?a12 ?a3)]] + [?e2 :age ?a2] + [?e3 :age ?a3] + [(+ ?a1 ?a2) ?a12] + [(= ?a12 ?a3)]] db) #{[1 2 3] [2 1 3]}))) (testing "Two conflicting function values for one binding." (is (= (d/q '[:find ?n :where [(identity 1) ?n] - [(identity 2) ?n]] + [(identity 2) ?n]] db) #{}))) (testing "Destructured conflicting function values for two bindings." (is (= (d/q '[:find ?n ?x :where [(identity [3 4]) [?n ?x]] - [(identity [1 2]) [?n ?x]]] + [(identity [1 2]) [?n ?x]]] db) #{}))) @@ -148,31 +147,31 @@ (testing "Conflicting relational bindings with function binding. (rel, fn)" (is (= (d/q '[:find ?age :where [_ :age ?age] - [(identity 100) ?age]] + [(identity 100) ?age]] db) #{}))) (testing "Conflicting relational bindings with function binding. (fn, rel)" (is (= (d/q '[:find ?age :where [(identity 100) ?age] - [_ :age ?age]] + [_ :age ?age]] db) #{}))) (testing "Function on empty rel" (is (= (d/q '[:find ?e ?y :where [?e :salary ?x] - [(+ ?x 100) ?y]] + [(+ ?x 100) ?y]] [[0 :age 15] [1 :age 35]]) #{}))) - + (testing "Returning nil from function filters out tuple from result" (is (= (d/q '[:find ?x :in [?in ...] ?f :where [(?f ?in) ?x]] - [1 2 3 4] - #(when (even? %) %)) - #{[2] [4]}))) + [1 2 3 4] + #(when (even? %) %)) + #{[2] [4]}))) (testing "Result bindings" (is (= (d/q '[:find ?a ?c @@ -189,17 +188,15 @@ (is (= (d/q '[:find ?x ?z :in ?in - :where [(ground ?in) [[?x _ ?z]...]]] + :where [(ground ?in) [[?x _ ?z] ...]]] [[:a :b :c] [:d :e :f]]) #{[:a :c] [:d :f]})) - + (is (= (d/q '[:find ?in :in [?in ...] :where [(ground ?in) _]] []) - #{}))) -)) - + #{}))))) (deftest test-predicates (let [entities [{:db/id 1 :name "Ivan" :age 10} @@ -211,77 +208,76 @@ ;; plain predicate [:find ?e ?a :where [?e :age ?a] - [(> ?a 10)]] + [(> ?a 10)]] #{[2 20] [4 20]} ;; join in predicate [:find ?e ?e2 :where [?e :name] - [?e2 :name] - [(< ?e ?e2)]] + [?e2 :name] + [(< ?e ?e2)]] #{[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]} - + ;; join with extra symbols [:find ?e ?e2 :where [?e :age ?a] - [?e2 :age ?a2] - [(< ?e ?e2)]] + [?e2 :age ?a2] + [(< ?e ?e2)]] #{[1 2] [1 3] [1 4] [2 3] [2 4] [3 4]} ;; empty result [:find ?e ?e2 :where [?e :name "Ivan"] - [?e2 :name "Oleg"] - [(= ?e ?e2)]] + [?e2 :name "Oleg"] + [(= ?e ?e2)]] #{} ;; pred over const, true [:find ?e :where [?e :name "Ivan"] - [?e :age 20] - [(= ?e 2)]] + [?e :age 20] + [(= ?e 2)]] #{[2]} ;; pred over const, false [:find ?e :where [?e :name "Ivan"] - [?e :age 20] - [(= ?e 1)]] + [?e :age 20] + [(= ?e 1)]] #{}) (let [pred (fn [db e a] (= a (:age (d/entity db e))))] (is (= (d/q '[:find ?e :in $ ?pred :where [?e :age ?a] - [(?pred $ ?e 10)]] + [(?pred $ ?e 10)]] db pred) #{[1] [3]}))))) - (deftest test-exceptions (is (thrown-with-msg? ExceptionInfo #"Unknown predicate 'fun in \[\(fun \?e\)\]" - (d/q '[:find ?e - :in [?e ...] - :where [(fun ?e)]] - [1]))) - + (d/q '[:find ?e + :in [?e ...] + :where [(fun ?e)]] + [1]))) + (is (thrown-with-msg? ExceptionInfo #"Unknown function 'fun in \[\(fun \?e\) \?x\]" - (d/q '[:find ?e ?x - :in [?e ...] - :where [(fun ?e) ?x]] - [1])))) + (d/q '[:find ?e ?x + :in [?e ...] + :where [(fun ?e) ?x]] + [1])))) (deftest test-issue-180 (is (= #{} (d/q '[:find ?e ?a :where [_ :pred ?pred] - [?e :age ?a] - [(?pred ?a)]] + [?e :age ?a] + [(?pred ?a)]] (d/db-with (d/empty-db) [[:db/add 1 :age 20]]))))) (defn sample-query-fn [] 42) #?(:clj -(deftest test-symbol-resolution - (is (= 42 (d/q '[:find ?x . - :where [(datahike.test.query-fns/sample-query-fn) ?x]]))))) + (deftest test-symbol-resolution + (is (= 42 (d/q '[:find ?x . + :where [(datahike.test.query-fns/sample-query-fn) ?x]]))))) diff --git a/test/datahike/test/query_interop.cljc b/test/datahike/test/query_interop.cljc index 77ef5e15a..4d9c65b8b 100644 --- a/test/datahike/test/query_interop.cljc +++ b/test/datahike/test/query_interop.cljc @@ -15,17 +15,17 @@ (are [q expected] (= (d/q q test-db) expected) '[:find ?v :where [_ :name ?v] - [(.startsWith ?v "Ser")]] + [(.startsWith ?v "Ser")]] #{["Sergey"]} '[:find ?v :where [_ :name ?v] - [(.contains ?v "a")]] + [(.contains ?v "a")]] #{["Vlad"] ["Ivan"]} '[:find ?v :where [_ :name ?v] - [(.matches ?v ".+rg.+")]] + [(.matches ?v ".+rg.+")]] #{["Sergey"]})) (deftest test-bind diff --git a/test/datahike/test/query_not.cljc b/test/datahike/test/query_not.cljc index 5392618ed..e47ad9041 100644 --- a/test/datahike/test/query_not.cljc +++ b/test/datahike/test/query_not.cljc @@ -1,24 +1,22 @@ (ns datahike.test.query-not (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc]) - #?(:clj - (:import [clojure.lang ExceptionInfo]))) - + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (def test-db (delay - (d/db-with (d/empty-db) - [ {:db/id 1 :name "Ivan" :age 10} - {:db/id 2 :name "Ivan" :age 20} - {:db/id 3 :name "Oleg" :age 10} - {:db/id 4 :name "Oleg" :age 20} - {:db/id 5 :name "Ivan" :age 10} - {:db/id 6 :name "Ivan" :age 20} ]))) - + (d/db-with (d/empty-db) + [{:db/id 1 :name "Ivan" :age 10} + {:db/id 2 :name "Ivan" :age 20} + {:db/id 3 :name "Oleg" :age 10} + {:db/id 4 :name "Oleg" :age 20} + {:db/id 5 :name "Ivan" :age 10} + {:db/id 6 :name "Ivan" :age 20}]))) (deftest test-not (are [q res] (= (set (d/q (concat '[:find [?e ...] :where] (quote q)) @test-db)) @@ -26,34 +24,34 @@ [[?e :name] (not [?e :name "Ivan"])] #{3 4} - + [[?e :name] (not - [?e :name "Ivan"] - [?e :age 10])] + [?e :name "Ivan"] + [?e :age 10])] #{2 3 4 6} - + [[?e :name] (not [?e :name "Ivan"]) (not [?e :age 10])] #{4} - + ;; full exclude [[?e :name] (not [?e :age])] #{} - + ;; not-intersecting rels [[?e :name "Ivan"] (not [?e :name "Oleg"])] #{1 2 5 6} - + ;; exclude empty set [[?e :name] (not [?e :name "Ivan"] [?e :name "Oleg"])] #{1 2 3 4 5 6} - + ;; nested excludes [[?e :name] (not [?e :name "Ivan"] @@ -64,9 +62,7 @@ [[?e :name ?a] (not [?e :age ?f] [?e :age 10])] - #{2 4 6} -)) - + #{2 4 6})) (deftest test-not-join (are [q res] (= (d/q (concat '[:find ?e ?a :where] (quote q)) @test-db) @@ -74,26 +70,25 @@ [[?e :name] [?e :age ?a] (not-join [?e] - [?e :name "Oleg"] - [?e :age ?a])] + [?e :name "Oleg"] + [?e :age ?a])] #{[1 10] [2 20] [5 10] [6 20]} - + [[?e :age ?a] [?e :age 10] (not-join [?e] - [?e :name "Oleg"] - [?e :age ?a] - [?e :age 10])] + [?e :name "Oleg"] + [?e :age ?a] + [?e :age 10])] #{[1 10] [5 10]})) - (deftest test-default-source (let [db1 (d/db-with (d/empty-db) - [ [:db/add 1 :name "Ivan" ] - [:db/add 2 :name "Oleg"] ]) + [[:db/add 1 :name "Ivan"] + [:db/add 2 :name "Oleg"]]) db2 (d/db-with (d/empty-db) - [ [:db/add 1 :age 10 ] - [:db/add 2 :age 20] ])] + [[:db/add 1 :age 10] + [:db/add 2 :age 20]])] (are [q res] (= (set (d/q (concat '[:find [?e ...] :in $ $2 :where] @@ -104,22 +99,22 @@ [[?e :name] (not [?e :name "Ivan"])] #{2} - + ;; NOT can reference any source [[?e :name] (not [$2 ?e :age 10])] #{2} - + ;; NOT can change default source [[?e :name] ($2 not [?e :age 10])] #{2} - + ;; even with another default source, it can reference any other source explicitly [[?e :name] ($2 not [$ ?e :name "Ivan"])] #{2} - + ;; nested NOT keeps the default source [[?e :name] ($2 not (not [?e :age 10]))] @@ -130,70 +125,66 @@ ($2 not ($ not [?e :name "Ivan"]))] #{1}))) - (deftest test-impl-edge-cases (are [q res] (= (d/q (quote q) @test-db) res) ;; const \ empty [:find ?e :where [?e :name "Oleg"] - [?e :age 10] - (not [?e :age 20])] + [?e :age 10] + (not [?e :age 20])] #{[3]} - + ;; const \ const [:find ?e :where [?e :name "Oleg"] - [?e :age 10] - (not [?e :age 10])] + [?e :age 10] + (not [?e :age 10])] #{} - + ;; rel \ const [:find ?e :where [?e :name "Oleg"] - (not [?e :age 10])] + (not [?e :age 10])] #{[4]} ;; 2 rels \ 2 rels [:find ?e ?e2 :where [?e :name "Ivan"] - [?e2 :name "Ivan"] - (not [?e :age 10] - [?e2 :age 20])] + [?e2 :name "Ivan"] + (not [?e :age 10] + [?e2 :age 20])] #{[2 1] [6 5] [1 1] [2 2] [5 5] [6 6] [2 5] [1 5] [2 6] [6 1] [5 1] [6 2]} ;; 2 rels \ rel + const [:find ?e ?e2 :where [?e :name "Ivan"] - [?e2 :name "Oleg"] - (not [?e :age 10] - [?e2 :age 20])] + [?e2 :name "Oleg"] + (not [?e :age 10] + [?e2 :age 20])] #{[2 3] [1 3] [2 4] [6 3] [5 3] [6 4]} ;; 2 rels \ 2 consts [:find ?e ?e2 - :where [?e :name "Oleg"] - [?e2 :name "Oleg"] - (not [?e :age 10] - [?e2 :age 20])] - #{[4 3] [3 3] [4 4]} -)) - + :where [?e :name "Oleg"] + [?e2 :name "Oleg"] + (not [?e :age 10] + [?e2 :age 20])] + #{[4 3] [3 3] [4 4]})) (deftest test-insufficient-bindings (are [q msg] (thrown-msg? msg - (d/q (concat '[:find ?e :where] (quote q)) @test-db)) + (d/q (concat '[:find ?e :where] (quote q)) @test-db)) [(not [?e :name "Ivan"]) [?e :name]] "Insufficient bindings: none of #{?e} is bound in (not [?e :name \"Ivan\"])" - + [[?e :name] (not-join [?e] - (not [1 :age ?a]) - [?e :age ?a])] + (not [1 :age ?a]) + [?e :age ?a])] "Insufficient bindings: none of #{?a} is bound in (not [1 :age ?a])" - + [[?e :name] (not [?a :name "Ivan"])] - "Insufficient bindings: none of #{?a} is bound in (not [?a :name \"Ivan\"])" -)) + "Insufficient bindings: none of #{?a} is bound in (not [?a :name \"Ivan\"])")) diff --git a/test/datahike/test/query_or.cljc b/test/datahike/test/query_or.cljc index 15c1f100b..0dc2cec0d 100644 --- a/test/datahike/test/query_or.cljc +++ b/test/datahike/test/query_or.cljc @@ -1,22 +1,22 @@ (ns datahike.test.query-or (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc]) - #?(:clj - (:import [clojure.lang ExceptionInfo]))) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (def test-db (delay - (d/db-with (d/empty-db) - [ {:db/id 1 :name "Ivan" :age 10} - {:db/id 2 :name "Ivan" :age 20} - {:db/id 3 :name "Oleg" :age 10} - {:db/id 4 :name "Oleg" :age 20} - {:db/id 5 :name "Ivan" :age 10} - {:db/id 6 :name "Ivan" :age 20} ]))) + (d/db-with (d/empty-db) + [{:db/id 1 :name "Ivan" :age 10} + {:db/id 2 :name "Ivan" :age 20} + {:db/id 3 :name "Oleg" :age 10} + {:db/id 4 :name "Oleg" :age 20} + {:db/id 5 :name "Ivan" :age 10} + {:db/id 6 :name "Ivan" :age 20}]))) (deftest test-or (are [q res] (= (d/q (concat '[:find ?e :where] (quote q)) @test-db) @@ -26,23 +26,23 @@ [(or [?e :name "Oleg"] [?e :age 10])] #{1 3 4 5} - + ;; one branch empty [(or [?e :name "Oleg"] [?e :age 30])] #{3 4} - + ;; both empty [(or [?e :name "Petr"] [?e :age 30])] #{} - + ;; join with 1 var [[?e :name "Ivan"] (or [?e :name "Oleg"] [?e :age 10])] #{1 5} - + ;; join with 2 vars [[?e :age ?a] (or (and [?e :name "Ivan"] @@ -71,16 +71,16 @@ (are [q res] (= (d/q (concat '[:find ?e :where] (quote q)) @test-db) (into #{} (map vector) res)) [(or-join [?e] - [?e :name ?n] - (and [?e :age ?a] - [?e :name ?n]))] + [?e :name ?n] + (and [?e :age ?a] + [?e :name ?n]))] #{1 2 3 4 5 6} - + [[?e :name ?a] [?e2 :name ?a] (or-join [?e] - (and [?e :age ?a] - [?e2 :age ?a]))] + (and [?e :age ?a] + [?e2 :age ?a]))] #{1 2 3 4 5 6}) (is (= #{[:a1 :b1 :c1] @@ -88,53 +88,53 @@ (d/q '[:find ?a ?b ?c :in $xs $ys :where [$xs ?a ?b ?c] ;; check join by ?a, ignoring ?b, dropping ?c ?d - (or-join [?a] + (or-join [?a] [$ys ?a ?b ?d])] - [[:a1 :b1 :c1] - [:a2 :b2 :c2] - [:a3 :b3 :c3]] - [[:a1 :b1 :d1] ;; same ?a, same ?b - [:a2 :b2* :d2] ;; same ?a, different ?b. Should still be joined - [:a4 :b4 :c4]]))) ;; different ?a, should be dropped + [[:a1 :b1 :c1] + [:a2 :b2 :c2] + [:a3 :b3 :c3]] + [[:a1 :b1 :d1] ;; same ?a, same ?b + [:a2 :b2* :d2] ;; same ?a, different ?b. Should still be joined + [:a4 :b4 :c4]]))) ;; different ?a, should be dropped (is (= #{[:a1 :c1] [:a2 :c2]} - (d/q '[:find ?a ?c - :in $xs $ys - :where (or-join [?a ?c] - [$xs ?a ?b ?c] ; rel with hole (?b gets dropped, leaving {?a 0 ?c 2} and 3-element tuples) - [$ys ?a ?c])] - [[:a1 :b1 :c1]] - [[:a2 :c2]])))) + (d/q '[:find ?a ?c + :in $xs $ys + :where (or-join [?a ?c] + [$xs ?a ?b ?c] ; rel with hole (?b gets dropped, leaving {?a 0 ?c 2} and 3-element tuples) + [$ys ?a ?c])] + [[:a1 :b1 :c1]] + [[:a2 :c2]])))) (deftest test-default-source (let [db1 (d/db-with (d/empty-db) - [ [:db/add 1 :name "Ivan" ] - [:db/add 2 :name "Oleg"] ]) + [[:db/add 1 :name "Ivan"] + [:db/add 2 :name "Oleg"]]) db2 (d/db-with (d/empty-db) - [ [:db/add 1 :age 10 ] - [:db/add 2 :age 20] ])] + [[:db/add 1 :age 10] + [:db/add 2 :age 20]])] (are [q res] (= (d/q (concat '[:find ?e :in $ $2 :where] (quote q)) db1 db2) (into #{} (map vector) res)) ;; OR inherits default source [[?e :name] (or [?e :name "Ivan"])] #{1} - + ;; OR can reference any source [[?e :name] (or [$2 ?e :age 10])] #{1} - + ;; OR can change default source [[?e :name] ($2 or [?e :age 10])] #{1} - + ;; even with another default source, it can reference any other source explicitly [[?e :name] ($2 or [$ ?e :name "Ivan"])] #{1} - + ;; nested OR keeps the default source [[?e :name] ($2 or (or [?e :age 10]))] @@ -145,16 +145,15 @@ ($2 or ($ or [?e :name "Ivan"]))] #{1}))) - (deftest test-errors (is (thrown-msg? "Join variable not declared inside clauses: [?a]" - (d/q '[:find ?e - :where (or [?e :name _] - [?e :age ?a])] - @test-db))) + (d/q '[:find ?e + :where (or [?e :name _] + [?e :age ?a])] + @test-db))) (is (thrown-msg? "Insufficient bindings: #{?e} not bound in (or-join [[?e]] [?e :name \"Ivan\"])" - (d/q '[:find ?e - :where (or-join [[?e]] - [?e :name "Ivan"])] - @test-db)))) \ No newline at end of file + (d/q '[:find ?e + :where (or-join [[?e]] + [?e :name "Ivan"])] + @test-db)))) \ No newline at end of file diff --git a/test/datahike/test/query_pull.cljc b/test/datahike/test/query_pull.cljc index acbb787b0..7a2017aa5 100644 --- a/test/datahike/test/query_pull.cljc +++ b/test/datahike/test/query_pull.cljc @@ -1,15 +1,15 @@ (ns datahike.test.query-pull (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (def test-db (d/db-with (d/empty-db) - [{:db/id 1 :name "Petr" :age 44} - {:db/id 2 :name "Ivan" :age 25} - {:db/id 3 :name "Oleg" :age 11}])) + [{:db/id 1 :name "Petr" :age 44} + {:db/id 2 :name "Ivan" :age 25} + {:db/id 3 :name "Oleg" :age 11}])) (deftest test-basics (are [find res] (= (set (d/q {:find find @@ -41,21 +41,21 @@ res) '[(pull ?e ?pattern)] [:name] #{[{:name "Ivan"}] [{:name "Petr"}]} - + '[?e ?a ?pattern (pull ?e ?pattern)] [:name] #{[2 25 [:name] {:name "Ivan"}] [1 44 [:name] {:name "Petr"}]})) ;; not supported #_(deftest test-multi-pattern - (is (= (set (d/q '[:find ?e ?p (pull ?e ?p) - :in $ [?p ...] - :where [?e :age ?a] - [>= ?a 18]] - test-db [[:name] [:age]])) - #{[2 [:name] {:name "Ivan"}] - [2 [:age] {:age 25}] - [1 [:name] {:name "Petr"}] - [1 [:age] {:age 44}]}))) + (is (= (set (d/q '[:find ?e ?p (pull ?e ?p) + :in $ [?p ...] + :where [?e :age ?a] + [>= ?a 18]] + test-db [[:name] [:age]])) + #{[2 [:name] {:name "Ivan"}] + [2 [:age] {:age 25}] + [1 [:name] {:name "Petr"}] + [1 [:age] {:age 44}]}))) (deftest test-multiple-sources (let [db1 (d/db-with (d/empty-db) [{:db/id 1 :name "Ivan" :age 25}]) @@ -65,13 +65,13 @@ :where [$1 ?e :age 25]] db1 db2)) #{[1 {:name "Ivan"}]})) - + (is (= (set (d/q '[:find ?e (pull $2 ?e [:name]) :in $1 $2 :where [$2 ?e :age 25]] db1 db2)) #{[1 {:name "Petr"}]})) - + (testing "$ is default source" (is (= (set (d/q '[:find ?e (pull ?e [:name]) :in $1 $ @@ -82,36 +82,36 @@ (deftest test-find-spec (is (= (d/q '[:find (pull ?e [:name]) . :where [?e :age 25]] - test-db) + test-db) {:name "Ivan"})) - + (is (= (set (d/q '[:find [(pull ?e [:name]) ...] :where [?e :age ?a]] - test-db)) + test-db)) #{{:name "Ivan"} {:name "Petr"} {:name "Oleg"}})) (is (= (d/q '[:find [?e (pull ?e [:name])] :where [?e :age 25]] - test-db) + test-db) [2 {:name "Ivan"}]))) (deftest test-find-spec-input (is (= (d/q '[:find (pull ?e ?p) . :in $ ?p :where [(ground 2) ?e]] - test-db [:name]) + test-db [:name]) {:name "Ivan"})) (is (= (d/q '[:find (pull ?e p) . :in $ p :where [(ground 2) ?e]] - test-db [:name]) + test-db [:name]) {:name "Ivan"}))) (deftest test-aggregates (let [db (d/db-with (d/empty-db {:value {:db/cardinality :db.cardinality/many}}) - [{:db/id 1 :name "Petr" :value [10 20 30 40]} - {:db/id 2 :name "Ivan" :value [14 16]} - {:db/id 3 :name "Oleg" :value 1}])] + [{:db/id 1 :name "Petr" :value [10 20 30 40]} + {:db/id 2 :name "Ivan" :value [14 16]} + {:db/id 3 :name "Oleg" :value 1}])] (is (= (set (d/q '[:find ?e (pull ?e [:name]) (min ?v) (max ?v) :where [?e :value ?v]] db)) @@ -120,14 +120,14 @@ [3 {:name "Oleg"} 1 1]})))) (deftest test-lookup-refs - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity }}) - [{:db/id 1 :name "Petr" :age 44} - {:db/id 2 :name "Ivan" :age 25} - {:db/id 3 :name "Oleg" :age 11}])] + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity}}) + [{:db/id 1 :name "Petr" :age 44} + {:db/id 2 :name "Ivan" :age 25} + {:db/id 3 :name "Oleg" :age 11}])] (is (= (set (d/q '[:find ?ref ?a (pull ?ref [:db/id :name]) :in $ [?ref ...] :where [?ref :age ?a] - [(>= ?a 18)]] + [(>= ?a 18)]] db [[:name "Ivan"] [:name "Oleg"] [:name "Petr"]])) #{[[:name "Petr"] 44 {:db/id 1 :name "Petr"}] [[:name "Ivan"] 25 {:db/id 2 :name "Ivan"}]})))) diff --git a/test/datahike/test/query_rules.cljc b/test/datahike/test/query_rules.cljc index b438089ce..735badbf8 100644 --- a/test/datahike/test/query_rules.cljc +++ b/test/datahike/test/query_rules.cljc @@ -1,39 +1,39 @@ (ns datahike.test.query-rules (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (deftest test-rules - (let [db [ [5 :follow 3] + (let [db [[5 :follow 3] [1 :follow 2] [2 :follow 3] [3 :follow 4] [4 :follow 6] - [2 :follow 4]]] + [2 :follow 4]]] (is (= (d/q '[:find ?e1 ?e2 :in $ % :where (follow ?e1 ?e2)] db - '[[(follow ?x ?y) - [?x :follow ?y]]]) + '[[(follow ?x ?y) + [?x :follow ?y]]]) #{[1 2] [2 3] [3 4] [2 4] [5 3] [4 6]})) - + (testing "Joining regular clauses with rule" (is (= (d/q '[:find ?y ?x :in $ % :where [_ _ ?x] - (rule ?x ?y) - [(even? ?x)]] + (rule ?x ?y) + [(even? ?x)]] db '[[(rule ?a ?b) [?a :follow ?b]]]) #{[3 2] [6 4] [4 2]}))) - + (testing "Rule context is isolated from outer context" (is (= (d/q '[:find ?x :in $ % :where [?e _ _] - (rule ?x)] + (rule ?x)] db '[[(rule ?e) [_ ?e _]]]) @@ -45,11 +45,11 @@ :where (follow ?e1 ?e2)] db 1 - '[[(follow ?e2 ?e1) - [?e2 :follow ?e1]] - [(follow ?e2 ?e1) - [?e2 :follow ?t] - [?t :follow ?e1]]]) + '[[(follow ?e2 ?e1) + [?e2 :follow ?e1]] + [(follow ?e2 ?e1) + [?e2 :follow ?t] + [?t :follow ?e1]]]) #{[2] [3] [4]}))) (testing "Recursive rules" @@ -58,32 +58,32 @@ :where (follow ?e1 ?e2)] db 1 - '[[(follow ?e1 ?e2) - [?e1 :follow ?e2]] - [(follow ?e1 ?e2) - [?e1 :follow ?t] - (follow ?t ?e2)]]) + '[[(follow ?e1 ?e2) + [?e1 :follow ?e2]] + [(follow ?e1 ?e2) + [?e1 :follow ?t] + (follow ?t ?e2)]]) #{[2] [3] [4] [6]})) (is (= (d/q '[:find ?e1 ?e2 - :in $ % - :where (follow ?e1 ?e2)] - [[1 :follow 2] [2 :follow 3]] - '[[(follow ?e1 ?e2) - [?e1 :follow ?e2]] - [(follow ?e1 ?e2) - (follow ?e2 ?e1)]]) - #{[1 2] [2 3] [2 1] [3 2]})) + :in $ % + :where (follow ?e1 ?e2)] + [[1 :follow 2] [2 :follow 3]] + '[[(follow ?e1 ?e2) + [?e1 :follow ?e2]] + [(follow ?e1 ?e2) + (follow ?e2 ?e1)]]) + #{[1 2] [2 3] [2 1] [3 2]})) (is (= (d/q '[:find ?e1 ?e2 - :in $ % - :where (follow ?e1 ?e2)] - [[1 :follow 2] [2 :follow 3] [3 :follow 1]] - '[[(follow ?e1 ?e2) - [?e1 :follow ?e2]] - [(follow ?e1 ?e2) - (follow ?e2 ?e1)]]) - #{[1 2] [2 3] [3 1] [2 1] [3 2] [1 3]}))) + :in $ % + :where (follow ?e1 ?e2)] + [[1 :follow 2] [2 :follow 3] [3 :follow 1]] + '[[(follow ?e1 ?e2) + [?e1 :follow ?e2]] + [(follow ?e1 ?e2) + (follow ?e2 ?e1)]]) + #{[1 2] [2 3] [3 1] [2 1] [3 2] [1 3]}))) (testing "Mutually recursive rules" (is (= (d/q '[:find ?e1 ?e2 @@ -95,21 +95,21 @@ [3 :f2 4] [4 :f1 5] [5 :f2 6]] - '[[(f1 ?e1 ?e2) - [?e1 :f1 ?e2]] - [(f1 ?e1 ?e2) - [?t :f1 ?e2] - (f2 ?e1 ?t)] - [(f2 ?e1 ?e2) - [?e1 :f2 ?e2]] - [(f2 ?e1 ?e2) - [?t :f2 ?e2] - (f1 ?e1 ?t)]]) - #{[0 1] [0 3] [0 5] - [1 3] [1 5] - [2 3] [2 5] - [3 5] - [4 5]}))) + '[[(f1 ?e1 ?e2) + [?e1 :f1 ?e2]] + [(f1 ?e1 ?e2) + [?t :f1 ?e2] + (f2 ?e1 ?t)] + [(f2 ?e1 ?e2) + [?e1 :f2 ?e2]] + [(f2 ?e1 ?e2) + [?t :f2 ?e2] + (f1 ?e1 ?t)]]) + #{[0 1] [0 3] [0 5] + [1 3] [1 5] + [2 3] [2 5] + [3 5] + [4 5]}))) (testing "Passing ins to rule" (is (= (d/q '[:find ?x ?y @@ -123,7 +123,7 @@ [(?pred ?e2)]]] even?) #{[4 6] [2 4]}))) - + (testing "Using built-ins inside rule" (is (= (d/q '[:find ?x ?y :in $ % @@ -138,35 +138,34 @@ (d/q '[:find ?p :in $ % ?fn :where (rule ?p ?fn "a") - (rule ?p ?fn "b")] + (rule ?p ?fn "b")] [[1 :attr "a"]] - '[[(rule ?p ?fn ?x) - [?p :attr ?x] - [(?fn ?x)]]] - (constantly true))) - ) - + '[[(rule ?p ?fn ?x) + [?p :attr ?x] + [(?fn ?x)]]] + (constantly true)))) (testing "Specifying db to rule" (is (= (d/q '[:find ?n :in $sexes $ages % :where ($sexes male ?n) - ($ages adult ?n) ] - [["Ivan" :male] ["Darya" :female] ["Oleg" :male] ["Igor" :male]] - [["Ivan" 15] ["Oleg" 66] ["Darya" 32]] - '[[(male ?x) - [?x :male]] - [(adult ?y) - [?y ?a] - [(>= ?a 18)]]]) - #{["Oleg"]}))) - ) + ($ages adult ?n)] + [["Ivan" :male] ["Darya" :female] ["Oleg" :male] ["Igor" :male]] + [["Ivan" 15] ["Oleg" 66] ["Darya" 32]] + '[[(male ?x) + [?x :male]] + [(adult ?y) + [?y ?a] + [(>= ?a 18)]]]) + #{["Oleg"]})))) ;; https://github.com/tonsky/datahike/issues/218 + + (deftest test-false-arguments - (let [db (d/db-with (d/empty-db) - [[:db/add 1 :attr true] - [:db/add 2 :attr false]]) + (let [db (d/db-with (d/empty-db) + [[:db/add 1 :attr true] + [:db/add 2 :attr false]]) rules '[[(is ?id ?val) [?id :attr ?val]]]] (is (= (d/q '[:find ?id :in $ % diff --git a/test/datahike/test/query_v3.cljc b/test/datahike/test/query_v3.cljc index 85040782d..ddf18372e 100644 --- a/test/datahike/test/query_v3.cljc +++ b/test/datahike/test/query_v3.cljc @@ -1,15 +1,13 @@ (ns datahike.test.query-v3 (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.query-v3 :as dq] - [datahike.test.core :as tdc]) - #?(:clj - (:import [clojure.lang ExceptionInfo]))) - - + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.query-v3 :as dq] + [datahike.test.core :as tdc]) + #?(:clj + (:import [clojure.lang ExceptionInfo]))) (deftest test-validation (are [q ins msg] (thrown-with-msg? ExceptionInfo msg (apply dq/q q ins)) @@ -18,8 +16,8 @@ '[:find ?a :where [?a]] [0 1] #"Wrong number of arguments for bindings \[\$\], 1 required, 2 provided" '[:find ?a :where [?a 1]] [:a] #"Cannot match by pattern \[\?a 1\] because source is not a collection: :a")) - + #_(deftest test-query - (is (= (dq/q '[:find ?a :where [?a ?a]] - [[1 2] [3 3] [4 5] [6 6]]) - #{[3] [6]}))) + (is (= (dq/q '[:find ?a :where [?a ?a]] + [[1 2] [3 3] [4 5] [6 6]]) + #{[3] [6]}))) diff --git a/test/datahike/test/schema.cljc b/test/datahike/test/schema.cljc index 3ed86a149..ab1aec114 100644 --- a/test/datahike/test/schema.cljc +++ b/test/datahike/test/schema.cljc @@ -264,7 +264,6 @@ (d/transact conn [{:db/id [:db/ident :owner] :db/cardinality :db.cardinality/many}])))))) - (deftest test-schema-persistence (testing "test file persistence" (let [os (System/getProperty "os.name") diff --git a/test/datahike/test/store.cljc b/test/datahike/test/store.cljc index b2d1aa799..e96729abf 100644 --- a/test/datahike/test/store.cljc +++ b/test/datahike/test/store.cljc @@ -28,7 +28,6 @@ (deftest test-db-mem-store (test-store {:store {:backend :mem :id "test-mem"}})) - (deftest test-persistent-set-index (let [config {:store {:backend :mem :id "test-persistent-set"} diff --git a/test/datahike/test/time_variance.cljc b/test/datahike/test/time_variance.cljc index fc82a9dcb..b4ed10a32 100644 --- a/test/datahike/test/time_variance.cljc +++ b/test/datahike/test/time_variance.cljc @@ -1,9 +1,9 @@ (ns datahike.test.time-variance (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing use-fixtures]]) - [datahike.constants :as const] - [datahike.api :as d]) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing use-fixtures]]) + [datahike.constants :as const] + [datahike.api :as d]) (:import [java.util Date])) (def schema-tx [{:db/ident :name @@ -38,7 +38,7 @@ (testing "historical values" (d/transact conn [{:db/id [:name "Alice"] :age 30}]) (are [x y] - (= x y) + (= x y) #{[30]} (d/q '[:find ?a :in $ ?e :where [?e :age ?a]] @conn [:name "Alice"]) #{[30] [25]} @@ -46,7 +46,7 @@ (testing "historical values after with retraction" (d/transact conn [[:db/retractEntity [:name "Alice"]]]) (are [x y] - (= x y) + (= x y) #{} (d/q '[:find ?a :in $ ?e :where [?e :age ?a]] @conn [:name "Alice"]) #{[30] [25]} @@ -107,11 +107,11 @@ query '[:find ?a :in $ ?e :where [?e :age ?a ?tx]]] (testing "get values at specific time" (is (= #{[25]} - (d/q query (d/as-of @conn first-date) [:name "Alice"])))) + (d/q query (d/as-of @conn first-date) [:name "Alice"])))) (testing "use transaction ID" (let [tx-id 536870914] (is (= #{[25]} - (d/q query (d/as-of @conn tx-id) [:name "Alice"]))))))) + (d/q query (d/as-of @conn tx-id) [:name "Alice"]))))))) (deftest test-since-db (let [uri "datahike:mem://test-historical-queries" @@ -122,7 +122,7 @@ query '[:find ?a :where [?e :age ?a]]] (testing "empty after first insertion" (is (= #{} - (d/q query (d/since @conn first-date))))) + (d/q query (d/since @conn first-date))))) (testing "added new value" (let [new-age 30 _ (d/transact conn [{:db/id [:name "Alice"] :age new-age}])] @@ -135,8 +135,7 @@ _ (d/create-database uri :initial-tx [{:db/ident :name :db/cardinality :db.cardinality/one :db/valueType :db.type/string - :db/unique :db.unique/identity - } + :db/unique :db.unique/identity} {:db/ident :age :db/cardinality :db.cardinality/one :db/valueType :db.type/long diff --git a/test/datahike/test/transact.cljc b/test/datahike/test/transact.cljc index 96a492984..d6223a454 100644 --- a/test/datahike/test/transact.cljc +++ b/test/datahike/test/transact.cljc @@ -1,13 +1,13 @@ (ns datahike.test.transact (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) (deftest test-with - (let [db (-> (d/empty-db {:aka { :db/cardinality :db.cardinality/many }}) + (let [db (-> (d/empty-db {:aka {:db/cardinality :db.cardinality/many}}) (d/db-with [[:db/add 1 :name "Ivan"]]) (d/db-with [[:db/add 1 :name "Petr"]]) (d/db-with [[:db/add 1 :aka "Devil"]]) @@ -22,8 +22,8 @@ (testing "Retract" (let [db (-> db - (d/db-with [[:db/retract 1 :name "Petr"]]) - (d/db-with [[:db/retract 1 :aka "Devil"]]))] + (d/db-with [[:db/retract 1 :name "Petr"]]) + (d/db-with [[:db/retract 1 :aka "Devil"]]))] (is (= (d/q '[:find ?v :where [1 :name ?v]] db) @@ -32,7 +32,7 @@ :where [1 :aka ?v]] db) #{["Tupen"]})) - (is (= (into {} (d/entity db 1)) { :aka #{"Tupen"} })))) + (is (= (into {} (d/entity db 1)) {:aka #{"Tupen"}})))) (testing "Cannot retract what's not there" (let [db (-> db @@ -40,7 +40,7 @@ (is (= (d/q '[:find ?v :where [1 :name ?v]] db) #{["Petr"]}))))) - + (testing "Skipping nils in tx" (let [db (-> (d/empty-db) (d/db-with [[:db/add 1 :attr 2] @@ -49,7 +49,6 @@ (is (= [[1 :attr 2], [3 :attr 4]] (map (juxt :e :a :v) (d/datoms db :eavt))))))) - (deftest test-with-datoms (testing "keeps tx number" (let [db (-> (d/empty-db) @@ -58,10 +57,10 @@ [:db/add 1 :aka "x" (+ 2 d/tx0)]]))] (is (= [[1 :age 17 (+ 1 d/tx0)] [1 :aka "x" (+ 2 d/tx0)] - [1 :name "Oleg" d/tx0 ]] + [1 :name "Oleg" d/tx0]] (map (juxt :e :a :v :tx) (d/datoms db :eavt)))))) - + (testing "retraction" (let [db (-> (d/empty-db) (d/db-with [(d/datom 1 :name "Oleg") @@ -72,11 +71,11 @@ (d/datoms db :eavt))))))) (deftest test-retract-fns - (let [db (-> (d/empty-db {:aka { :db/cardinality :db.cardinality/many } - :friend { :db/valueType :db.type/ref }}) - (d/db-with [ { :db/id 1, :name "Ivan", :age 15, :aka ["X" "Y" "Z"], :friend 2 } - { :db/id 2, :name "Petr", :age 37 } ]))] - (let [db (d/db-with db [ [:db.fn/retractEntity 1] ])] + (let [db (-> (d/empty-db {:aka {:db/cardinality :db.cardinality/many} + :friend {:db/valueType :db.type/ref}}) + (d/db-with [{:db/id 1, :name "Ivan", :age 15, :aka ["X" "Y" "Z"], :friend 2} + {:db/id 2, :name "Petr", :age 37}]))] + (let [db (d/db-with db [[:db.fn/retractEntity 1]])] (is (= (d/q '[:find ?a ?v :where [1 ?a ?v]] db) #{})) @@ -90,12 +89,12 @@ (testing "Retract entitiy with incoming refs" (is (= (d/q '[:find ?e :where [1 :friend ?e]] db) #{[2]})) - - (let [db (d/db-with db [ [:db.fn/retractEntity 2] ])] + + (let [db (d/db-with db [[:db.fn/retractEntity 2]])] (is (= (d/q '[:find ?e :where [1 :friend ?e]] db) #{})))) - - (let [db (d/db-with db [ [:db.fn/retractAttribute 1 :name] ])] + + (let [db (d/db-with db [[:db.fn/retractAttribute 1 :name]])] (is (= (d/q '[:find ?a ?v :where [1 ?a ?v]] db) #{[:age 15] [:aka "X"] [:aka "Y"] [:aka "Z"] [:friend 2]})) @@ -103,7 +102,7 @@ :where [2 ?a ?v]] db) #{[:name "Petr"] [:age 37]}))) - (let [db (d/db-with db [ [:db.fn/retractAttribute 1 :aka] ])] + (let [db (d/db-with db [[:db.fn/retractAttribute 1 :aka]])] (is (= (d/q '[:find ?a ?v :where [1 ?a ?v]] db) #{[:name "Ivan"] [:age 15] [:friend 2]})) @@ -112,10 +111,10 @@ #{[:name "Petr"] [:age 37]}))))) (deftest test-retract-fns-not-found - (let [db (-> (d/empty-db { :name { :db/unique :db.unique/identity } }) + (let [db (-> (d/empty-db {:name {:db/unique :db.unique/identity}}) (d/db-with [[:db/add 1 :name "Ivan"]])) all #(vec (d/datoms % :eavt))] - (are [op] (= [(d/datom 1 :name "Ivan")] + (are [op] (= [(d/datom 1 :name "Ivan")] (all (d/db-with db [op]))) [:db/retract 2 :name "Petr"] [:db.fn/retractAttribute 2 :name] @@ -124,8 +123,8 @@ [:db/retract [:name "Petr"] :name "Petr"] [:db.fn/retractAttribute [:name "Petr"] :name] [:db.fn/retractEntity [:name "Petr"]]) - - (are [op] (= [[] []] + + (are [op] (= [[] []] [(all (d/db-with db [op])) (all (d/db-with db [op op]))]) ;; idempotency [:db/retract 1 :name "Ivan"] @@ -137,7 +136,7 @@ [:db.fn/retractEntity [:name "Ivan"]]))) (deftest test-transact! - (let [conn (d/create-conn {:aka { :db/cardinality :db.cardinality/many }})] + (let [conn (d/create-conn {:aka {:db/cardinality :db.cardinality/many}})] (d/transact! conn [[:db/add 1 :name "Ivan"]]) (d/transact! conn [[:db/add 1 :name "Petr"]]) (d/transact! conn [[:db/add 1 :aka "Devil"]]) @@ -160,36 +159,36 @@ (d/transact! conn [[:db/cas 1 :weight 300 400]]) (is (= (:weight (d/entity @conn 1)) 400)) (is (thrown-msg? ":db.fn/cas failed on datom [1 :weight 400], expected 200" - (d/transact! conn [[:db.fn/cas 1 :weight 200 210]])))) - - (let [conn (d/create-conn {:label { :db/cardinality :db.cardinality/many }})] + (d/transact! conn [[:db.fn/cas 1 :weight 200 210]])))) + + (let [conn (d/create-conn {:label {:db/cardinality :db.cardinality/many}})] (d/transact! conn [[:db/add 1 :label :x]]) (d/transact! conn [[:db/add 1 :label :y]]) (d/transact! conn [[:db.fn/cas 1 :label :y :z]]) (is (= (:label (d/entity @conn 1)) #{:x :y :z})) (is (thrown-msg? ":db.fn/cas failed on datom [1 :label (:x :y :z)], expected :s" - (d/transact! conn [[:db.fn/cas 1 :label :s :t]])))) + (d/transact! conn [[:db.fn/cas 1 :label :s :t]])))) (let [conn (d/create-conn)] (d/transact! conn [[:db/add 1 :name "Ivan"]]) (d/transact! conn [[:db.fn/cas 1 :age nil 42]]) (is (= (:age (d/entity @conn 1)) 42)) (is (thrown-msg? ":db.fn/cas failed on datom [1 :age 42], expected nil" - (d/transact! conn [[:db.fn/cas 1 :age nil 4711]])))) + (d/transact! conn [[:db.fn/cas 1 :age nil 4711]])))) (let [conn (d/create-conn)] (is (thrown-msg? "Can't use tempid in '[:db.fn/cas -1 :attr nil :val]'. Tempids are allowed in :db/add only" - (d/transact! conn [[:db/add -1 :name "Ivan"] - [:db.fn/cas -1 :attr nil :val]]))))) + (d/transact! conn [[:db/add -1 :name "Ivan"] + [:db.fn/cas -1 :attr nil :val]]))))) (deftest test-db-fn - (let [conn (d/create-conn {:aka { :db/cardinality :db.cardinality/many }}) + (let [conn (d/create-conn {:aka {:db/cardinality :db.cardinality/many}}) inc-age (fn [db name] (if-let [[eid age] (first (d/q '{:find [?e ?age] :in [$ ?name] :where [[?e :name ?name] [?e :age ?age]]} - db name))] + db name))] [{:db/id eid :age (inc age)} [:db/add eid :had-birthday true]] (throw (ex-info (str "No entity with name: " name) {}))))] (d/transact! conn [{:db/id 1 :name "Ivan" :age 31}]) @@ -198,42 +197,42 @@ (d/transact! conn [[:db/add 1 :aka "Tupen"]]) (is (= (d/q '[:find ?v ?a :where [?e :name ?v] - [?e :age ?a]] @conn) + [?e :age ?a]] @conn) #{["Petr" 31]})) (is (= (d/q '[:find ?v :where [?e :aka ?v]] @conn) #{["Devil"] ["Tupen"]})) (is (thrown-msg? "No entity with name: Bob" - (d/transact! conn [[:db.fn/call inc-age "Bob"]]))) + (d/transact! conn [[:db.fn/call inc-age "Bob"]]))) (let [{:keys [db-after]} (d/transact! conn [[:db.fn/call inc-age "Petr"]]) e (d/entity db-after 1)] (is (= (:age e) 32)) (is (:had-birthday e))))) #_(deftest test-db-ident-fn ;; TODO: check for :db/ident support within hhtree - (let [conn (d/create-conn {:name {:db/unique :db.unique/identity}}) - inc-age (fn [db name] - (if-some [ent (d/entity db [:name name])] - [{:db/id (:db/id ent) - :age (inc (:age ent))} - [:db/add (:db/id ent) :had-birthday true]] - (throw (ex-info (str "No entity with name: " name) {}))))] - (d/transact! conn [{:db/id 1 - :name "Petr" - :age 31 - :db/ident :Petr} - {:db/ident :inc-age - :db/fn inc-age}]) - (is (thrown-msg? "Can’t find entity for transaction fn :unknown-fn" - (d/transact! conn [[:unknown-fn]]))) - (is (thrown-msg? "Entity :Petr expected to have :db/fn attribute with fn? value" - (d/transact! conn [[:Petr]]))) - (is (thrown-msg? "No entity with name: Bob" - (d/transact! conn [[:inc-age "Bob"]]))) - (d/transact! conn [[:inc-age "Petr"]]) - (let [e (d/entity @conn 1)] - (is (= (:age e) 32)) - (is (:had-birthday e))))) + (let [conn (d/create-conn {:name {:db/unique :db.unique/identity}}) + inc-age (fn [db name] + (if-some [ent (d/entity db [:name name])] + [{:db/id (:db/id ent) + :age (inc (:age ent))} + [:db/add (:db/id ent) :had-birthday true]] + (throw (ex-info (str "No entity with name: " name) {}))))] + (d/transact! conn [{:db/id 1 + :name "Petr" + :age 31 + :db/ident :Petr} + {:db/ident :inc-age + :db/fn inc-age}]) + (is (thrown-msg? "Can’t find entity for transaction fn :unknown-fn" + (d/transact! conn [[:unknown-fn]]))) + (is (thrown-msg? "Entity :Petr expected to have :db/fn attribute with fn? value" + (d/transact! conn [[:Petr]]))) + (is (thrown-msg? "No entity with name: Bob" + (d/transact! conn [[:inc-age "Bob"]]))) + (d/transact! conn [[:inc-age "Petr"]]) + (let [e (d/entity @conn 1)] + (is (= (:age e) 32)) + (is (:had-birthday e))))) (deftest test-resolve-eid (let [conn (d/create-conn) @@ -243,14 +242,14 @@ [:db/add -2 :age 22]]) t2 (d/transact! conn [[:db/add "Serg" :name "Sergey"] [:db/add "Serg" :age 30]])] - (is (= (:tempids t1) { -1 1, -2 2, :db/current-tx (+ d/tx0 1) })) - (is (= (:tempids t2) { "Serg" 3, :db/current-tx (+ d/tx0 2) })) + (is (= (:tempids t1) {-1 1, -2 2, :db/current-tx (+ d/tx0 1)})) + (is (= (:tempids t2) {"Serg" 3, :db/current-tx (+ d/tx0 2)})) (is (= #{[1 "Ivan" 19 (+ d/tx0 1)] [2 "Petr" 22 (+ d/tx0 1)] [3 "Sergey" 30 (+ d/tx0 2)]} (d/q '[:find ?e ?n ?a ?t :where [?e :name ?n ?t] - [?e :age ?a]] @conn))))) + [?e :age ?a]] @conn))))) (deftest test-resolve-eid-refs (let [conn (d/create-conn {:friend {:db/valueType :db.type/ref @@ -266,9 +265,9 @@ q '[:find ?fn :in $ ?n :where [?e :name ?n] - [?e :friend ?fe] - [?fe :name ?fn]]] - (is (= (:tempids tx) { -1 2, -2 3, "B" 4, -3 5, :db/current-tx (+ d/tx0 1) })) + [?e :friend ?fe] + [?fe :name ?fn]]] + (is (= (:tempids tx) {-1 2, -2 3, "B" 4, -3 5, :db/current-tx (+ d/tx0 1)})) (is (= (d/q q @conn "Sergey") #{["Ivan"] ["Petr"]})) (is (= (d/q q @conn "Boris") #{["Oleg"]})) (is (= (d/q q @conn "Oleg") #{["Boris"]})))) @@ -283,12 +282,12 @@ [:db/add -1 :name "Y"] [:db/add -1 :created-at tx-tempid]])] (is (= (d/q '[:find ?e ?a ?v :where [?e ?a ?v]] @conn) - #{[1 :name "X"] - [1 :created-at (+ d/tx0 1)] - [(+ d/tx0 1) :prop1 "prop1"] - [(+ d/tx0 1) :prop2 "prop2"] - [2 :name "Y"] - [2 :created-at (+ d/tx0 1)]})) + #{[1 :name "X"] + [1 :created-at (+ d/tx0 1)] + [(+ d/tx0 1) :prop1 "prop1"] + [(+ d/tx0 1) :prop2 "prop2"] + [2 :name "Y"] + [2 :created-at (+ d/tx0 1)]})) (is (= (:tempids tx1) (assoc {-1 2, :db/current-tx (+ d/tx0 1)} tx-tempid (+ d/tx0 1)))) (let [tx2 (d/transact! conn [[:db/add tx-tempid :prop3 "prop3"]]) diff --git a/test/datahike/test/upsert.cljc b/test/datahike/test/upsert.cljc index c18909105..2a62b9edd 100644 --- a/test/datahike/test/upsert.cljc +++ b/test/datahike/test/upsert.cljc @@ -1,17 +1,17 @@ (ns datahike.test.upsert (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.db :as db] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.db :as db] + [datahike.test.core :as tdc])) #?(:cljs (def Throwable js/Error)) (deftest test-upsert - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/identity } - :email { :db/unique :db.unique/identity }}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/identity} + :email {:db/unique :db.unique/identity}}) [{:db/id 1 :name "Ivan" :email "@1"} {:db/id 2 :name "Petr" :email "@2"}]) touched (fn [tx e] (into {} (d/touch (d/entity (:db-after tx) e)))) @@ -29,7 +29,7 @@ {:name "Ivan" :email "@1" :age 35})) (is (= (tempids tx) {})))) - + (testing "upsert with tempid" (let [tx (d/with db [{:db/id -1 :name "Ivan" :age 35}])] (is (= (touched tx 1) @@ -48,14 +48,14 @@ (is (= (tempids tx) {"1" 1 "2" 2})))) - + (testing "upsert by 2 attrs with tempid" (let [tx (d/with db [{:db/id -1 :name "Ivan" :email "@1" :age 35}])] (is (= (touched tx 1) {:name "Ivan" :email "@1" :age 35})) (is (= (tempids tx) {-1 1})))) - + (testing "upsert to two entities, resolve to same tempid" (let [tx (d/with db [{:db/id -1 :name "Ivan" :age 35} {:db/id -1 :name "Ivan" :age 36}])] @@ -88,12 +88,12 @@ (testing "upsert conficts with existing id" (is (thrown-with-msg? Throwable #"Conflicting upsert: \[:name \"Ivan\"\] resolves to 1, but entity already has :db/id 2" - (d/with db [{:db/id 2 :name "Ivan" :age 36}])))) + (d/with db [{:db/id 2 :name "Ivan" :age 36}])))) (testing "upsert conficts with non-existing id" (is (thrown-with-msg? Throwable #"Conflicting upsert: \[:name \"Ivan\"\] resolves to 1, but entity already has :db/id 3" - (d/with db [{:db/id 3 :name "Ivan" :age 36}])))) - + (d/with db [{:db/id 3 :name "Ivan" :age 36}])))) + (testing "upsert by non-existing value resolves as update" (let [tx (d/with db [{:name "Ivan" :email "@3" :age 35}])] (is (= (touched tx 1) @@ -103,7 +103,7 @@ (testing "upsert by 2 conflicting fields" (is (thrown-with-msg? Throwable #"Conflicting upserts: \[:name \"Ivan\"\] resolves to 1, but \[:email \"@2\"\] resolves to 2" - (d/with db [{:name "Ivan" :email "@2" :age 35}])))) + (d/with db [{:name "Ivan" :email "@2" :age 35}])))) (testing "upsert over intermediate db" (let [tx (d/with db [{:name "Igor" :age 35} @@ -112,7 +112,7 @@ {:name "Igor" :age 36})) (is (= (tempids tx) {})))) - + (testing "upsert over intermediate db, tempids" (let [tx (d/with db [{:db/id -1 :name "Igor" :age 35} {:db/id -1 :name "Igor" :age 36}])] @@ -131,12 +131,10 @@ (testing "upsert and :current-tx conflict" (is (thrown-with-msg? Throwable #"Conflicting upsert: \[:name \"Ivan\"\] resolves to 1, but entity already has :db/id \d+" - (d/with db [{:db/id :db/current-tx :name "Ivan" :age 35}])))) -)) - + (d/with db [{:db/id :db/current-tx :name "Ivan" :age 35}])))))) (deftest test-redefining-ids - (let [db (-> (d/empty-db {:name { :db/unique :db.unique/identity }}) + (let [db (-> (d/empty-db {:name {:db/unique :db.unique/identity}}) (d/db-with [{:db/id -1 :name "Ivan"}]))] (let [tx (d/with db [{:db/id -1 :age 35} {:db/id -1 :name "Ivan" :age 36}])] @@ -144,13 +142,13 @@ (tdc/all-datoms (:db-after tx)))) (is (= {-1 1, :db/current-tx (+ d/tx0 2)} (:tempids tx))))) - - (let [db (-> (d/empty-db {:name { :db/unique :db.unique/identity }}) + + (let [db (-> (d/empty-db {:name {:db/unique :db.unique/identity}}) (d/db-with [{:db/id -1 :name "Ivan"} {:db/id -2 :name "Oleg"}]))] (is (thrown-with-msg? Throwable #"Conflicting upsert: -1 resolves both to 1 and 2" - (d/with db [{:db/id -1 :name "Ivan" :age 35} - {:db/id -1 :name "Oleg" :age 36}]))))) + (d/with db [{:db/id -1 :name "Ivan" :age 35} + {:db/id -1 :name "Oleg" :age 36}]))))) ;; https://github.com/tonsky/datahike/issues/285 (deftest test-retries-order @@ -177,16 +175,16 @@ [[:db/add -1 :name "Ivan"] [:db/add -1 :age 12]] #{[1 :age 12] [1 :name "Ivan"]} - + [[:db/add -1 :age 12] [:db/add -1 :name "Ivan"]] #{[1 :age 12] [1 :name "Ivan"]})) - - (let [db (-> (d/empty-db {:name { :db/unique :db.unique/identity }}) + + (let [db (-> (d/empty-db {:name {:db/unique :db.unique/identity}}) (d/db-with [[:db/add -1 :name "Ivan"] [:db/add -2 :name "Oleg"]]))] (is (thrown-with-msg? Throwable #"Conflicting upsert: -1 resolves both to 1 and 2" - (d/with db [[:db/add -1 :name "Ivan"] - [:db/add -1 :age 35] - [:db/add -1 :name "Oleg"] - [:db/add -1 :age 36]]))))) + (d/with db [[:db/add -1 :name "Ivan"] + [:db/add -1 :age 35] + [:db/add -1 :name "Oleg"] + [:db/add -1 :age 36]]))))) diff --git a/test/datahike/test/validation.cljc b/test/datahike/test/validation.cljc index a1c20b1f8..6601097f1 100644 --- a/test/datahike/test/validation.cljc +++ b/test/datahike/test/validation.cljc @@ -1,40 +1,40 @@ (ns datahike.test.validation (:require - #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] - :clj [clojure.test :as t :refer [is are deftest testing]]) - [datahike.core :as d] - [datahike.test.core :as tdc])) + #?(:cljs [cljs.test :as t :refer-macros [is are deftest testing]] + :clj [clojure.test :as t :refer [is are deftest testing]]) + [datahike.core :as d] + [datahike.test.core :as tdc])) #?(:cljs (def Throwable js/Error)) (deftest test-with-validation - (let [db (d/empty-db {:profile { :db/valueType :db.type/ref }})] + (let [db (d/empty-db {:profile {:db/valueType :db.type/ref}})] (are [tx] (thrown-with-msg? Throwable #"Expected number, string or lookup ref for :db/id" (d/db-with db tx)) [{:db/id #"" :name "Ivan"}]) - + (are [tx] (thrown-with-msg? Throwable #"Bad entity attribute" (d/db-with db tx)) [[:db/add -1 nil "Ivan"]] [[:db/add -1 17 "Ivan"]] [{:db/id -1 17 "Ivan"}]) - + (are [tx] (thrown-with-msg? Throwable #"Cannot store nil as a value" (d/db-with db tx)) [[:db/add -1 :name nil]] [{:db/id -1 :name nil}]) - + (are [tx] (thrown-with-msg? Throwable #"Expected number or lookup ref for entity id" (d/db-with db tx)) [[:db/add nil :name "Ivan"]] [[:db/add {} :name "Ivan"]] [[:db/add -1 :profile #"regexp"]] [{:db/id -1 :profile #"regexp"}]) - + (is (thrown-with-msg? Throwable #"Unknown operation" (d/db-with db [["aaa" :name "Ivan"]]))) (is (thrown-with-msg? Throwable #"Bad entity type at" (d/db-with db [:db/add "aaa" :name "Ivan"]))) (is (thrown-with-msg? Throwable #"Tempids are allowed in :db/add only" (d/db-with db [[:db/retract -1 :name "Ivan"]]))) (is (thrown-with-msg? Throwable #"Bad transaction data" (d/db-with db {:profile "aaa"}))))) (deftest test-unique - (let [db (d/db-with (d/empty-db {:name { :db/unique :db.unique/value }}) + (let [db (d/db-with (d/empty-db {:name {:db/unique :db.unique/value}}) [[:db/add 1 :name "Ivan"] [:db/add 2 :name "Petr"]])] (are [tx] (thrown-with-msg? Throwable #"unique constraint" (d/db-with db tx))