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

时间:2018-09-04 11:32:14

标签: firebase google-cloud-firestore firebase-admin

我正在尝试使用timestamp管理员时间戳记更新具有超过500个文档的集合中的字段Firestore

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个文档,直到所有文档都被更新。

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

https://firebase.google.com/docs/firestore/manage-data/delete-data#collections

但是,对于更新,由于文档未删除,我不确定如何结束执行。

5 个答案:

答案 0 :(得分:8)

我喜欢使用打字稿的简单解决方案:

       MyButton.Enabled = true;

只需记住在顶部添加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) 。基于this answer

答案 1 :(得分:3)

如上所述,@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);
}

答案 2 :(得分:2)

您的解决方案将不是真正的“递归”。如果要批量更新包含500个以上文档的集合,则必须迭代文档,自己创建500个或更少的批次,然后分别提交这些批次。或者,您可以简单地单独更新每个文档,因为您的情况下可能没有真正的批量需求。

答案 3 :(得分:0)

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

我使用云功能来更新Firestore中的收藏集,但这也适用于客户端代码。

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

所有更新完成后,代码将遍历batchArray并提交数组内部的每个批处理。

重要的是要对批次进行的每个操作进行计数 set(), update(), delete(),因为它们都计入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(firestore.collection('my-collection').doc(documentData.id), documentData);
    operationCounter++;

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

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

return;

答案 4 :(得分:0)

简单的解决方案 就开两次火? 我的数组是“resultsFinal” 我以 490 的限制触发批处理一次,第二次以数组的长度限制(results.lenght) 对我来说很好用:) 你怎么查的? 你去 firebase 并删除你的收藏,firebase 说你已经删除了 XXX 文档,与你的数组的长度相同?好的,你可以走了

async function quickstart(results) {
    // we get results in parameter for get the data inside quickstart function
    const resultsFinal = results;
    // console.log(resultsFinal.length);
    let batch = firestore.batch();
    // limit of firebase is 500 requests per transaction/batch/send 
    for (i = 0; i < 490; i++) {
        const doc = firestore.collection('testMore490').doc();
        const object = resultsFinal[i];
        batch.set(doc, object);
    }
    await batch.commit();
    // const batchTwo = firestore.batch();
    batch = firestore.batch();

    for (i = 491; i < 776; i++) {
        const objectPartTwo = resultsFinal[i];
        const doc = firestore.collection('testMore490').doc();
        batch.set(doc, objectPartTwo);
    }
    await batch.commit();

}