【问题标题】:Firebase Cloud Functions: Transactions function not returning promise?Firebase Cloud Functions:交易功能不返回承诺?
【发布时间】:2020-04-21 13:07:55
【问题描述】:

这是我尝试使用 firebase 云功能做的事情:

-监听“用户”集合下的文档之一的任何变化。

-更新'comment'和'post'集合中相关文档中用户信息的副本。

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

这是我写的代码。它返回错误消息,'函数返回未定义,预期的承诺或值'。

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 的出色回答,我创建了以下代码以防有人需要它。 事务的复杂性在于,相同的数据可能存储在不同的索引下或以不同的格式存储。例如相同的“地图”变量可以存储在一个集合的索引下,也可以作为另一个集合的一部分存储。在这种情况下,查询返回的每个文档都需要不同的更新方法。

我使用 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**)
                    }
                })
            })
    })

希望这会有所帮助!

【问题讨论】:

    标签: javascript node.js firebase google-cloud-functions


    【解决方案1】:

    序言:这是一个非常有趣的用例!

    错误消息标识的问题来自于您没有返回runTransaction() 方法返回的Promise。但是,您的代码中还有其他几个问题。

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

    因此,您需要通过传递 DocumentReferences 的解压缩数组来使用 getAll() 方法。 (再次注意,getAll() 方法仅在 Node.js 服务器 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 的方法。

    【讨论】:

      猜你喜欢
      • 2017-08-31
      • 2019-03-04
      • 2018-05-24
      • 2019-07-21
      • 2018-08-13
      • 2020-03-21
      • 2017-09-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多