datascript icon indicating copy to clipboard operation
datascript copied to clipboard

Nested upserts to the same entity by identity fails

Open brandonbloom opened this issue 8 years ago • 3 comments

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]})))

brandonbloom avatar Dec 25 '16 06:12 brandonbloom

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}])

vvvvalvalval avatar Oct 06 '20 19:10 vvvvalvalval

So this would be solved if I process all non-ref keys in a map first, then all the ref keys, right?

tonsky avatar Oct 07 '20 11:10 tonsky

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}]

vvvvalvalval avatar Oct 07 '20 12:10 vvvvalvalval