Firebase云函数:删除用户时关联帖子无法删除问题排查
场景与需求
拥有两个Firestore集合,数据结构如下:
/root /users (collection) /user_id_a (document with fields) - user_id_a - user_name_aa /my_post_list (collection) /first_list (document with fields) - actual_list // 包含post_id_c /posts (collection) /post_id_c (document with fields) - post_id_c - post_title - posted_by - created_at
需求:删除user_id_a文档时,自动删除该用户发布的所有post_id_c文档及其子集合。
现有云函数代码
export const onUserDeleted_v0 = onDocumentDeleted( "users/{userUid}", async (event) => { // Get an object representing the document const snapshot = event.data!; if (!snapshot) { console.log("No data associated with the event"); return; } const userData = snapshot.data(); const docFirstListSnapshot = await admin.firestore() .collection('users') .doc(event.params.userUid) // 对应user_id_a .collection('my_post_list') .doc('first_list').get(); console.log("docFirstListSnapshot is: " + util.inspect(docFirstListSnapshot)); if (docFirstListSnapshot.exists) { const documentIdsList = docFirstListSnapshot.data()!.actual_list; for (var index in documentIdsList) { const ref = admin.firestore() .collection('posts').doc(documentIdsList[index]) await admin.firestore().recursiveDelete(ref); } } else { console.log("failed to get my post document ids list of " + event.params.userUid); } });
日志信息
docFirstListSnapshot is: DocumentSnapshot { _fieldsProto: undefined, _ref: DocumentReference { _firestore: Firestore { _settings: [Object], _settingsFrozen: true, _serializer: [Serializer], _projectId: 'my-project-name', _databaseId: '(default)', registeredListenersCount: 0, bulkWritersCount: 0, _backoffSettings: [Object], _clientPool: [ClientPool], _gax: [Object] }, _path: ResourcePath { segments: [Array] }, _converter: { toFirestore: [Function: toFirestore], fromFirestore: [Function: fromFirestore] } }, _serializer: Serializer { createReference: [Function (anonymous)], createInteger: [Function (anonymous)], allowUndefined: false }, _readTime: Timestamp { _seconds: 1717121572, _nanoseconds: 3938000 }, _createTime: undefined, _updateTime: undefined }
用户疑问
通过util.inspect看到docFirstListSnapshot有内容,但docFirstListSnapshot.exists返回false,该需求是否可实现?遗漏了什么?
问题分析与解决
需求可行性
该需求完全可实现,Firebase云函数结合Firestore的recursiveDelete方法可以批量删除文档及其子集合。
核心问题
目标文档实际不存在
日志中_fieldsProto: undefined、_createTime: undefined、_updateTime: undefined明确说明users/{userUid}/my_post_list/first_list文档不存在,exists: false是Firestore对不存在文档的标准返回。util.inspect显示的是DocumentSnapshot对象的结构,而非文档实际存储的数据。数组遍历方式错误
使用for (var index in documentIdsList)会遍历数组的索引值,而非数组中的post_id元素,会导致后续删除错误的文档。查询逻辑不够可靠
依赖已删除用户文档下的子集合查询,不如直接从onDocumentDeleted事件提供的用户文档快照中获取数据(如果将actual_list存储在用户文档字段中),避免子集合文档不存在的风险。
修正方案
方案一:将帖子ID列表存储在用户文档字段中(推荐)
直接从删除事件的快照中获取帖子ID,无需额外查询子集合:
export const onUserDeleted_v1 = onDocumentDeleted( "users/{userUid}", async (event) => { const snapshot = event.data; if (!snapshot) { console.log("No data associated with the event"); return; } const userData = snapshot.data(); const documentIdsList = userData?.actual_list; if (!documentIdsList || !Array.isArray(documentIdsList)) { console.log("No valid post IDs found for user " + event.params.userUid); return; } // 并行删除所有帖子,提升效率 const deletePromises = documentIdsList.map(postId => { const postRef = admin.firestore().collection('posts').doc(postId); return admin.firestore().recursiveDelete(postRef); }); await Promise.all(deletePromises); console.log("Successfully deleted all posts for user " + event.params.userUid); } );
方案二:保留子集合存储方式(需确保子集合文档存在)
如果坚持使用子集合存储帖子ID,需先确认first_list文档已正确创建,同时修正遍历逻辑:
export const onUserDeleted_v2 = onDocumentDeleted( "users/{userUid}", async (event) => { const userUid = event.params.userUid; const firstListRef = admin.firestore() .collection('users') .doc(userUid) .collection('my_post_list') .doc('first_list'); const docFirstListSnapshot = await firstListRef.get(); if (!docFirstListSnapshot.exists) { console.log("my_post_list/first_list does not exist for user " + userUid); return; } const documentIdsList = docFirstListSnapshot.data()?.actual_list; if (!documentIdsList || !Array.isArray(documentIdsList)) { console.log("No valid post IDs list for user " + userUid); return; } // 并行执行删除操作 const deleteTasks = documentIdsList.map(postId => { return admin.firestore().recursiveDelete(admin.firestore().collection('posts').doc(postId)); }); await Promise.all(deleteTasks); console.log("Successfully deleted all posts for user " + userUid); } );
额外检查点
- 确认用户发布帖子时,
actual_list字段已正确写入对应的文档(用户文档或子集合文档) - 云函数需配置足够的超时时间(批量删除大量文档时)
内容的提问来源于stack exchange,提问作者foseja

