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
相关产品推荐
相关产品推荐

