I am new to firebase cloud functions and I want to update username
field of some documents from posts
collection when the users
collection changes it username
field of a particular document.
I use the following code to do that:
exports.updateProfileUsername = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) =>
{
const {userId} = context.params;
var newUsername = change.after.data().username;
var previousUsername = change.before.data().username;
if (newUsername.localeCompare(previousUsername) !== 0)
{
let postCollectionRef = db.collection('posts');
let postQuery = postCollectionRef.where('userId', '==', `${userId}`);
return new Promise((resolve, reject) =>
{
updateUsernameDocuments(postQuery, reject, newUsername);
});
}
});
function updateUsernameDocuments(query, reject, newValue)
{
query.get()
.then((snapshot) =>
{
if (snapshot.size === 0)
{
return 0;
}
return snapshot.docs.forEach((doc) =>
{
doc.ref.update({username : `${newValue}`});
});
}).catch(reject);
}
This code works fine. usernames in posts
collection are changing correctly. But, after some time, the cloud functions log shows this log : Function execution took 60002 ms, finished with status: 'timeout'
. How to solve that?
And will this function be a problem if i have to update millions of docs in posts
collection?
The problem comes from the fact that you are not returning the Promise returned by the update()
method, therefore the Cloud Function is not informed that the work is done and runs up to the timeout.
What may also happen, if you have to update "millions of docs in posts
collection", is that the Cloud Function ends before your updates are all done. This is more annoying!
I would suggest you watch the 3 videos titled "Learn JavaScript Promises" from the Firebase video series which explain this key point of returning Promises for background triggered functions.
The following code should work. Note that I have used a batched write, which is especially dedicated to multiple write operations.
exports.updateProfileUsername = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const { userId } = context.params;
var newUsername = change.after.data().username;
var previousUsername = change.before.data().username;
if (newUsername.localeCompare(previousUsername) !== 0) {
const postCollectionRef = db.collection('posts');
const postQuery = postCollectionRef.where('userId', '==', `${userId}`);
return postQuery.get()
.then(querySnapshot => {
if (querySnapshot.empty) {
return null;
} else {
let batch = db.batch();
querySnapshot.forEach(doc => {
batch.update(doc.ref, { username: `${newUsername}` });
});
return batch.commit();
}
});
} else {
return null;
}
});
Note that a batched write can contain up to 500 operations. If you plan to update more than 500 documents, you may use Promise.all()
instead, as follows:
exports.updateProfileUsername = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
const { userId } = context.params;
var newUsername = change.after.data().username;
var previousUsername = change.before.data().username;
if (newUsername.localeCompare(previousUsername) !== 0) {
const postCollectionRef = db.collection('posts');
const postQuery = postCollectionRef.where('userId', '==', `${userId}`);
return postQuery.get()
.then(querySnapshot => {
if (querySnapshot.empty) {
return null;
} else {
const promises = []
querySnapshot.forEach(doc => {
promises.push(doc.ref.update({ username: `${newUsername}` }));
});
return Promise.all(promises);
}
});
} else {
return null;
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With