Cloud Functions中'Duplicate key 'PostStatus''错误及子集合更新问题
问题1:解决Firestore批量更新中
Duplicate key 'PostStatus'错误 错误原因
你在对象字面量中重复定义了PostStatus键,JavaScript不允许对象存在重复键;同时逻辑顺序存在问题——TimestampArrival(到达时间)必然晚于TimestampDeparture(出发时间),所以"Completed"状态的判断优先级应该高于"Started"。
修正后的代码
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); const database = admin.firestore(); const min = "* * * * *"; exports.scheduled = functions.pubsub.schedule(min).onRun(async (context) => { const batch = database.batch(); const { docs } = await database.collection("ADSGHANA").get(); // 改用for...of处理异步逻辑(为后续问题2的异步操作做准备) for (const doc of docs) { const { TimestampArrival, TimestampDeparture, TimeToDelete, PostStatus, } = doc.data(); const now = admin.firestore.Timestamp.now(); const starting = now > TimestampDeparture; const ending = now > TimestampArrival; const shouldDelete = now > TimeToDelete; // 避免使用JS保留字"delete" // 调整状态判断优先级:先判断完成,再判断开始,最后保留原状态 const updatedPostStatus = ending ? "Completed" : starting ? "Started" : PostStatus; batch.set( database.doc(`ADSGHANA/${doc.id}`), { Timestamp: shouldDelete ? TimestampDeparture : now, PostStatus: updatedPostStatus, }, { merge: true } ); } await batch.commit(); });
关键修改点
- 移除重复的
PostStatus键,用嵌套三元表达式处理状态优先级,确保"Completed"状态优先触发 - 将变量名
delete改为shouldDelete,避免使用JavaScript保留字导致语法问题 - 把
docs.map改为for...of循环,确保后续异步子集合操作能正确等待执行
问题2:触发条件时更新子集合指定文档
实现思路
当now > TimestampDeparture条件满足时,查询当前文档的子集合,过滤出AdStatus不在["Passenger cancelled","Rejected"]范围内的文档,将这些文档的AdStatus批量更新为"Started",继续使用Firestore批量操作优化性能。
整合后的完整代码
const functions = require("firebase-functions"); const admin = require("firebase-admin"); admin.initializeApp(); const database = admin.firestore(); const min = "* * * * *"; exports.scheduled = functions.pubsub.schedule(min).onRun(async (context) => { const batch = database.batch(); const { docs } = await database.collection("ADSGHANA").get(); for (const doc of docs) { const { TimestampArrival, TimestampDeparture, TimeToDelete, PostStatus, } = doc.data(); const now = admin.firestore.Timestamp.now(); const starting = now > TimestampDeparture; const ending = now > TimestampArrival; const shouldDelete = now > TimeToDelete; // 更新主文档的PostStatus const updatedPostStatus = ending ? "Completed" : starting ? "Started" : PostStatus; batch.set( database.doc(`ADSGHANA/${doc.id}`), { Timestamp: shouldDelete ? TimestampDeparture : now, PostStatus: updatedPostStatus, }, { merge: true } ); // 满足出发条件时,更新子集合文档 if (starting) { // 替换为你实际使用的子集合名称 const subCollectionRef = database .collection("ADSGHANA") .doc(doc.id) .collection("subCollectionName"); // 查询符合条件的子集合文档 const subDocsSnapshot = await subCollectionRef .where("AdStatus", "not-in", ["Passenger cancelled", "Rejected"]) .get(); // 将子集合文档的更新操作加入批量任务 subDocsSnapshot.docs.forEach(subDoc => { batch.update(subDoc.ref, { AdStatus: "Started" }); }); } } await batch.commit(); });
关键实现细节
- 使用
not-in操作符对应Flutter中的whereNotIn,过滤出不符合排除状态的文档 - 所有更新操作(主文档+子集合文档)加入同一个批量任务,减少Firestore请求次数
- 在
for...of循环中等待子集合查询的异步操作,确保所有更新都被正确加入批量任务 - 替换代码中的
subCollectionName为你实际使用的子集合名称
内容的提问来源于stack exchange,提问作者EBENEZER OCANSEY
相关产品推荐
相关产品推荐

