Skip to content

Instantly share code, notes, and snippets.

@wvdlaan
Last active March 16, 2018 06:53
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wvdlaan/5772825 to your computer and use it in GitHub Desktop.
Save wvdlaan/5772825 to your computer and use it in GitHub Desktop.
Datomic multiple attribute key example
(ns myupsert.core
(require [datomic.api :as d]))
(def schema
[
{:db/id #db/id [:db.part/db]
:db/ident :product/name
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
:db/ident :product/version
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/db]
:db/ident :product/price
:db/valueType :db.type/double
:db/cardinality :db.cardinality/one
:db.install/_attribute :db.part/db}
{:db/id #db/id [:db.part/user]
:db/ident :upsertProduct
:db/fn #db/fn
{:lang :clojure
:params [db m]
:code (if-let [id (ffirst
(d/q '[:find ?e
:in $ ?n ?v
:where
[?e :product/name ?n]
[?e :product/version ?v]]
db (:product/name m) (:product/version m)))]
[(-> (dissoc m :product/name :product/version)
(assoc :db/id id))]
[m])}}
])
(defn init-db []
(let [uri "datomic:mem://test"
_ (d/create-database uri)
conn (d/connect uri)
_ (d/transact conn schema)]
conn))
(defn get-product [name version conn]
(->> (d/q '[:find ?e
:in $ ?n ?v
:where [?e :product/name ?n] [?e :product/version ?v]]
(d/db conn) name version)
(map first)
(map #(d/entity (d/db conn) %))
(map d/touch)))
(comment
(def conn (init-db))
(d/transact conn [[:upsertProduct {:db/id #db/id [:db.part/user]
:product/name "Foo"
:product/version "1.1"
:product/price 12.34}]])
(get-product "Foo" "1.1" conn)
(d/transact conn [[:upsertProduct {:db/id #db/id [:db.part/user]
:product/name "Foo"
:product/version "1.1"
:product/price 56.78}]])
(get-product "Foo" "1.1" conn)
)
@alexanderkiel
Copy link

Nicely done. But this upsert doesn't work if you try to upsert two products in the same transaction. Executing

(d/transact conn [[:upsertProduct {:db/id (d/tempid :db.part/user)
                                   :product/name "Foo"
                                   :product/version "1.1"
                                   :product/price 12.34}]
                  [:upsertProduct {:db/id (d/tempid :db.part/user)
                                   :product/name "Foo"
                                   :product/version "1.1"
                                   :product/price 12.56}]])

leads to two products with the same :product/name and :product/version. The reason for this is that both :upsertProduct functions get the same database from the start of the transaction.

@wvdlaan
Copy link
Author

wvdlaan commented Mar 12, 2015

Yes, you are right. And AFAIK this can not be solve using a transaction function. This topic is discussed here: https://groups.google.com/d/msg/datomic/dhzwweFSiso/lqET7Isa6XcJ

@neverfox
Copy link

Yes, you are right. And AFAIK this can not be solve using a transaction function.

But I can live with that. I think it's more of a logical limitation on what a transaction is supposed to be than on Datomic's feature set. I'd love to see a Gist for a utility method for breaking up transactions into multiple ones based on compound keys before I have to work it out myself, lol.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment