【问题标题】:Set new document after transaction in firebase admin在firebase admin中交易后设置新文档
【发布时间】:2021-11-17 11:51:31
【问题描述】:

我正在创建一个 API 来检查计数器值,递增 1,然后在文档集合中创建新文档。为此,我使用runTransaction()

我遇到了问题。事务按预期工作,检查计数器值,然后将其加一。但在那之后,当我尝试设置新文档时,我得到错误(空对象),我不能这样做。我认为我使用的逻辑可能很糟糕,所以我需要你的建议。

const db = admin.firestore()

const couterRef = db.collection('invoices').doc('invoices_doc')

let counterValue = 0

    // check last invoice counter number and increment by 1
    return db.runTransaction((transaction) => {
      return transaction
        .get(couterRef)
        .then((counter) => {
          counterValue = counter.data().counter + 1
          // Update counter
          return transaction.update(couterRef, { counter: counterValue })
        })
        .catch((error) => response.status(200).send({ status: 'TRANSACTION ERROR', error }))
    })
      .then(() => {
        // Create new invoice document
        couterRef.collection('invoices').doc(counterValue).set({
           series: 'SS',
           series_nr: counterValue
        })
        .then(() => response.status(200).send({ status: 'OK' }))
        .catch((error) => response.status(200).send({ status: 'DOCUMENT SET ERROR', error }))
      })
      .catch((error) => {
        response.status(200).send({ status: 'RUN TRANSACTION ERROR', error })
      })

【问题讨论】:

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


    【解决方案1】:

    我没有测试过您的代码,但问题很可能来自 您在事务函数中修改了应用程序状态,这是您必须避免的事情。文档中有一个关于这个问题的specific section

    您需要将counterValue的新值从您的事务函数中传递出去,如下:

      const db = admin.firestore();
      const couterRef = db.collection('invoices').doc('invoices_doc');
    
      // let counterValue = 0  Remove this line
    
      return db
        .runTransaction((transaction) => {
          return transaction.get(couterRef).then((counter) => {
            const counterValue = counter.data().counter + 1;
              // See the changes below !!
            transaction.update(couterRef, { counter: counterValue });  // Actually, this in not an asynchronous operation
            return counterValue;  // We pass the new value of counterValue out of the transaction function
          });
        })
        .then((counterValue) => {
          // Create new invoice document
          couterRef.collection('invoices').doc(counterValue.toString(10)).set({
            series: 'SS',   // you missed a ,
            series_nr: counterValue,
          });
        })
        .then(() => response.status(200).send({ status: 'OK' }))
        .catch((error) => {
          response.status(200).send({ status: 'RUN TRANSACTION ERROR', error });
        });
    

    另外,在您的 HTTPS 云函数中,不要将响应从事务内发送回客户端:这也是应用程序状态修改,不应在事务内完成。

    同样,不要在then 块中包含catch 块:在promise chain 的末尾添加一个catch仅一次。如果你想在这个独特的catch 块中处理不同的错误类型,只需抛出带有不同错误消息的错误,并在catch 块中根据消息决定做什么。或者,您可以创建 Error 类的一些子类。


    说了这么多,由于您的事务只影响一个文档并且只增加一个计数器,您可以很好地使用FieldValue.increment() 方法,它是原子的。有关详细信息,请参阅此 Firebase blog post

    【讨论】:

    • 感谢 Renaud,您让我的代码更加简洁。并且在某种程度上帮助我找到了一个真正的原因,为什么我在尝试创建新文档时总是出错。我发现主要问题是counterValue 变量。这个变量给我一个number而不是string,所以你不能用数字设置新文档,你必须先把它改成字符串。
    • 很高兴能帮到你!确实,您必须将其转换为字符串,因为我没有测试代码,所以我没有抓住这个;-) 我会相应地调整答案。
    猜你喜欢
    • 2016-10-06
    • 1970-01-01
    • 2021-04-06
    • 2018-09-12
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    相关资源
    最近更新 更多