Skip to content

Commit

Permalink
Allow lookup-refs inside tuples used for lookup-refs (closes #452)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Jun 18, 2024
1 parent 3915250 commit 9ae1fd5
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
- Correctly restore `:max-tx` from storage
- Fixed tempid/upsert resolution when multiple tempids are added first #472
- Allow upsert by implicit tuple when only tuple components are specified #473
- Allow lookup-refs inside tuples used for lookup-refs #452

# 1.6.5 - May 3, 2024

Expand Down
37 changes: 30 additions & 7 deletions src/datascript/db.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -1238,6 +1238,14 @@
(raise "Bad attribute type: " attr ", expected keyword or string"
{:error :transact/syntax, :attribute attr})))

(defn resolve-tuple-refs [db a vs]
(mapv
(fn [a v]
(if (and (ref? db a) (sequential? v)) ;; lookup-ref
(entid-strict db v)
v))
(-> db -schema (get a) :db/tupleAttrs) vs))

(defn+ ^number entid [db eid]
{:pre [(db? db)]}
(cond
Expand All @@ -1250,15 +1258,22 @@
(let [[attr value] eid]
(cond
(not= (count eid) 2)
(raise "Lookup ref should contain 2 elements: " eid
{:error :lookup-ref/syntax, :entity-id eid})
(raise "Lookup ref should contain 2 elements: " eid
{:error :lookup-ref/syntax, :entity-id eid})

(not (is-attr? db attr :db/unique))
(raise "Lookup ref attribute should be marked as :db/unique: " eid
{:error :lookup-ref/unique, :entity-id eid})
(raise "Lookup ref attribute should be marked as :db/unique: " eid
{:error :lookup-ref/unique, :entity-id eid})

(tuple? db attr)
(let [value' (resolve-tuple-refs db attr value)]
(-> (-datoms db :avet attr value' nil nil) first :e))

(nil? value)
nil
nil

:else
(-> (-datoms db :avet attr value nil nil) first :e)))
(-> (-datoms db :avet attr value nil nil) first :e)))

#?@(:cljs [(array? eid) (recur db (array-seq eid))])

Expand Down Expand Up @@ -1838,6 +1853,13 @@
(allocate-eid v resolved)
(update ::value-tempids assoc resolved v))]
(recur report' es)))

(and
(or (= op :db/add) (= op :db/retract))
(not (::internal (meta entity)))
(tuple? db a)
(not= v (resolve-tuple-refs db a v)))
(recur report (cons [op e a (resolve-tuple-refs db a v)] entities))

(tempid? e)
(let [upserted-eid (when (is-attr? db a :db.unique/identity)
Expand All @@ -1861,7 +1883,8 @@
(raise "Conflicting upsert: " e " resolves to " upserted-eid " via " entity
{:error :transact/upsert})))

(and (not (::internal (meta entity)))
(and
(not (::internal (meta entity)))
(tuple? db a))
;; allow transacting in tuples if they fully match already existing values
(let [tuple-attrs (get-in db [:schema a :db/tupleAttrs])]
Expand Down
31 changes: 31 additions & 0 deletions test/datascript/test/tuples.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,37 @@
:c "c"}
(d/pull (d/db conn) '[*] [:a+b ["a" "b"]])))))

;; https://github.com/tonsky/datascript/issues/452
(deftest lookup-refs-in-tuple
(let [schema {:ref {:db/valueType :db.type/ref}
:name {:db/unique :db.unique/identity}
:ref+name {:db/valueType :db.type/tuple
:db/tupleAttrs [:ref :name]
:db/unique :db.unique/identity}}
db (-> (d/empty-db schema)
(d/db-with
[{:db/id -1 :name "Ivan"}
{:db/id -2 :name "Oleg"}
{:db/id -3 :name "Petr" :ref -1}
{:db/id -4 :name "Yuri" :ref -2}]))]
(let [db' (d/db-with db [{:ref+name [1 "Petr"], :age 32}])]
(is (= {:age 32} (d/pull db' [:age] 3))))

(let [db' (d/db-with db [{:ref+name [[:name "Ivan"] "Petr"], :age 32}])]
(is (= {:age 32} (d/pull db' [:age] 3))))

(let [db' (d/db-with db [[:db/add -1 :ref+name [1 "Petr"]]
[:db/add -1 :age 32]])]
(is (= {:age 32} (d/pull db' [:age] 3))))

(let [db' (d/db-with db [[:db/add -1 :ref+name [[:name "Ivan"] "Petr"]]
[:db/add -1 :age 32]])]
(is (= {:age 32} (d/pull db' [:age] 3))))

(is (= 1 (:db/id (d/entity db [:name "Ivan"]))))
(is (= 3 (:db/id (d/entity db [:ref+name [1 "Petr"]]))))
(is (= 3 (:db/id (d/entity db [:ref+name [[:name "Ivan"] "Petr"]]))))))

(deftest test-validation
(let [db (d/empty-db {:a+b {:db/tupleAttrs [:a :b]}})
db1 (d/db-with db [[:db/add 1 :a "a"]])]
Expand Down

0 comments on commit 9ae1fd5

Please sign in to comment.