如何在Firestore事务中执行多Get操作实现去重数据写入?
嘿,我懂你现在的困扰——手里有一堆带重复条目的本地JSON,要确保每条数据只写入Firestore一次,官方文档说事务能搞定多Get后再操作,但示例大多是单Get场景,不想用一堆嵌套的then()把代码搞成“回调地狱”对吧?其实用现代异步写法完全能解决这个问题,让代码清爽又易读。
核心思路
我们要做的是:先对本地JSON做初步去重(减少事务里的操作量),然后在Firestore事务中批量检查所有待写入数据对应的文档是否存在,不存在的才写入。关键是用async/await配合Promise.all来替代嵌套then(),实现并行的多Get操作。
示例代码(Admin SDK为例)
假设你的JSON数据里每个条目都有一个唯一id(用来作为Firestore文档ID,或者你可以替换成业务唯一键):
const admin = require('firebase-admin'); admin.initializeApp(); const db = admin.firestore(); // 你的原始JSON数据(带重复条目) const rawJsonData = [ { id: 'user_001', name: 'Alice', email: 'alice@example.com' }, { id: 'user_002', name: 'Bob', email: 'bob@example.com' }, { id: 'user_001', name: 'Alice', email: 'alice@example.com' }, // 重复条目 // ... 更多数据 ]; // 第一步:本地初步去重,用Map根据id过滤重复项 const uniqueLocalData = [...new Map(rawJsonData.map(item => [item.id, item])).values()]; async function writeUniqueDocsToFirestore() { try { // 启动Firestore事务,事务函数用async/await写法 await db.runTransaction(async (transaction) => { // 1. 生成所有待检查的文档引用 const docReferences = uniqueLocalData.map(item => db.collection('users').doc(item.id)); // 2. 用Promise.all并行获取所有文档快照,无需嵌套then() const docSnapshots = await Promise.all(docReferences.map(ref => transaction.get(ref))); // 3. 遍历快照,只写入不存在的文档 for (let i = 0; i < docSnapshots.length; i++) { const snapshot = docSnapshots[i]; const data = uniqueLocalData[i]; if (!snapshot.exists) { transaction.set(docReferences[i], data); } } }); console.log('所有唯一数据已成功写入Firestore!'); } catch (error) { console.error('事务执行失败:', error); } } // 执行写入操作 writeUniqueDocsToFirestore();
关键细节解释
本地初步去重:先在本地过滤掉重复条目,能大幅减少事务中需要执行的Get操作数量,既提升效率,也降低事务冲突的概率(毕竟Firestore事务有操作次数上限)。这里用
Map根据id去重,你可以换成适合你业务的唯一标识字段。async/await + Promise.all:在事务函数内部,我们用
Promise.all并行获取所有文档的快照,这样所有Get操作同时发起,不用一个个嵌套then(),代码结构更扁平易维护。Firestore允许在事务中并行执行多个读取操作,不会触发冲突。事务的原子性保障:整个检查+写入过程在事务中执行,确保如果中间任何一步失败,所有操作都会回滚,不会出现部分数据写入的情况。即使有其他进程同时尝试写入相同数据,事务也能保证最终每条数据只存一次。
注意事项
- 如果你的数据量极大(比如超过500条),要分成批次处理——Firestore事务最多支持500次读写操作,所以可以每400条作为一个批次,分批执行事务。
- 确保你用来判断唯一性的字段是绝对可靠的,比如文档ID或者业务上的唯一键,避免因为字段重复导致误判。
- 如果你用的是客户端Firestore SDK,逻辑完全一致,只是初始化Firestore的方式不同,
runTransaction同样支持async函数。
内容的提问来源于stack exchange,提问作者regisin

