Firebase RTD,原子“移动" ...从两个“表"中删除并添加? [英] Firebase RTD, atomic "move" ... delete and add from two "tables"?

查看:53
本文介绍了Firebase RTD,原子“移动" ...从两个“表"中删除并添加?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在Firebase实时数据库中,这是您很常见的事务

In Firebase Realtime Database, it's a pretty common transactional thing that you have

  • 表格" A-认为它是待定"
  • 表格" B-认为是结果"

某些状态发生了,您需要将项目从A移动"到B.

Some state happens, and you need to "move" an item from A to B.

所以,我当然是说这可能是一个云功能.

So, I certainly mean this would likely be a cloud function doing this.

很明显,此操作必须是 atomic (原子的),并且必须防止赛道效果等等.

Obviously, this operation has to be atomic and you have to be guarded against racetrack effects and so on.

因此,对于项目123456,您必须做三件事

So, for item 123456, you have to do three things

  • 阅读A/123456/
  • 删除A/123456/
  • 将值写入B/123456

全部具有锁定功能.

简而言之,Firebase实现此目的的方法是什么?

In short what is the Firebase way to achieve this?

  • 已经有很棒的ref.transaction系统,但是我认为这与这里无关.

  • There's already the awesome ref.transaction system, but I don't think it's relevant here.

也许以扭曲的方式使用触发器?

Perhaps using triggers in a perverted manner?

IDK

对于在这里搜索的任何人来说,值得注意的是令人难以置信的新型Firestore(很难想象有什么比传统的Firebase令人难以置信的了,但是在那里……),新型Firestore系统内置 .......

Just for anyone googling here, it's worth noting that the mind-boggling new Firestore (it's hard to imagine anything being more mind-boggling than traditional Firebase, but there you have it...), the new Firestore system has built-in .......

这个问题是关于良好的传统Firebase Realtime.

This question is about good old traditional Firebase Realtime.

推荐答案

Gustavo的答案允许通过单个API调用进行更新,该API调用要么成功要么失败.而且由于不必使用事务,因此争用问题更少.它只是从要移动的键中加载值,然后编写单个更新.

Gustavo's answer allows the update to happen with a single API call, which either complete succeeds or fails. And since it doesn't have to use a transaction, it has much less contention issues. It just loads the value from the key it wants to move, and then writes a single update.

问题在于,与此同时,可能有人修改了数据.因此,您需要使用安全规则来捕获并拒绝这种情况.因此,配方变为:

The problem is that somebody might have modified the data in the meantime. So you need to use security rules to catch that situation and reject it. So the recipe becomes:

  1. 读取源节点的值
  2. 在单个update()调用中删除旧位置的同时将值写入新位置
  3. 安全规则通过接受或拒绝操作来验证操作
  4. 如果被拒绝,客户将从#1重试
  1. read the value of the source node
  2. write the value to its new location while deleting the old location in a single update() call
  3. the security rules validate the operation, either accepting or rejecting it
  4. if rejected, the client retries from #1

这样做实质上是使用客户端代码和(有些棘手的)安全规则重新实现Firebase数据库事务.

Doing so essentially reimplements Firebase Database transactions with client-side code and (some admittedly tricky) security rules.

为了能够做到这一点,更新变得有些棘手.假设我们具有以下结构:

To be able to do this, the update becomes a bit more tricky. Say that we have this structure:

"key1": "value1",
"key2": "value2"

我们想将value1key1移到key3,然后Gustavo的方法将发送此JSON:

And we want to move value1 from key1 to key3, then Gustavo's approach would send this JSON:

ref.update({
  "key1": null,
  "key3": "value1"
})

何时可以使用以下规则轻松验证此操作:

When can easily validate this operation with these rules:

".validate": "
    !data.child("key3").exists() && 
    !newData.child("key1").exists() &&
    newData.child("key3").val() === data.child("key1").val()
"

换句话说:

  • key3当前没有值.
  • 更新后key1中没有任何值
  • key3的新值是key1
  • 的当前值
  • There is currently no value in key3.
  • There is no value in key1 after the update
  • The new value of key3 is the current value of key1

这很好用,但是不幸的是,这意味着我们在规则中对key1key3进行了硬编码.为了防止对它们进行硬编码,我们可以将密钥添加到我们的更新语句中:

This works great, but unfortunately means that we're hardcoding key1 and key3 in our rules. To prevent hardcoding them, we can add the keys to our update statement:

ref.update({
  _fromKey: "key1",
  _toKey: "key3",
  key1: null,
  key3: "value1"
})

不同之处在于,我们添加了两个具有已知名称的键,以指示移动的源和目的地.现在,有了这种结构,我们便拥有了所需的所有信息,并且可以通过以下方式验证此举:

The different is that we added two keys with known names, to indicate the source and destination of the move. Now with this structure we have all the information we need, and we can validate the move with:

".validate": "
    !data.child(newData.child('_toKey').val()).exists() && 
    !newData.child(newData.child('_fromKey').val()).exists() &&
    newData.child(newData.child('_toKey').val()).val() === data.child(newData.child('_fromKey').val()).val()
"

读取时间稍长,但每一行的含义仍与以前相同.

It's a bit longer to read, but each line still means the same as before.

在客户端代码中,我们将这样做:

And in the client code we'd do:

function move(from, to) {
  ref.child(from).once("value").then(function(snapshot) {
    var value = snapshot.val();
    updates = {
      _fromKey: from,
      _toKey: to
    };
    updates[from] = null;
    updates[to] = value;
    ref.update(updates).catch(function() {
      // the update failed, wait half a second and try again
      setTimeout(function() {
        move(from, to);
      }, 500);
    });
}
move ("key1", "key3");

如果您想使用这些规则的代码,请查看: https: //jsbin.com/munosih/edit?js,控制台

If you feel like playing around with the code for these rules, have a look at: https://jsbin.com/munosih/edit?js,console

这篇关于Firebase RTD,原子“移动" ...从两个“表"中删除并添加?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆