我是Firebase云功能的新手,我想在username
集合更改特定文档的posts
字段时,从users
集合更新某些文档的username
字段文献。
我使用以下代码来做到这一点:
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);
}
此代码可以正常工作。 posts
集合中的用户名正确更改。但是,一段时间后,云功能日志将显示以下日志:Function execution took 60002 ms, finished with status: 'timeout'
。怎么解决呢?
如果我必须更新posts
集合中的数百万个文档,此功能是否会有问题?
答案 0 :(得分:4)
问题源于您没有返回update()
方法返回的Promise的事实,因此没有通知Cloud Function工作已完成并超时。
如果您必须更新“ posts
集合中的数百万个文档”,则云功能也会在完成之前结束。更烦人了!
我建议您观看Firebase video series上名为“学习JavaScript承诺”的3个视频,这些视频解释了为后台触发的函数返回承诺的关键点。
以下代码应该可以工作。请注意,我使用了batched write,它专门用于多重写入操作。
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;
}
});
请注意,批量写入最多可以包含500个操作。如果您打算更新500多个文档,则可以改为使用Promise.all()
,如下所示:
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;
}
});