【问题标题】:Firestore: How can I use a transaction to read and then write to a document that I don't have a reference to?Firestore:如何使用事务读取然后写入我没有参考的文档?
【发布时间】:2020-08-01 20:57:26
【问题描述】:

我有一个文档,我用这样的查询获取:

var myPromise = db.collection("games").where("started", "==", false).orderBy("created").limit(1).get()

我需要创建一个从该文档读取然后写入的事务。 例如(取自文档):

// Create a reference to the SF doc.
var sfDocRef = db.collection("cities").doc("SF");

// Uncomment to initialize the doc.
// sfDocRef.set({ population: 0 });

return db.runTransaction(function(transaction) {
    // This code may get re-run multiple times if there are conflicts.
    return transaction.get(sfDocRef).then(function(sfDoc) {
        if (!sfDoc.exists) {
            throw "Document does not exist!";
        }

        // Add one person to the city population.
        // Note: this could be done without a transaction
        //       by updating the population using FieldValue.increment()
        var newPopulation = sfDoc.data().population + 1;
        transaction.update(sfDocRef, { population: newPopulation });
    });
}).then(function() {
    console.log("Transaction successfully committed!");
}).catch(function(error) {
    console.log("Transaction failed: ", error);
});

我想用 myPromise 变量替换 sfDocRef 变量,但我不能,因为一个是文档引用,另一个是承诺。 如何在 myPromise 代表的文档上创建事务?

【问题讨论】:

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


    【解决方案1】:

    一旦您实际执行该查询,该参考就很容易获得。你可以简单地:

    1. 按现在的方式执行查询
    2. 在查询结果中找到DocumentSnapshot(与处理查询结果时通常一样)
    3. 使用 DocumentSnapshot 的ref 属性获取文档的引用,即DocumentReference
    4. 在事务中使用该引用。

    【讨论】:

    • 我好像在阅读同一个文档两次。一次是等待评估承诺,一次是当我“获取”事务中的特定文档时。有没有一种方法可以让我只使用一个网络请求而不是两个?
    • transaction.get() 的文档说您可以将引用或查询传递给它。尝试将查询传递给它。老实说,我不知道它是否会更快,实际上可能会更慢,因为您必须为事务锁定文档,这意味着它可以执行多次。 googleapis.dev/nodejs/firestore/latest/Transaction.html#get
    【解决方案2】:

    您需要等待承诺解决,例如通过附加then 回调:

    myPromise.then((querySnapshot) => {
      let ref = querySnapshot.documents[0].ref;
      return db.runTransaction(function(transaction) {
        return transaction.get(ref).then(function(sfDoc) {
            if (!sfDoc.exists) {
                throw "Document does not exist!";
            }
            var newPopulation = sfDoc.data().population + 1;
            transaction.update(sfDocRef, { population: newPopulation });
        });
      }).then(function() {
        console.log("Transaction successfully committed!");
      }).catch(function(error) {
        console.log("Transaction failed: ", error);
      });
    });
    

    这个querySnapshot.documents[0].ref 假设只有一个匹配的文档,或者您只关心第一个文档。如果您关心的内容可能更多,则需要遍历查询快照中的文档。

    您仍然需要get 事务中的特定文档如果(且仅当)您希望事务确保该文档在查询和写入时未修改交易。如果不需要,您可以使用QuerySnapshot 中的QueryDocumentSnapshot

    【讨论】:

    • 好像我在阅读同一个文档两次。一次是等待评估承诺,一次是当我“获取”事务中的特定文档时。有没有一种方法可以让我只使用一个网络请求而不是两个?
    • 不。查询是获取引用所必需的,但不能成为事务的一部分,因此在这种情况下您需要两次读取。这就是强烈建议在代码中跟踪文档 ID 的原因之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 2018-09-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多