在Firebase实时数据库中,这是一个非常常见的事务性事件
某些状态发生,您需要将项目从A“移动”到B。
所以,我当然认为这可能是一个云功能。
显然,此操作必须是原子,并且必须防止赛道效应等等。
因此,对于项目123456,您必须做三件事
所有原子,带锁。
简而言之,Firebase实现这一目标的方式是什么?
已经有了很棒的ref.transaction系统,但我不认为它与此相关。
也许以变态的方式使用触发器?
IDK
对于在网上搜索的人来说,值得注意的是令人难以置信的新Firestore(很难想象有什么东西比传统的Firebase更令人难以置信,但是你有它......),新的Firestore系统内置 .......
这个问题是关于旧的传统Firebase Realtime。
答案 0 :(得分:4)
Gustavo的回答允许使用单个API调用进行更新,该调用可以完成成功或失败。而且由于它不必使用事务,因此争用问题要少得多。它只是加载它想要移动的键的值,然后写一个更新。
问题是有人可能在此期间修改了数据。因此,您需要使用安全规则来捕获这种情况并拒绝它。所以食谱变成了:
update()
调用中删除旧位置这样做本质上是使用客户端代码和(有些公认的棘手的)安全规则重新实现Firebase数据库事务。
为了能够做到这一点,更新变得有点棘手。假设我们有这种结构:
"key1": "value1",
"key2": "value2"
我们希望将value1
从key1
移至key3
,然后Gustavo的方法会发送此JSON:
ref.update({
"key1": null,
"key3": "value1"
})
何时可以使用以下规则轻松验证此操作:
".validate": "
!data.child("key3").exists() &&
!newData.child("key1").exists() &&
newData.child("key3").val() === data.child("key1").val()
"
用语言说:
key3
目前没有值。key1
没有值key3
的新值是key1
这很有效,但遗憾的是我们在规则中对key1
和key3
进行了硬编码。为了防止对它们进行硬编码,我们可以将密钥添加到更新语句中:
ref.update({
_fromKey: "key1",
_toKey: "key3",
key1: null,
key3: "value1"
})
不同之处在于我们添加了两个具有已知名称的键,以指示移动的源和目的地。现在有了这个结构,我们可以获得所需的所有信息,我们可以通过以下方式验证移动:
".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()
"
阅读时间稍长,但每行仍然与以前相同。
在我们的客户代码中:
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,console
答案 1 :(得分:0)
Firebase可与字典,a.k.a,键值对配合使用。要在同一个事务中更改多个表中的数据,您可以获得基本引用,并使用包含“所有指令”的字典,例如在Swift中:
let reference = Database.database().reference() // base reference
let tableADict = ["TableA/SomeID" : NSNull()] // value that will be deleted on table A
let tableBDict = ["TableB/SomeID" : true] // value that will be appended on table B, instead of true you can put another dictionary, containing your values
然后您应该合并(如何在此处执行:How do you add a Dictionary of items into another Dictionary)将两个词典合并为一个,我们称之为finalDict
,
然后你可以更新这些值,并且两个表都将被更新,从A中删除并“移动到”B
reference.updateChildValues(finalDict) // update everything on the same time with only one transaction, w/o having to wait for one callback to update another table
答案 2 :(得分:0)
实时数据库中没有“表”,因此我将使用术语“位置”来引用包含一些子节点的路径。
实时数据库无法在两个不同的位置进行原子事务处理。当您执行交易时,您必须选择一个位置,并且您只能在该单个位置进行更改。
您可能认为您只能在数据库的根目录进行交易。这是可能的,但是面对数据库中任何地方的并发非事务写入操作,这些事务可能会失败。要求在事务发生的位置任何地方都不得进行非事务性写入。换句话说,如果您想在某个地点进行交易,那么所有客户都必须在那里进行交易,没有客户可以在没有交易的情况下在那里进行交易。
如果您在数据库的根目录进行交易,那么这条规则肯定会有问题,客户端可能在没有事务的情况下在整个地方编写数据。因此,如果您想执行原子“移动”,您必须让所有您的客户端始终在移动的公共根位置使用事务,或者接受您不能以原子方式做到这一点。