Firebase 如何使用批处理更新Firestore中的500多个文档?

Firebase 如何使用批处理更新Firestore中的500多个文档?,firebase,google-cloud-firestore,firebase-admin,Firebase,Google Cloud Firestore,Firebase Admin,我正试图用Firestoreadmin时间戳更新一个包含500多个文档的集合中的字段timestamp const batch = db.batch(); const serverTimestamp = admin.firestore.FieldValue.serverTimestamp(); db .collection('My Collection') .get() .then((docs) => { serverTimestamp, }, { mer

我正试图用
Firestore
admin时间戳更新一个包含500多个文档的集合中的字段
timestamp

const batch = db.batch();
const serverTimestamp = admin.firestore.FieldValue.serverTimestamp();

db
  .collection('My Collection')
  .get()
  .then((docs) => {
    serverTimestamp,
  }, {
    merge: true,
  })
  .then(() => res.send('All docs updated'))
  .catch(console.error);
这会抛出一个错误

{ Error: 3 INVALID_ARGUMENT: cannot write more than 500 entities in a single call
    at Object.exports.createStatusError (C:\Users\Growthfile\Desktop\cf-test\functions\node_modules\grpc\src\common.js:87:15)
    at Object.onReceiveStatus (C:\Users\Growthfile\Desktop\cf-test\functions\node_modules\grpc\src\client_interceptors.js:1188:28)
    at InterceptingListener._callNext (C:\Users\Growthfile\Desktop\cf-test\functions\node_modules\grpc\src\client_interceptors.js:564:42)
    at InterceptingListener.onReceiveStatus (C:\Users\Growthfile\Desktop\cf-test\functions\node_modules\grpc\src\client_interceptors.js:614:8)
    at callback (C:\Users\Growthfile\Desktop\cf-test\functions\node_modules\grpc\src\client_interceptors.js:841:24)
  code: 3,
  metadata: Metadata { _internal_repr: {} },
  details: 'cannot write more than 500 entities in a single call' }
有没有一种方法可以让我编写一个递归方法,创建一个批对象,一个一个地更新一批500个文档,直到所有文档都被更新

从文档中,我知道使用递归方法可以执行删除操作,如下所述:


但是,对于更新,我不确定如何结束执行,因为文档没有被删除。

我还遇到了更新Firestore集合中500多个文档的问题。我想和大家分享我是如何解决这个问题的

我使用云函数更新Firestore中的集合,但这也适用于客户端代码

解决方案统计对批次进行的每个操作,并在达到限制后创建一个新批次,并将其推送到
batchArray

所有更新完成后,代码通过
batchArray
循环,并提交数组中的每个批

重要的是要对批次中的每个操作进行计数,因为它们都计数到500次操作限制

const documentSnapshotArray = await firestore.collection('my-collection').get();

const batchArray = [];
batchArray.push(firestore.batch());
let operationCounter = 0;
let batchIndex = 0;

documentSnapshotArray.forEach(documentSnapshot => {
    const documentData = documentSnapshot.data();

    // update document data here...

    batchArray[batchIndex].update(documentSnapshot.ref, documentData);
    operationCounter++;

    if (operationCounter === 499) {
      batchArray.push(firestore.batch());
      batchIndex++;
      operationCounter = 0;
    }
});

batchArray.forEach(async batch => await batch.commit());

return;

我喜欢这个简单的解决方案:

const users = await db.collection('users').get()

const batches = _.chunk(users.docs, 500).map(userDocs => {
    const batch = db.batch()
    userDocs.forEach(doc => {
        batch.set(doc.ref, { field: 'myNewValue' }, { merge: true })
    })
    return batch.commit()
})

await Promise.all(batches)

只需记住在顶部添加“lodash”中的
import*as。根据上面提到的,@Sebastian的答案是好的,我也投了更高的票。虽然在一次性更新25000多个文档时遇到了问题。
对逻辑的调整如下所示

console.log(`Updating documents...`);
let collectionRef = db.collection('cities');
try {
  let batch = db.batch();
  const documentSnapshotArray = await collectionRef.get();
  const records = documentSnapshotArray.docs;
  const index = documentSnapshotArray.size;
  console.log(`TOTAL SIZE=====${index}`);
  for (let i=0; i < index; i++) {
    const docRef = records[i].ref;
    // YOUR UPDATES
    batch.update(docRef, {isDeleted: false});
    if ((i + 1) % 499 === 0) {
      await batch.commit();
      batch = db.batch();
    }
  }
  // For committing final batch
  if (!(index % 499) == 0) {
    await batch.commit();
  }
  console.log('write completed');
} catch (error) {
  console.error(`updateWorkers() errored out : ${error.stack}`);
  reject(error);
}
console.log(`updatedocuments…`);
让collectionRef=db.collection('cities');
试一试{
设batch=db.batch();
const documentSnapshotArray=await collectionRef.get();
const records=documentSnapshotArray.docs;
常量索引=documentSnapshotArray.size;
log(`TOTAL SIZE===${index}`);
for(设i=0;i
为什么不遍历所有500个文档,更新并使用最后一个文档键构造startAt以创建新查询?您可以限制然后递归批处理,面临同样的问题,这就是我的解决方案:如何确保所有批处理都成功执行,因为只有批处理中的操作是原子操作。如果某些批执行,而某些批未执行,则会导致数据不一致t@Adarsh是的,你说得对。我遗漏了错误处理部分。我将很快在答案中添加这一部分。我已经将我的数据库更新为一个新的数据模型,这在我的例子中是一个幂等运算。因此,我可以重复代码,直到每个批处理成功。因此,您可以做几件事。您可以在创建云函数时选中重试选项。这将确保在任何异常情况下执行云函数。但是你必须处理你认为是“代码>瞬态<代码>的失败,否则它将是一个无止境的循环。此外,在云函数执行之间还必须保持某种状态,以便先前执行的批不会再次执行。也许您可以在每次成功的批处理操作时写入实时数据库/firestore,并在下次重试时,在某些批处理没有执行时继续执行,或者您可以将作业详细信息(更新详细信息)写入,比如说
/queue/pendingUpdate/
,然后编写一个按计划运行的云函数(比如每5分钟一次)它执行更新。操作成功后,您可以将作业删除/标记为已完成。否则它会在下一个时间间隔内自动重试。这比第一个容易得多。你的想法是什么?@Mihae Kheel是的,循环在达到500次操作后会创建一个新批,但重要的是要计算每个操作的数量。您还需要某种形式的错误处理。“使用类型脚本”。。。我没有看到任何字体脚本被更正,谢谢。这应该是官方文档的一部分。或者至少是类似的,不依赖于矿粉。工作起来很有魅力!:)