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

Firestore新增成就文档时如何避免生成重复副本

首先你的现有代码存在逻辑偏差:achievements 是集合引用,调用 get() 返回的是集合查询快照,只有文档快照才有 exists 属性,集合快照判断是否为空用的是 empty 属性,你当前的 !snapshot.exists 永远为真,所以每次调用都会触发 add() 逻辑,这是重复创建的直接诱因。此外你的原有逻辑是判断用户的整个成就集合是否为空,而非判断待发放的单个成就是否已存在,本身不符合需求。

针对避免成就重复添加的需求,有两种成熟解决方案:

方案1:手动指定固定文档ID(更推荐)

每个成就预设唯一的固定标识(比如英文slug、数字ID),直接作为文档ID使用,从根源上避免重复。可以用Firestore的create()方法,该方法仅当目标文档不存在时才会写入,文档已存在时会直接抛出错误,完全规避重复写入风险。

const GiveAchievementTest = async () => {
  // 给当前成就指定唯一固定ID,示例为测试成就分配test_achievement作为唯一标识
  const achievementRef = firestore
    .collection("users")
    .doc(user.uid)
    .collection("achievements")
    .doc('test_achievement'); // 手动指定文档ID
  try {
    // create方法仅文档不存在时写入,已存在会抛出already-exists错误
    await achievementRef.create({
      title: 'Test Achievement',
      reward: 200,
      desc: 'Testing Function'
    });
    console.log('Gave Achievement');
  } catch (error) {
    // 文档已存在的错误直接忽略即可
    if (error.code === 'already-exists') {
      console.log('Achievement already granted');
      return;
    }
    console.log('Error in giving achievement to user', error);
  }        
}

该方案优势:

  • 仅需要1次读写操作,性能远优于先全量查询的方案
  • 天然支持并发场景,就算同一时间多次调用接口,也不会生成重复文档
  • 逻辑简洁,不需要额外维护查询条件和索引

方案2:先查询匹配字段判断是否存在

如果你的成就属于动态生成、没有预设唯一ID的场景,可以先查询对应字段(比如title)判断成就是否已发放,再决定是否写入。注意需要给查询的字段提前建立索引,高并发场景下建议配合事务使用,避免查询和写入的间隙有其他请求写入导致重复。

const GiveAchievementTest = async () => {
  const achievementsRef = firestore
    .collection("users")
    .doc(user.uid)
    .collection("achievements");
  try {
    // 查询是否已有相同title的成就
    const querySnapshot = await achievementsRef
      .where('title', '==', 'Test Achievement')
      .limit(1) // 仅查询1条即可,优化性能
      .get();
    if (querySnapshot.empty) {
      // 没有匹配记录,新增成就
      await achievementsRef.add({
        title: 'Test Achievement',
        reward: 200,
        desc: 'Testing Function'
      });
      console.log('Gave Achievement');
    } else {
      console.log('Achievement already granted');
    }
  } catch (error) {
    console.log('Error in giving achievement to user', error);
  }        
}

内容的提问来源于stack exchange,提问作者Demonix

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 21:21:01