Updating transaction in Datomic for an attribute that has many cardinality

南楼画角 提交于 2019-12-22 11:27:51

问题


I have searched for two days and haven't seen any code that is closed to this. This is the only code in java that I seen and it's not exactly what I wanted.

conn.transact(list(list("db.fn/cas", datomic_id, "attribute you want to update", old value, new value))).get();

I have tried this code with a single value in the old value and a single value in the new value but it just stack the information instead of overlaying it. Example: old value is chicken and new value is fish. After the transaction, it's [chicken, fish] instead of what I expected to be just [fish] and chicken will be move into archive(history).

So the question is, how do you ref the old array value and how do you give the new value an array so it'll update as what I expected to be as stated above.

I remember reading somewhere that under the hood it's just a series of values linking to one attribute. If this is the case does that mean that I have to find the datomic id of the string and change it? Also have to remove it if it's not on the new list?


回答1:


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.




回答2:


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.


来源:https://stackoverflow.com/questions/36356933/updating-transaction-in-datomic-for-an-attribute-that-has-many-cardinality

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!