TL;DR Should I use Transaction.getAll()
or use a for loop and update the documents one by one using Transaction.get()
consider the following schema:
- someTopLevelCollection
- accountId1
- projects
- tasks
- users
- accountId2
- projects
- tasks
- users
Whenever a project/task is created in projects, tasks sub collection respectively, counters in users collection are updated say, projectsCount and tasksCount.
The reference to the users are kept inside the project and tasks as array of userIds as follows:
Note: other fields removed for sake of brevity
project/tasks structure:
{
"name": "someName",
"status": "someStatus",
"users": [
"userId1",
"userId2",
....
],
...
}
Now I need to update say a counter for all the userIds inside the users array using the transaction.
Method 1:
const accountId = context.params.accountId;
const userIds = snapshot.data().users;
userIds.forEach(userId => {
const userDocRef = db.collection(someTopLevelCollection)
.doc(accountId)
.collection('users')
.doc(userId);
let transaction = db.runTransaction(transaction => {
return transaction.get(userDocRef)
.then(doc => {
const snapshotData = doc.data();
let newCounterValue = snapshotData[counterName] + 1;
transaction.update(userDocRef, {counterName: newCounterValue});
return Promise.resolve(`Incremented ${counterName} to ${newCounterValue}`);
});
}).then(result => {
console.log('Transaction success!', result);
return true;
}).catch(err => {
console.error('Transaction failure:', err);
return false;
});
});
Method 2:
const accountId = context.params.accountId;
const userIds = snapshot.data().users;
let userDocRefs = [];
userIds.forEach(userId => {
const userDocRef = db.collection(someTopLevelCollection)
.doc(accountId)
.collection('users')
.doc(userId);
userDocRefs.push(userDocRef)
});
let transaction = db.runTransaction(transaction => {
return transaction.getAll(userDocRefs)
.then(docs => {
docs.forEach(doc => {
const snapshotData = doc.data();
let newCounterValue = snapshotData[counterName] + 1;
transaction.update(doc.ref, {counterName: newCounterValue});
});
return Promise.resolve('Completed transaction successfully');
});
}).then(result => {
console.log('Transaction success!', result);
return true;
}).catch(err => {
console.error('Transaction failure:', err);
return false;
});
The following are my questions:
- when a document change occurs externally when
getAll()
is under execution, are all the documents fetched again to maintain consistency. If so, what's the use case to use getAll()? - When a transaction is run one by one using the for loop and a document change occurs externally to the current document being modified in the transaction, is the transaction retried just for that document?
Thanks.