Firestore transaction with multiple get

前端 未结 3 1504
一个人的身影
一个人的身影 2021-02-13 05:13

I\'m trying to run a transaction with a variable number of read operations. I put the read () operations before than update ().

Reading the Firestore doc on https://clou

相关标签:
3条回答
  • 2021-02-13 05:40

    I couldn't figure out how to do this in pure Typescript, but I was able to find a JavaScript example that uses promises, so I adapted that to fit my needs. It seems to be working correctly, however when I run my function rapidly (by clicking on a button in rapid succession) I get console errors that read POST https://firestore.googleapis.com/v1beta1/projects/myprojectname/databases/(default)/documents:commit 400 (). I am unclear on whether those are errors I should be worried about, or if they're simply a a result of the transaction retrying. I posted my own question about that, and am hopeful to get some answers on it. In the meantime, here is the code that I came up with:

    async vote(username, recipeId, direction) {
    
      let value;
    
      if ( direction == 'up' ) {
        value = 1;
      }
    
      if ( direction == 'down' ) {
        value = -1;
      }
    
      // assemble vote object to be recorded in votes collection
      const voteObj: Vote = { username: username, recipeId: recipeId , value: value };
    
      // get references to both vote and recipe documents
      const voteDocRef = this.afs.doc(`votes/${username}_${recipeId}`).ref;
      const recipeDocRef = this.afs.doc('recipes/' + recipeId).ref;
    
      await this.afs.firestore.runTransaction( async t => {
    
        const voteDoc = await t.get(voteDocRef);
        const recipeDoc = await t.get(recipeDocRef);
        const currentRecipeScore = await recipeDoc.get('score');
    
        if (!voteDoc.exists) {
    
          // This is a new vote, so add it to the votes collection
          // and apply its value to the recipe's score
          t.set(voteDocRef, voteObj);
          t.update(recipeDocRef, { score: (currentRecipeScore + value) });
    
        } else {
    
          const voteData = voteDoc.data();
    
          if ( voteData.value == value ) {
    
            // existing vote is the same as the button that was pressed, so delete
            // the vote document and revert the vote from the recipe's score
            t.delete(voteDocRef);
            t.update(recipeDocRef, { score: (currentRecipeScore - value) });
    
          } else {
    
            // existing vote is the opposite of the one pressed, so update the
            // vote doc, then apply it to the recipe's score by doubling it.
            // For example, if the current score is 1 and the user reverses their
            // +1 vote by pressing -1, we apply -2 so the score will become -1.
            t.set(voteDocRef, voteObj);
            t.update(recipeDocRef, { score: (currentRecipeScore + (value*2))});
          }
    
        }
    
        return Promise.resolve(true);
    
      });
    
    }
    
    0 讨论(0)
  • 2021-02-13 05:46

    I was facing the same problem and decided to use a combination of a batched write and "normal" reads. The decision was guided by the fact that I needed to make many reads that did not rely on each other. At first I used a method similar to the one proposed by Derrick above, but it proved not sustainable for may reads. The code dictates that every loop is blocking to the next one. What I did was to batch all the reads to run in parallel with Promise.all The disadvantage of this is that you dont take advantage of transaction features, but since the field I was iterested in was not changing, it made sense Here's my sample code

    const batch = firestore().batch()
     const readPromises = invoiceValues.map(val => {
                            return orderCollection(omcId).where(<query field>, '<query operation>', <query>).get()
                        })
    
                        return Promise.all(readPromises).then(orderDocs => {
                      //Perform batch operations here
                            return batch.commit()
                         })
    

    This has proven to be more efficient for many reads, while remaining safe since the fields I'm interested in dont change

    0 讨论(0)
  • 2021-02-13 05:59

    The Firestore doc doesn't say this, but the answer is hidden in the API reference: https://cloud.google.com/nodejs/docs/reference/firestore/0.13.x/Transaction?authuser=0#getAll

    You can use Transaction.getAll() instead of Transaction.get() to get multiple documents. Your example will be:

    const reservationCol = this.db.firestore.collection('reservations');
    return this.db.firestore.runTransaction(t => {
      return t.getAll(reservationCol.doc('id1'), reservationCol.doc('id2'))
        .then(docs => {
          const id1 = docs[0];
          const id2 = docs[1];
          if (!(id1.exists && id2.exists)) {
            // do stuff
          } else {
            // throw error
          }
        })
    }).then(() => console.log('Transaction succeeded'));
    
    0 讨论(0)
提交回复
热议问题