Firebase RTD,原子式“;移动“。。。从两处删除并添加“;表格;?
在Firebase实时数据库中,这是一种非常常见的事务处理方式Firebase RTD,原子式“;移动“。。。从两处删除并添加“;表格;?,firebase,firebase-realtime-database,Firebase,Firebase Realtime Database,在Firebase实时数据库中,这是一种非常常见的事务处理方式 “表”A-将其视为“待定” “表”B-将其视为“结果” 某些状态发生时,您需要将项目从A“移动”到B 所以,我的意思是,这很可能是一个云函数在做这件事 显然,此操作必须是原子的,并且必须防止赛道效应等等 因此,对于项目123456,您必须做三件事 读A/123456/ 删除A/123456/ 将值写入B/123456 都是原子的,带锁的 简而言之,Firebase实现这一目标的方法是什么 现在已经有了非常棒的ref.tra
- “表”A-将其视为“待定”
- “表”B-将其视为“结果”
- 读A/123456/
- 删除A/123456/
- 将值写入B/123456
- 现在已经有了非常棒的ref.transaction系统,但我认为它在这里并不重要
- 也许是以一种反常的方式使用触发器
对于在谷歌上搜索的任何人来说,值得注意的是令人难以置信的新Firestore(很难想象有什么比传统Firebase更令人难以置信,但你有它……,新Firestore系统内置了
这个问题是关于好的传统Firebase实时的。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
然后,您应该将两个词典合并(如何在此处执行:)为一个,我们称之为finalDict
,
然后您可以更新这些值,这两个表都将被更新,从A中删除并“移动到”B
实时数据库中没有“表”,所以我将使用术语“位置”来表示包含一些子节点的路径
实时数据库无法在两个不同的位置进行原子化的事务处理。执行事务时,必须选择单个位置,并且只能在该位置下进行更改
您可能认为可以在数据库的根上进行事务处理。这是可能的,但在数据库中任何位置的并发非事务写入操作面前,这些事务可能会失败。这是一项要求,即在事务发生的位置的任何地方都不得有非事务性写入。换句话说,如果您希望在某个位置进行交易,则所有客户机都必须在该位置进行交易,并且没有交易的客户机都不能在该位置进行写操作
如果您在数据库的根目录下进行事务处理,那么这个规则肯定会有问题,因为客户端可能在没有事务的情况下到处写入数据。因此,如果您想执行原子“移动”,您必须让所有客户机在移动的公共根位置始终使用事务,或者接受您不能真正以原子方式执行此操作。Gustavo的回答允许使用单个API调用进行更新,该调用要么成功,要么失败。而且,由于它不必使用事务,因此争用问题要少得多。它只是从要移动的键加载值,然后写入一个更新 问题是有人可能同时修改了数据。因此,您需要使用安全规则来捕获这种情况并拒绝它。因此,配方变成:
update()
调用中删除旧位置时,将值写入其新位置“key1”:“value1”,
“键2”:“值2”
我们想将value1
从key1
移动到key3
,然后Gustavo的方法将发送以下JSON:
ref.update({
“key1”:空,
“键3”:“值1”
})
何时可以使用以下规则轻松验证此操作:
“.validate”:
!data.child(“key3”).exists()&&
!newData.child(“key1”).exists()&&
newData.child(“key3”).val()==data.child(“key1”).val()
"
简言之:
中当前没有值key3
- 更新后,
中没有值key1
的新值是key3
key1
key1
和key3
。为了防止硬编码,我们可以将密钥添加到update语句中:
ref.update({
_fromKey:“key1”,
_托克伊:“钥匙3”,
键1:null,
键3:“值1”
})
不同的是,我们添加了两个具有已知名称的键,以指示移动的源和目标。现在有了这个结构,我们就有了所需的所有信息,我们可以通过以下方式验证移动:
“.validate”:
!data.child(newData.child(''u toKey').val()).exists()&&
!newData.child(newData.child(“'u fromKey').val()).exists()&&
newData.child(newData.child('u toKey').val()).val()==data.child(newData.child('u fromKey').val()).val()
"
读起来有点长,但每一行的意思都和以前一样
在客户端代码中,我们将执行以下操作:
功能移动(从、到){
ref.child(from).一次(“值”).然后(函数(快照){
var value=snapshot.val();
更新={
_fromKey:from,
_托克伊:到
};
更新[from]=null;
更新[到]=值;
ref.update(updates).catch(函数(){
//更新失败,请稍候
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