You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 14:12:47