I recently encountered a slight difficulty with updating cardinality many attributes in Datomic, so I thought I would make a short walkthrough post about it.

The Problem

In Datomic each attribute has a “cardinality”, signifying how many values an attribute is allowed. Cardinality can be either “one” or “many”. Adding values for a cardinality many attribute is fairly straightforward, but updating them to a specific set is more difficult. Let’s take the example of a simple Todo list, where each todo has a title and multiple tags. Our schema looks like this:

(def schema [{:db/ident :todo/title :db/valueType :db.type/string :db/cardinality :db.cardinality/one} {:db/ident :todo/tags :db/valueType :db.type/string :db/cardinality :db.cardinality/many}])

Creating a new todo with a set of tags is fairly straightforward:

(d/transact conn [{:todo/title "Do the dishes" :todo/tags ["whenever"]}])

As is adding a tag to an existing todo:

(d/transact conn [:db/add todo-entity-id :todo/tags "boring"])

However, setting a todo’s tags to a specific set of values is more difficult. There’s no built-in way to do this in Datomic. Let’s say we want to set the tags for a todo to ["important" "today"] , how would we do that?

The Solution

One solution is to:

Query the current values of the attribute Diff them with the new values Use that diff to create transactions to: Add any new values Retract values we no longer want Apply the transactions using d/transact

Here’s the code to do this:

(defn update-attr-txs [db entity-id attr values] (let [;; Step 1 current-vals (-> (d/q '[:find [?p ...] :where [?id :intention/parents ?p] :in $ ?id] db entity-id) (set)) ;; Step 2 [added removed] (clojure.data/diff (set values) current-vals)] ;; Step 3 (concat ;; Step 3.1 (->> added (map #(-> [:db/add entity-id attr %]))) ;; Step 3.2 (->> removed (map #(-> [:db/retract entity-id attr %])))))) ;; Step 4 (->> (update-attr-txs (d/db conn) todo-entity-id :todo/tags #{"important" "today"}) (d/transact conn))

That should be all you need! I’ve created a gist containing the update-attr-txs function here. You can find some alternative solutions in this StackOverflow post, and also this one.

I hope this helps you out, please don’t hesitate to get in touch if you have any questions.