Firebase从父集合访问子集合的最佳实践咨询
Firebase 数据访问最佳实践建议
针对你的场景,以下几个方向可以解决问题,同时符合NoSQL的设计原则:
1. 预聚合进度数据(优先推荐,适配进度条需求)
因为你需要展示「已完成任务数/总任务数」的进度条,完全不需要每次都查询所有任务来计算。可以在里程碑文档中新增两个字段:
totalTasks:该里程碑下的总任务数completedTasks:已完成的任务数
每次新增、删除或修改任务状态时,同时更新对应里程碑的这两个字段(用Firebase的事务或批量写入保证数据一致性)。这样获取里程碑数据时,直接就能算出进度,完全不需要查询tasks集合,大幅减少请求量。
如果还需要展示任务列表,再单独批量查询任务(见下文)。
2. 批量并行查询任务集合
如果必须获取所有任务的详细数据,不用逐个发起请求,而是利用Promise.all并行执行多个查询:
- 先获取指定项目下的所有里程碑,记录每个里程碑的
MILESTONE_ID - 对每个里程碑,构造对应的tasks集合路径:
projects/{PROJECT_ID}/milestones/{MILESTONE_ID}/tasks - 用
Promise.all并行发起所有tasks集合的查询请求 - 将查询到的任务数据合并到对应的里程碑对象中
Firebase支持HTTP/2多路复用,并行请求的性能开销远低于串行请求,实际体验和单次请求差异不大。示例代码逻辑(以JavaScript为例):
async function getMilestonesWithTasks(projectId) { // 获取所有里程碑 const milestonesSnapshot = await db.collection(`projects/${projectId}/milestones`).get(); // 并行查询所有任务集合并合并数据 const milestones = await Promise.all( milestonesSnapshot.docs.map(async (milestoneDoc) => { const milestoneData = milestoneDoc.data(); const tasksSnapshot = await db.collection(`projects/${projectId}/milestones/${milestoneDoc.id}/tasks`).get(); const tasks = tasksSnapshot.docs.map(taskDoc => taskDoc.data()); return { ...milestoneData, tasks }; }) ); return milestones; }
3. 调整数据结构(适合任务量不大的场景)
如果你的每个里程碑下任务数量不多(比如少于几百个),可以考虑将tasks直接作为里程碑文档中的数组字段存储。这样获取里程碑时就能一次性拿到所有任务数据,无需额外查询。但要注意Firebase单文档大小限制为1MB,若任务数量或内容过大,不建议采用这种方式。
关于Collection Group Query的说明
Collection Group Query是用来查询所有同名子集合的,比如查询所有项目下的所有tasks。但你需要的是指定项目下的里程碑对应的tasks,直接用它无法过滤项目维度,所以不太适合你的场景。
内容的提问来源于stack exchange,提问作者Taylor Foster
相关产品推荐
相关产品推荐

