为一个巨大的对象撤消重做

Undo redo for a huge object

我有一个要求,产品需要实现撤销-重做功能。

目前我持有一个来自单个 mongo 集合的巨大对象。

伪结构:

{
  cart:{
    products:[
      {
        name: "Watch",
        quantity: 1,
        shippingDate: 123456782,
        text: "lorem ipsum....",
        prices:[
          {
            currency: "USD",
            price: 300
          },
          {
            currency: "GBP",
            price: 220
          }
        ]
      }
    ],
    ...someMoreKeyValuePair
  }
}

现在可以在任何节点上进行更新,例如:产品获取 added/modified/removed、购物车级别数据获取 added/modified/removed。

并且结构非常庞大,现在我如何维护对整个结构发生的任何更改的撤消-重做操作。

我应该阅读哪些数据结构或设计模式以获得更好的解决方法。

更新:

undo-redo 需要保存在数据库中。我使用的语言是 Javascript-NodeJS

您可以使用 deep-diff,例如。

deep-diff is a javascript/node.js module providing utility functions for determining the structural differences between objects and includes some utilities for applying differences across objects.

因此您可以在版本之间创建差异,并将它们存储在您的数据库中:

const change = diff(oldValue, newValue);

要应用差异:

applyChange(target, source, change);

jsondiffpatch is another option. If you use Mongoose, you can implement a plugin for managing changes, or use an existing one: mongoose-diff-history

您需要保留更改日志。因此,您的服务器应用程序 (nodejs) 处理的客户端请求应转换为 both 的数据库操作 记录更改(像撤销日志一样)并在一个事务中执行它。

然后实施逻辑以在适当的时候清除撤消日志,从该日志中弹出一个操作并执行反向操作,...等等

这样的撤消日志中的一条记录将包含 3 到 4 个元素:

  1. 一个动作:“更新”、“插入”或“删除”。后两者主要用于表示数组操作。
  2. 用于标识应在对象中应用更改的位置的路径。该路径将是一个属性数组,例如可以将其编码为以点分隔的字符串。
  3. 该位置的旧值(在“更新”或“删除”的情况下),JSON 编码。
  4. 该位置的新值(在“更新”或“插入”的情况下),JSON 编码。

此类记录的示例:

  • ("update", "cart.products.0.name", '"Watch"', '"Watching"')
  • ("delete", "cart.products.prices.1", '{"currency":"GBP","price":220}', null):这表示从 prices 数组的索引 1 中删除旧值的操作。间隙通过移动数组值来填补,很像 JavaScript.
  • 中的 splice(index, 1) 操作
  • ("insert", "cart.products.prices.0", null, '{"currency":"EUR","price":270}'):这表示将新值插入到 prices 数组中的索引 0 处的操作。如果该索引已经有一个值,它将向右移动,就像 JavaScript.
  • 中的 splice(index, 0, newvalue) 操作一样

null 值仅表示此参数与该特定操作无关。

以上示例日志将累积到以下数据,从您提供的示例数据开始:

{
  cart:{
    products:[
      {
        name: "Watching",
        quantity: 1,
        shippingDate: 123456782,
        text: "lorem ipsum....",
        prices:[
          {
            currency: "EUR",
            price: 270
          },
          {
            currency: "USD",
            price: 300
          }
        ]
      }
    ]
  }
}

这样的日志包含撤消操作的所有内容:如果数据被擦除(“更新”、“删除”),您可以从第三个参数恢复被擦除的数据。