Skip to content

Instantly share code, notes, and snippets.

@Hendekagon
Last active August 10, 2018 09:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hendekagon/2fc00cbf4eeddab8f834c84f6eb66262 to your computer and use it in GitHub Desktop.
Save Hendekagon/2fc00cbf4eeddab8f834c84f6eb66262 to your computer and use it in GitHub Desktop.
Unique upsert in Datomic
; create attributes p and q, where p is unique identity
(d/transact edb1 {:tx-data [{:db/ident :p :db/cardinality :db.cardinality/one :db/valueType :db.type/keyword :db/unique :db.unique/identity}]})
=>
(d/transact edb1 {:tx-data [{:db/ident :q :db/cardinality :db.cardinality/one :db/valueType :db.type/keyword}]})
=>
; now add a datum
(d/transact edb1 {:tx-data [{:p :a1 :q :x}]})
=>
; and another with a different :p
(d/transact edb1 {:tx-data [{:p :a2 :q :x}]})
=>
; let's have a look:
(d/q '{:find [(pull ?e [*])] :where [[?e :p]]} (d/db edb1))
=>
[[{:db/id 37216269576962135, :p :a1, :q :x}]
[{:db/id 5286451906347096, :p :a2, :q :x}]]
; there they are
; now let's transact a different :q for the same value of :p
(d/transact edb1 {:tx-data [{:p :a1 :q :y}]})
=>
; (all is well)
(d/q '{:find [(pull ?e [*])] :where [[?e :p]]} (d/db edb1))
=>
[[{:db/id 37216269576962135, :p :a1, :q :y}]
[{:db/id 5286451906347096, :p :a2, :q :x}]]
; that same entity 37216269576962135 now has a :q of :y not :x
; let's upsert again:
(d/transact edb1 {:tx-data [{:p :a1 :q :z}]})
=>
(d/q '{:find [(pull ?e [*])] :where [[?e :p]]} (d/db edb1))
=>
[[{:db/id 37216269576962135, :p :a1, :q :z}]
[{:db/id 5286451906347096, :p :a2, :q :x}]]
; again it updates
; now let's try to upsert the same entity with two conflicting assertions:
(d/transact edb1 {:tx-data [{:p :a1 :q :z} {:p :a1 :q :w}]})
ExceptionInfo Two datoms in the same transaction conflict: {:d1 #datom[37216269576962135 79 :z 13194139533352 true], :d2 #datom[37216269576962135 79 :w 13194139533352 true]} clojure.core/ex-info (core.clj:4739)
; because those two datums had conflicting values in the same transaction - split that into 2 separate transactions if that's what you want
; next let's look at :db.unique/value
; add an attibute :r which is unique by value
(d/transact edb1 {:tx-data [{:db/ident :r :db/cardinality :db.cardinality/one :db/valueType :db.type/keyword :db/unique :db.unique/value}]})
=>
; and assert that there's an the :r of entity with :p :a2 is :x
(d/transact edb1 {:tx-data [{:p :a2 :r :x}]})
=>
; let's try the same thing with the entity with :p :a1
(d/transact edb1 {:tx-data [{:p :a1 :r :x}]})
ExceptionInfo Unique conflict: :r, value: :x already held by: 5286451906347096 asserted for: 37216269576962135 clojure.core/ex-info (core.clj:4739)
; no - there can be only one with :r :x
; now let's see what happens without unique identity
; we'll add an attribute :s which doesn't have :db/unique :db.unique/identity
(d/transact edb1 {:tx-data [{:db/ident :s :db/cardinality :db.cardinality/one :db/valueType :db.type/keyword}]})
=>
; now assert something with a :s on it
(d/transact edb1 {:tx-data [{:s :x :q :x}]})
=>
; let's have a look what we've got:
(d/q '{:find [(pull ?e [*])] :where [[?e :q]]} (d/db edb1))
=>
[[{:db/id 37216269576962135, :p :a1, :q :z}]
[{:db/id 5286451906347096, :p :a2, :q :x, :r :x}]
[{:db/id 54298282226090073, :q :x, :s :x}]]
; there it is, 54298282226090073, along with the previous things
; what happens when we assert that again ?
(d/transact edb1 {:tx-data [{:s :x :q :x}]})
=>
(d/q '{:find [(pull ?e [*])] :where [[?e :q]]} (d/db edb1))
=>
[[{:db/id 37216269576962135, :p :a1, :q :z}]
[{:db/id 5286451906347096, :p :a2, :q :x, :r :x}]
[{:db/id 54298282226090073, :q :x, :s :x}]
[{:db/id 54047593574957146, :q :x, :s :x}]]
; because :s isn't unique identity, there's no upsert so we get multiple datums
; let's have another:
(d/transact edb1 {:tx-data [{:s :x :q :y}]})
=>
(d/q '{:find [(pull ?e [*])] :where [[?e :q]]} (d/db edb1))
=>
[[{:db/id 12208977114824795, :q :y, :s :x}]
[{:db/id 37216269576962135, :p :a1, :q :z}]
[{:db/id 5286451906347096, :p :a2, :q :x, :r :x}]
[{:db/id 54298282226090073, :q :x, :s :x}]
[{:db/id 54047593574957146, :q :x, :s :x}]]
; and so on - we can have as many as we like
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment