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

Firestore:获取自动生成文档ID以创建/更新文档数组的更佳方案

优化Firebase Firestore用户书签文档的实现方案

问题描述

我为每位用户创建一个文档存储书签ID,用addDoc让Firebase自动生成文档ID。现在需要判断该文档是否存在,来决定创建新文档还是更新原有文档,但获取自动生成ID的方法太繁琐,现有代码能运行但不够优雅。另外我明确每个用户只有一个这类文档,有没有更高效获取doc.id的方法?

原实现代码

//Add a value to the bookmarks array for individual user
export const addBookmarkForUser = async (userAuth, showId) => {
  const bookmarkDocRef = collection(db, 'users', userAuth.uid, 'bookmarks')
  const bookmarkSnapshot = await getDocs(bookmarkDocRef)
  let userBookmarkDocId
  if(bookmarkSnapshot){   
    bookmarkSnapshot.forEach((doc) => {
      if(doc.id){
        userBookmarkDocId = doc.id
        console.log(userBookmarkDocId)
      }
    })    
  }  
  try {    
    if(!bookmarkSnapshot) {
      await addDoc((collection(db, 'users', userAuth.uid, 'bookmarks'), {
        favorites: [{showId: showId}],
      }))
    }else {
      const userBookmarkRef = doc(db, 'users', userAuth.uid, 'bookmarks', userBookmarkDocId)
      await updateDoc(userBookmarkRef, {
        favorites: arrayUnion({showId: showId})
      })}
    
  } catch (error) {
    console.log('Error creating bookmark', error.message);
  }
};

Firestore结构说明

  • 根集合为users,每个用户对应一个以自身uid为ID的文档
  • 用户文档下包含子集合bookmarks,该子集合内每个用户仅有一个自动生成ID的文档,文档内存储favorites数组,数组元素为包含showId的对象

最优解决方案:使用固定文档ID替代自动生成ID

既然每个用户只有一个书签文档,完全没必要用Firebase自动生成的随机ID,直接给这个文档设置固定ID(比如user-favorites),可以彻底跳过查询集合找文档ID的步骤,直接定位文档操作,效率和代码简洁度都会大幅提升。

方案1:固定ID直接操作文档(推荐)

直接指向users/{uid}/bookmarks/user-favorites这个固定文档,用setDoc配合merge: true参数,既可以创建新文档(不存在时),也可以更新现有文档的favorites数组,无需额外判断存在性:

import { doc, setDoc, arrayUnion } from "firebase/firestore";

export const addBookmarkForUser = async (userAuth, showId) => {
  try {
    // 直接定位到固定ID的书签文档
    const bookmarkDocRef = doc(db, 'users', userAuth.uid, 'bookmarks', 'user-favorites');
    // merge: true 确保存在则更新,不存在则创建
    await setDoc(bookmarkDocRef, {
      favorites: arrayUnion({ showId: showId })
    }, { merge: true });
  } catch (error) {
    console.log('Error updating bookmark', error.message);
  }
};

如果需要确保首次创建时favorites数组不为空,可以拆分逻辑:

import { doc, getDoc, setDoc, updateDoc, arrayUnion } from "firebase/firestore";

export const addBookmarkForUser = async (userAuth, showId) => {
  try {
    const bookmarkDocRef = doc(db, 'users', userAuth.uid, 'bookmarks', 'user-favorites');
    const docSnapshot = await getDoc(bookmarkDocRef);
    
    if (!docSnapshot.exists()) {
      // 首次创建,初始化数组
      await setDoc(bookmarkDocRef, {
        favorites: [{ showId: showId }]
      });
    } else {
      // 更新现有文档的数组
      await updateDoc(bookmarkDocRef, {
        favorites: arrayUnion({ showId: showId })
      });
    }
  } catch (error) {
    console.log('Error updating bookmark', error.message);
  }
};

方案2:保留自动生成ID的简化写法

如果一定要保留自动生成的文档ID,也可以简化ID获取逻辑——因为每个用户只有一个文档,直接取查询结果的第一个文档即可,无需遍历集合:

import { collection, getDocs, addDoc, doc, updateDoc, arrayUnion } from "firebase/firestore";

export const addBookmarkForUser = async (userAuth, showId) => {
  try {
    const bookmarksCollectionRef = collection(db, 'users', userAuth.uid, 'bookmarks');
    const bookmarksSnapshot = await getDocs(bookmarksCollectionRef);
    
    // 直接取第一个文档(每个用户仅存一个)
    const existingDoc = bookmarksSnapshot.docs[0];
    
    if (!existingDoc) {
      // 无文档则创建新的
      await addDoc(bookmarksCollectionRef, {
        favorites: [{ showId: showId }]
      });
    } else {
      // 有文档则更新
      await updateDoc(doc(db, 'users', userAuth.uid, 'bookmarks', existingDoc.id), {
        favorites: arrayUnion({ showId: showId })
      });
    }
  } catch (error) {
    console.log('Error updating bookmark', error.message);
  }
};

固定ID方案的优势

  • 减少查询开销:无需先查询整个bookmarks集合,直接定位目标文档,节省Firestore读取次数和网络请求
  • 代码更简洁:避免繁琐的ID遍历逻辑,业务逻辑清晰易懂
  • 结构更合理:完全匹配“每个用户对应一个书签文档”的业务规则,集合结构直观

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 05:05:29