如何阻止Firestore删除类Cloud Function触发其他云函数?
解决Firestore删除云函数的递归触发问题
在Create/Update类型的云函数中,我们可以通过给文档添加preventFurtherTriggers字段,阻止后续云函数被触发,示例代码如下:
exports.onUpdateAll = functions.firestore .document("content/{authorId}") .onUpdate(async (snapshot, context) => { const data = snapshot.data(); const newEventData = Object.assign(data, { preventFurtherTriggers: true, }); admin.firestore().collection("all").doc(authorId).collection("events").doc(eventId).update(newEventData); });
exports.onUpdateEvent = functions.firestore .document("all/{authorId}/events/{eventId}") .onUpdate(async (change, context) => { const newValue = change.after.data(); if (newValue.preventFurtherTriggers === true) return; console.log("递归函数已停止!"); });
我目前有三个由客户端触发的删除类云函数:
- 删除特定组下所有用户的所有事件;
- 仅删除单个用户的事件;
- 删除组及该组下所有用户的所有事件。
问题在于,当执行onDeleteEventsForOneUser等函数删除事件时,会触发onDeleteEventForAllUsers函数,导致不必要的递归执行。想知道删除类云函数能否实现类似更新操作的逻辑——传递判断参数,阻止后续删除云函数的执行?
相关删除云函数代码如下:
exports.onDeleteEventForAllUsers = functions.firestore .document("all/{authorId}/userevents/{eventId}") .onDelete(async (snapshot, context) => { const data = snapshot.data(); const eventid = context.params.eventId; const eventingroup = data.eventingroup; const allContentFollowersRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", eventingroup) .where("eventid", "==", eventid); const querySnapshot = await allContentFollowersRef.get(); querySnapshot.forEach((doc) => { doc.ref.delete(data); }); });
exports.onDeleteEventsForOneUser = functions.firestore .document("following/{groupId}/followinggroup/{userId}") .onDelete(async (snapshot, context) => { const groupId = context.params.groupId; const userId = context.params.userId; const contentInMapRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", groupId); const userContentPathRef = admin .firestore() .collection("all") .doc(userId) .collection("userevents"); const contentQuerySnapshot = await contentInMapRef.get(); contentQuerySnapshot.forEach((doc) => { if (doc.exists) { userContentPathRef.doc(doc.id).delete(); } }); });
exports.onDeleteGroupAndEvents = functions.firestore .document("group/{groupId}") .onDelete(async (snapshot, context) => { const groupId = context.params.groupId; const groupsRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", groupId); const querySnapshot = await groupsRef.get(); querySnapshot.forEach((doc) => { if (doc.exists) { doc.ref.delete(); } }); });
解决方案
删除类云函数可以通过临时添加标记字段+批量操作的方式实现触发控制,核心思路是:在删除文档前,先给文档添加preventFurtherTriggers标记,再执行删除;被触发的onDelete函数读取删除前的文档数据,若存在标记则直接退出,不执行后续逻辑。
修改后的代码示例
- 修改
onDeleteEventsForOneUser函数:
exports.onDeleteEventsForOneUser = functions.firestore .document("following/{groupId}/followinggroup/{userId}") .onDelete(async (snapshot, context) => { const groupId = context.params.groupId; const userId = context.params.userId; const contentInMapRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", groupId); const userContentPathRef = admin .firestore() .collection("all") .doc(userId) .collection("userevents"); const contentQuerySnapshot = await contentInMapRef.get(); // 使用批量操作,先添加阻止触发标记,再删除文档 const batch = admin.firestore().batch(); contentQuerySnapshot.forEach((doc) => { if (doc.exists) { const docRef = userContentPathRef.doc(doc.id); batch.update(docRef, { preventFurtherTriggers: true }); batch.delete(docRef); } }); await batch.commit(); });
- 修改
onDeleteEventForAllUsers函数,添加标记判断:
exports.onDeleteEventForAllUsers = functions.firestore .document("all/{authorId}/userevents/{eventId}") .onDelete(async (snapshot, context) => { const data = snapshot.data(); // 检查是否存在阻止触发标记,有则直接返回 if (data?.preventFurtherTriggers === true) return; const eventid = context.params.eventId; const eventingroup = data.eventingroup; const allContentFollowersRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", eventingroup) .where("eventid", "==", eventid); const querySnapshot = await allContentFollowersRef.get(); // 同样使用批量操作添加标记后删除,避免触发其他函数 const batch = admin.firestore().batch(); querySnapshot.forEach((doc) => { batch.update(doc.ref, { preventFurtherTriggers: true }); batch.delete(doc.ref); }); await batch.commit(); });
- 修改
onDeleteGroupAndEvents函数:
exports.onDeleteGroupAndEvents = functions.firestore .document("group/{groupId}") .onDelete(async (snapshot, context) => { const groupId = context.params.groupId; const groupsRef = admin .firestore() .collectionGroup("userevents") .where("eventingroup", "==", groupId); const querySnapshot = await groupsRef.get(); const batch = admin.firestore().batch(); querySnapshot.forEach((doc) => { if (doc.exists) { batch.update(doc.ref, { preventFurtherTriggers: true }); batch.delete(doc.ref); } }); await batch.commit(); });
原理说明
Firestore的onDelete触发器会保留文档删除前的最后状态(即snapshot.data()能获取到删除前的所有字段)。我们通过批量操作先给文档添加preventFurtherTriggers标记,再执行删除,这样被触发的onDelete函数就能读取到该标记,直接退出,从而避免递归触发其他删除函数。
内容的提问来源于stack exchange,提问作者Redstain
相关产品推荐
相关产品推荐

