datascript
datascript copied to clipboard
Nested upserts to the same entity by identity fails
Opening a separate issue based on comment at https://github.com/tonsky/datascript/issues/76#issuecomment-269110873
In order the following fails with Cannot add #datascript/Datom [1 :name "Ivan" true] because of unique constraint
, but succeeds if you swap the order of keys in the map.
(deftest test-ref-upsert
(let [db (d/empty-db {:name {:db/unique :db.unique/identity}
:ref {:db/valueType :db.type/ref}})]
(are [tx res] (= res (tdc/all-datoms (d/db-with db tx)))
[(array-map :ref {:name "Ivan"} :name "Ivan")]
#{[1 :name "Ivan"]
[1 :ref 1]})))
Was just bitten by this as well...
Here's another repro:
(def db (dts/empty-db {:my_id {:db/unique :db.unique/identity}
:my_ref {:db/valueType :db.type/ref
:db/cardinality :db.cardinality/many}}))
(dts/with db
[{:my_other_attr "hello"
:my_ref
[{:my_id 2
:my_ref [{:my_id 1}]}]
:my_id 1}])
;Error: Cannot add #datascript/Datom [1 :my_id 1 536870913 true] because of unique constraint: (#datascript/Datom [3 :my_id 1 536870913 true])
(dts/with db
[{:my_id 1 ;; <----------------- the only difference to the above is that this map entry has moved.
:my_other_attr "hello"
:my_ref
[{:my_id 2
:my_ref [{:my_id 1}]}]}])
;; Succeeds
In the meantime, my mitigation strategy has consisted of adding tempids everywhere, e.g:
(dts/with db
[{:db/id -1
:my_other_attr "hello"
:my_ref
[{:my_id 2
:my_ref [{:db/id -1
:my_id 1}]}]
:my_id 1}])
So this would be solved if I process all non-ref keys in a map first, then all the ref keys, right?
I don't know enough about the implementation details of DataScript, but to me it seems more a matter of making sure the entities unify correctly based on unique attributes. (That may not be in contradiction to what you just wrote).
Consider the following tx as the ultimate pathological example:
[{:my_other_attr "hello"
:my_ref
[{:my_id 2
:my_ref [{:my_id 1, :yet_another_attr "how are you?"}]}]
:my_id 1}]