Firebase云函数:事务函数不返回promise



以下是我尝试使用firebase云函数:

-听取"用户"集合下某个文档中的任何更改。

-在"评论"one_answers"帖子"集合中的相关文档中更新用户信息的碳副本。

因为我需要查询相关文档并立即更新它们,所以我正在为交易操作编写代码。

这是我写的代码。它返回错误消息"Function return 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"方法返回了一个promise?我可能错过了一些重要的东西,但我去年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()方法返回的Promise。但是,您的代码中还有其他几个问题。

使用Node.js服务器SDK,您确实可以将查询传递给事务的get()方法(使用JavaScriptSDK则无法(。但是,在您的情况下,您希望更新两个查询返回的文档。不能调用两次db.runTransaction(),因为它不再是唯一的事务。

因此,您需要使用getAll()方法,通过传递DocumentReferences的未封装数组。(再次注意,此getAll()方法仅在Node.js Server SDK中可用,而在JavaScript SDK中不可用(。

以下代码将完成此操作。

我们运行这两个查询,并将结果转换为一个DocumentReferences数组。然后我们调用runTransaction()方法,并使用扩展运算符来解压缩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 })
})
})
})
})
});

最后两点:

  1. 我不确定db.collection('comment').where('userinfo', '==', olduserinfo);是否有效,因为olduserinfo是通过change.before.data()获得的。您可能需要指定一个字段。这可能与newuserinfo相同
  2. 请注意,您不能在事务中执行doc.ref.update(),您需要调用事务的update()方法,而不是DocumentReference方法

最新更新