Updating transaction in Datomic for an attribute that has many cardinality

醉酒当歌 提交于 2019-12-06 07:29:36

FYI, these are the generic transaction functions I currently use for this kind of task (declared from Clojure, but should be fairly easy to adapt to Java if required):

[{:db/ident :bsu.fns/replace-to-many-scalars,
  :db/doc "Given an entity's lookup ref, a to-many (scalar) attribute, and a list of new values,
 yields a transaction that replaces the old values by new ones"
  :db/id (d/tempid :db.part/user),
  :db/fn (d/function
           '{:lang :clojure,
             :imports [],
             :requires [[datomic.api :as d]],
             :params [db entid attr new-vals],
             :code (let [old-vals (if-let [e (d/entity db entid)] (get e attr) ())
                         to-remove (remove (set (seq new-vals)) old-vals)]
                     (concat
                       (for [ov to-remove] [:db/retract entid attr ov])
                       (for [nv new-vals] [:db/add entid attr nv]))
                     )}),
  }
 {:db/ident :bsu.fns/to-many-retract-all-but,
  :db/doc "Given an entity lookup ref, a to-many (entity) attribute, and a list of lookup refs
  expands to a transaction which will retract all the [origin `to-many-attr` target] relationships but those for which target is among the `to-spare-lookup-refs`"
  :db/id (d/tempid :db.part/user),
  :db/fn (d/function
           '{:lang :clojure,
             :imports [],
             :requires [[datomic.api :as d]],
             :params [db origin to-many-attr to-spare-lookup-refs],
             :code (let [old-targets-ids (d/q '[:find [?t ...] :in $ ?to-many-attr ?origin :where [?origin ?to-many-attr ?t]]
                                           db to-many-attr origin)
                         to-spare-ids (for [lr to-spare-lookup-refs] (:db/id (d/entity db lr)))
                         to-delete (->> old-targets-ids (remove (set to-spare-ids)))]
                     (for [eid to-delete] [:db/retract origin to-many-attr eid])
                     #_[old-targets-ids to-update-ids to-delete])}),
  }]

I don't claim at all they're optimal performance or design-wise, but they've worked for me so far. HTH.

If you need a "last write wins" style consistent solution to replace all values for a particular entity for a card many attribute, your best bet is to go with a transaction function. You could take the following approach:

  1. Get all datoms matching entity + attribute you want to retract all values for.
  2. Generate retractions for all of them.
  3. Create add transactions for all new values (e.g. from a passed collection)
  4. Remove any conflicts (i.e. if you have the same EAV with both an add and a result)
  5. Return the resulting transaction data.
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!