Firestore Aggregation - Fanout and Update Cloud Function 文档分发与同步实现问询
Firestore 文档分发同步实现方案
原始文档参考结构:
{ name: "ABC", image: "https://.....g.png", duplicateTo: ["someID1", "someID2", "someID3"] }
1. 文档内容分发操作实现
核心通过 Firebase Cloud Functions 的 onCreate + onUpdate 触发器实现,逻辑如下:
- 触发后提取原始文档中除
duplicateTo外需要分发的业务字段 - 遍历
duplicateTo内的目标文档ID,通过 Firestore 批量写入接口完成分发 - 单批次最多支持500次操作,超过量级可拆分为多批次处理
示例核心代码:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); const db = admin.firestore(); // 触发器同时覆盖文档创建和更新场景 exports.syncDocToTargets = functions.firestore .document("源集合名/{docId}") .onWrite(async (change, context) => { // 提取要同步的载荷,排除duplicateTo字段 const rawData = change.after.data(); const { duplicateTo = [], ...syncPayload } = rawData || {}; // 如果没有要同步的目标ID直接返回 if (duplicateTo.length === 0) return null; // 初始化批量操作对象 let batch = db.batch(); let operationCount = 0; for (const targetId of duplicateTo) { const targetRef = db.doc("目标集合名/" + targetId); // 用merge: true避免覆盖目标文档其他字段 batch.set(targetRef, syncPayload, { merge: true }); operationCount++; // 达到批量操作上限时提交并新建批次 if (operationCount === 500) { await batch.commit(); batch = db.batch(); operationCount = 0; } } // 提交剩余操作 if (operationCount > 0) await batch.commit(); return null; });
2. onUpdate 触发器能否感知 duplicateTo 数组的删除变更
可以。onUpdate(上文中的onWrite已包含onUpdate能力)触发器会返回变更前后的两份文档快照:
change.before存储更新前的文档全量数据change.after存储更新后的文档全量数据
通过对比两个快照中duplicateTo数组的差集,即可精准拿到被删除的ID列表。
3. 同步删除目标文档对应数据的实现
可以实现,只需在上述逻辑基础上补充差集计算和删除逻辑即可:
- 计算变更前后
duplicateTo数组的差集,得到被移除的目标ID列表 - 对每个被移除的ID对应的目标文档,执行对应字段删除或整文档删除操作
- 所有读写操作可合并到同一个批量任务中,保证事务一致性
补充后的完整逻辑代码:
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); const db = admin.firestore(); const FieldValue = admin.firestore.FieldValue; exports.syncDocToTargets = functions.firestore .document("源集合名/{docId}") .onWrite(async (change, context) => { // 文档被删除时的特殊处理:如果需要同步删除所有目标文档可在此处补充逻辑 if (!change.after.exists) { const beforeDuplicate = change.before.get("duplicateTo") || []; let batch = db.batch(); let count = 0; for (const id of beforeDuplicate) { const ref = db.doc("目标集合名/" + id); // 按需选择删除整文档或者删除对应字段 // batch.delete(ref); batch.update(ref, { name: FieldValue.delete(), image: FieldValue.delete() }); if (++count === 500) { await batch.commit(); batch = db.batch(); count = 0; } } if (count > 0) await batch.commit(); return null; } const rawData = change.after.data(); const { duplicateTo: afterDuplicate = [], ...syncPayload } = rawData || {}; const beforeDuplicate = change.before.get("duplicateTo") || []; // 计算新增的需要同步的ID、被移除的需要删除数据的ID const toAddIds = afterDuplicate.filter(id => !beforeDuplicate.includes(id)); const toRemoveIds = beforeDuplicate.filter(id => !afterDuplicate.includes(id)); if (toAddIds.length === 0 && toRemoveIds.length === 0) return null; let batch = db.batch(); let operationCount = 0; // 处理新增同步 for (const targetId of toAddIds) { const targetRef = db.doc("目标集合名/" + targetId); batch.set(targetRef, syncPayload, { merge: true }); if (++operationCount === 500) { await batch.commit(); batch = db.batch(); operationCount = 0; } } // 处理移除删除 for (const targetId of toRemoveIds) { const targetRef = db.doc("目标集合名/" + targetId); // 按需选择删除对应字段或者删除整文档 batch.update(targetRef, { name: FieldValue.delete(), image: FieldValue.delete() }); // batch.delete(targetRef); if (++operationCount === 500) { await batch.commit(); batch = db.batch(); operationCount = 0; } } if (operationCount > 0) await batch.commit(); return null; });
注意事项
- 部署前需确保 Cloud Functions 服务账号拥有目标集合的读写权限
- 若
duplicateTo数组量级过千,建议配合 Cloud Tasks 做异步分片处理,避免函数超时 - 若目标文档存在多个来源的同步数据,可在分发时增加来源文档ID标记,避免误删其他来源的字段
内容的提问来源于stack exchange,提问作者boorock34
相关产品推荐
相关产品推荐

