如何基于子集合权限查询Firestore的project集合文档?
最优解决方案:构建用户权限反向索引
核心思路是给每个用户单独维护一个「可访问项目」的索引集合,彻底规避sharedWith数组的弊端,同时解决无法查询协作者项目的问题。
具体实现步骤
1. 创建反向索引集合
新建userProjects集合,结构如下:
userProjects - <用户UID> accessibleProjects - <项目ID> (文档可空,或仅存项目名称/创建时间等轻量化元数据) - <项目ID>
每个用户的accessibleProjects子集合里,仅存储该用户有权访问的所有项目ID(包括自己作为作者和协作者的项目)。
2. 自动维护索引(用云函数触发)
通过Firebase云函数自动同步权限变化,避免手动操作出错:
- 项目创建时:当
project集合新增文档,触发云函数,在作者的userProjects/<作者UID>/accessibleProjects下添加该项目ID的文档。 - 添加协作者时:当
collaborators子集合新增用户文档,触发云函数,在该协作者的userProjects/<协作者UID>/accessibleProjects下添加对应项目ID的文档。 - 移除协作者时:当
collaborators子集合删除用户文档,触发云函数,删除该协作者userProjects里对应的项目ID文档。 - 项目删除时:当
project文档被删除,触发云函数,先查询该项目的collaborators子集合获取所有协作者UID,再批量删除作者+所有协作者userProjects里对应的项目ID文档。
3. 查询用户可访问的所有项目
先从userProjects获取当前用户的所有可访问项目ID,再批量拉取对应的项目详情:
// 获取当前用户UID const currentUid = auth.currentUser.uid; // 查询可访问的项目ID列表 const accessibleProjectsRef = collection(db, `userProjects/${currentUid}/accessibleProjects`); const snapshot = await getDocs(accessibleProjectsRef); // 批量获取项目详情 const projectIds = snapshot.docs.map(doc => doc.id); const projectRefs = projectIds.map(id => doc(db, "project", id)); const projectsSnapshot = await getAll(...projectRefs); // 处理结果 const projects = projectsSnapshot.map(doc => ({ id: doc.id, ...doc.data() }));
为什么这个方案优于sharedWith数组?
- 隐私保护:每个用户只能看到自己有权访问的项目,不会暴露其他协作者的列表。
- 无数据量限制:用子集合存储项目ID,哪怕协作者数量上千也不会有数组长度或性能问题。
- 避免更新冲突:添加/移除协作者时,只操作单个用户的
userProjects文档,不会同时修改原项目文档,彻底解决并发更新竞争问题。 - 查询高效:直接查询用户自己的索引集合,无需过滤全量项目,完全符合Firestore的查询规则要求。
补充:保留原权限规则
原有的Firestore规则可以继续保留,用来确保直接访问单个项目文档时的权限校验,防止越权访问:
function isLoggedIn() { return request.auth != null; } function canReadProject(projectId) { return exists(/databases/$(database)/documents/project/$(projectId)/collaborators/$(request.auth.uid)); } allow read: if isLoggedIn() && (request.auth.uid == resource.data.author || canReadProject(resource.id));
内容的提问来源于stack exchange,提问作者pewpewlasers
相关产品推荐
相关产品推荐

