如何检测Firebase Firestore中是否存在文档,不存在则创建新文档
Firestore 检测文档存在性并创建新文档的正确实现
原代码的问题点
- 用
snapshotChanges()做实时监听,但这里只需要单次查询,没必要保持监听,用get()更高效,还不用手动unsubscribe() - 查询后直接创建存在并发漏洞:查询到文档不存在后,可能在执行创建操作前,有其他客户端插入了相同
nome的文档,导致重复创建
方案一:基础版(低并发场景适用)
用单次查询判断文档是否存在,不存在则创建,代码更简洁高效:
async create(doc: any, collection: string): Promise<void> { try { // 执行单次查询,匹配指定nome的文档 const querySnapshot = await this.db.collection(collection, ref => ref.where('nome', '==', doc.nome) ).get().toPromise(); if (querySnapshot.empty) { // 无匹配文档,创建新文档 const res = await this.db.collection(collection).add({ ...doc }); console.log('创建成功,文档ID:', res.id); } else { console.log('该文档已存在,第一个匹配文档ID:', querySnapshot.docs[0].id); } } catch (err) { console.error('操作出错:', err); } }
关键优化点
- 用
get()替代snapshotChanges(),通过toPromise()转为Promise,配合async/await让代码逻辑更清晰 - 用
querySnapshot.empty判断是否有匹配文档,比判断数组长度更直观 - 统一用
try/catch捕获错误,避免分散的then/catch
方案二:高并发安全版
如果你的场景存在多客户端同时操作的可能,需要保证操作的原子性,避免重复创建,推荐以下两种方式:
子方案1:用唯一字段作为文档ID
如果nome是全局唯一的标识,可以直接把它设为文档ID,这样创建时天然避免重复:
async createWithUniqueId(doc: any, collection: string): Promise<void> { try { const docRef = this.db.collection(collection).doc(doc.nome); const docSnapshot = await docRef.get().toPromise(); if (!docSnapshot.exists) { await docRef.set({ ...doc }); console.log('文档创建成功'); } else { console.log('该文档已存在'); } } catch (err) { console.error('操作出错:', err); } }
更简洁的写法,直接用set()的默认行为(文档存在则报错):
async createWithUniqueId(doc: any, collection: string): Promise<void> { try { // merge: false 是默认值,文档存在时会抛出错误 await this.db.collection(collection).doc(doc.nome).set({ ...doc }, { merge: false }); console.log('文档创建成功'); } catch (err) { if ((err as any).code === 'already-exists') { console.log('该文档已存在'); } else { console.error('操作出错:', err); } } }
子方案2:事务(复杂场景适用)
如果不能用nome作为文档ID,用事务保证查询和创建操作的原子性,中间不会被其他操作打断:
async createWithTransaction(doc: any, collection: string): Promise<void> { await this.db.firestore.runTransaction(async (transaction) => { // 构建查询条件 const query = this.db.collection(collection).ref.where('nome', '==', doc.nome); const querySnapshot = await transaction.get(query); if (querySnapshot.empty) { // 创建新文档 const newDocRef = this.db.collection(collection).ref.doc(); transaction.set(newDocRef, { ...doc }); console.log('文档创建成功,ID:', newDocRef.id); } else { console.log('该文档已存在,第一个匹配ID:', querySnapshot.docs[0].id); // 抛出自定义错误终止事务 throw new Error('文档已存在'); } }).catch((err) => { // 只处理非自定义错误 if (err.message !== '文档已存在') { console.error('事务执行失败:', err); } }); }
内容的提问来源于stack exchange,提问作者xandeq
相关产品推荐
相关产品推荐

