【问题标题】:How do I undo or reverse a transaction in datomic?如何撤消或撤消 datomic 中的事务?
【发布时间】:2014-08-19 17:48:31
【问题描述】:

我不小心向 datomic 提交了一个事务,我想“撤消”整个事务。我确切地知道它是哪个事务并且我可以看到它的 datoms,但我不知道如何从那里得到一个回滚事务。

【问题讨论】:

    标签: clojure datomic


    【解决方案1】:

    基本流程:

    1. 检索在要撤消的事务中创建的 datoms。使用事务日志找到它们。
    2. 删除与事务实体本身相关的数据:我们不想收回事务元数据。
    3. 反转所有剩余 datom 的“已添加”状态,即,如果添加了 datom,则将其收回,如果已收回,则添加它。
    4. 颠倒倒置的 datoms 的顺序,以便在重新断言旧的好值之前收回坏新值。
    5. 提交新事务。

    在 Clojure 中,您的代码如下所示:

    (defn rollback
      "Reassert retracted datoms and retract asserted datoms in a transaction,
      effectively \"undoing\" the transaction.
    
      WARNING: *very* naive function!"
      [conn tx]
      (let [tx-log (-> conn d/log (d/tx-range tx nil) first) ; find the transaction
            txid   (-> tx-log :t d/t->tx) ; get the transaction entity id
            newdata (->> (:data tx-log)   ; get the datoms from the transaction
                         (remove #(= (:e %) txid)) ; remove transaction-metadata datoms
                         ; invert the datoms add/retract state.
                         (map #(do [(if (:added %) :db/retract :db/add) (:e %) (:a %) (:v %)]))
                         reverse)] ; reverse order of inverted datoms.
        @(d/transact conn newdata)))  ; commit new datoms.
    

    【讨论】:

    • 我想澄清第 (4) 点不是必需的 - 事务不是程序,事务中 Datoms 的顺序无关紧要。在这篇博文中还有更多关于处理不良数据的信息(以及为什么从系统推理的角度来看不良数据的记录是有用的):blog.datomic.com/2014/08/…
    • 看起来太糟糕了。还原整个事务非常有用,它应该是原生的,更容易。
    • @FelipeMicaroniLalli 考虑一下:1)在大多数数据库系统中,这是不可能的 2)“恢复”一个 tx 没有一个易于理解的含义,因为 tx 本身并没有停止存在。这与恢复旧的 git 提交是相同的问题:它可能需要手动合并才能有意义。
    【解决方案2】:

    这并不是为了回答最初的问题,而是为了那些从 Google 来到这里寻找关于如何回滚 datascript 事务的灵感的人。我没有找到关于它的文档,所以我自己写了:

    (defn rollback
      "Takes a transaction result and reasserts retracted
      datoms and retracts asserted datoms, effectively
      \"undoing\" the transaction."
      [{:keys [tx-data]}]
      ; The passed transaction result looks something like
      ; this:
      ;
      ; {:db-before
      ;  {1 :post/body,
      ;   1 :post/created-at,
      ;   1 :post/foo,
      ;   1 :post/id,
      ;   1 :post/title},
      ;  :db-after {},
      ;  :tx-data
      ;  [#datascript/Datom [1 :post/body "sdffdsdsf" 536870914 false]
      ;   #datascript/Datom [1 :post/created-at 1576538572631 536870914 false]
      ;   #datascript/Datom [1 :post/foo "foo" 536870914 false]
      ;   #datascript/Datom [1 :post/id #uuid "a21ad816-c509-42fe-a1b7-32ad9d3931ef" 536870914 false]
      ;   #datascript/Datom [1 :post/title "123" 536870914 false]],
      ;  :tempids {:db/current-tx 536870914},
      ;  :tx-meta nil}))))
      ;
      ; We want to transform each datom into a new piece of
      ; a transaction. The last field in each datom indicates
      ; whether it was added (true) or retracted (false). To
      ; roll back the datom, this boolean needs to be inverted.
      (let [t
            (map
              (fn [[entity-id attribute value _ added?]]
                (if added?
                  [:db/retract entity-id attribute value]
                  [:db/add entity-id attribute value]))
              tx-data)]
        (transact t)))
    

    您可以通过首先捕获事务的返回值,然后将该返回值传递给回滚 fn 来使用它:

    (let [tx (transact [...])]
      (rollback tx))
    

    不过要小心,我是 datascript/Datomic 世界的新手,所以我可能缺少一些东西。

    【讨论】:

      猜你喜欢
      • 2021-06-01
      • 2013-10-24
      • 2014-04-12
      • 1970-01-01
      • 2010-10-01
      • 2010-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多