下面是我正在尝试使用firebase云功能所做的事情:
-监听“用户”集合下的文档中的任何更改。
-更新“评论”和“发布”集合中相关文档中用户信息的副本。
因为我将需要在相关文档中进行查询并立即更新,所以我正在编写事务操作的代码。
这是我写的代码。它返回错误消息“Function returned undefined,expected Promise or value”。
exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change,context) => {
const olduserinfo=change.before.data();
const newuserinfo=change.after.data();
db.runTransaction(t=>{
return t.get(db.collection('comment').where('userinfo','==',olduserinfo))
.then((querysnapshot)=>{
querysnapshot.forEach((doc)=>{
doc.ref.update({userinfo:newuserinfo})
})
})
})
.then(()=>{
db.runTransaction(t=>{
return t.get(db.collection('post').where('userinfo','==',olduserinfo))
.then((querysnapshot)=>{
querysnapshot.forEach((doc)=>{
doc.ref.update({userinfo:newuserinfo})
})
})
})
})
});
我有点困惑,因为据我所知,'update'方法返回一个承诺?我可能错过了一些重要的东西,但我是去年11月才开始编程的,所以不要太苛刻。:)
有什么建议可以解决这个问题吗?谢谢!
编辑:基于Renaud的出色回答,我创建了下面的代码,以防有人可能需要它。事务处理的复杂之处在于,相同的数据可能以不同的索引或不同的格式存储。例如,相同的“map”变量可以存储在一个集合的索引下,而在另一个集合中作为数组的一部分。在这种情况下,查询返回的每个文档都需要不同的更新方法。
我使用doc.ref.path、split和switch方法解决了这个问题。这允许根据集合名称应用不同的更新方法。简单来说,就是这样的东西:
return db.runTransaction(t => {
return t.getAll(...refs)
.then(docs => {
docs.forEach(doc => {
switch (doc.ref.path.split('/')[0]) { //This returns the collection name and switch method assigns a relevant operation to be done.
case 'A':
t = t.update(doc.ref, **do whatever is needed for this collection**)
break;
case 'B':
t = t.update(doc.ref, **do whatever is needed for this collection**)
break;
default:
t = t.update(doc.ref, **do whatever is needed for this collection**)
}
})
})
})
希望这有帮助!
序言:这是一个非常有趣的用例!!
错误消息标识的问题来自这样一个事实,即您没有返回runtransaction()
方法返回的承诺。但是,您的代码中还有其他几个问题。
使用node.js服务器SDK,您确实可以向事务的get()
方法传递查询(使用JavaScript SDK则不行)。但是,在您的情况下,您希望更新由两个查询返回的文档。您不能两次调用db.runtransaction()
,因为它不再是唯一的事务。
因此您需要通过传递一个未打包的DocumentReferences
数组来使用getAll()
方法。(再次注意,这个getAll()
方法仅在node.js服务器SDK中可用,而在JavaScript SDK中不可用)。
下面的代码将完成这一任务。
我们运行这两个查询,并将结果转换为documentreferences
的一个数组。然后我们调用runtransaction()
方法,使用spread运算符解压缩documentreferences
的数组,并将其传递给getAll()
方法。
然后,我们循环这些文档,并将调用链接到事务的update()
方法,因为它返回事务。
但是,请注意,使用这种方法,如果两个原始查询之一的结果在事务期间发生更改,则事务将看不到任何新的或删除的文档。
exports.useInfoUpdate = functions.firestore.document('user/{userid}').onUpdate((change, context) => {
const olduserinfo = change.before.data();
const newuserinfo = change.after.data();
const db = admin.firestore();
const q1 = db.collection('comment').where('userinfo', '==', olduserinfo); // See the remark below: you probably need to use a document field here (e.g. olduserinfo.userinfo)
const q2 = db.collection('post').where('userinfo', '==', olduserinfo);
return Promise.all([q1.get(), q2.get()])
.then(results => {
refs = [];
results.forEach(querySnapshot => {
querySnapshot.forEach(documentSnapshot => {
refs.push(documentSnapshot.ref);
})
});
return db.runTransaction(t => {
return t.getAll(...refs)
.then(docs => {
docs.forEach(doc => {
t = t.update(doc.ref, { userinfo: newuserinfo })
})
})
})
})
});
最后两句话:
db.collection('comment').where('userinfo','==',olduserinfo);
是否有效,因为olduserinfo
是通过change.before.data()
获得的。您可能需要指定一个字段。对于newuserinfo
doc.ref.update()
,您需要调用事务的update()
方法,而不是DocumentReference
的方法。