【问题标题】:How to iterate firestore batch writes to perform more than 500 operations in Flutter?如何迭代 firestore 批量写入以在 Flutter 中执行超过 500 次操作?
【发布时间】:2022-07-20 12:12:20
【问题描述】:

这不是重复的,因为我没有找到任何关于在 FLUTTER 中使用批处理编写超过 500 个文档的问题或解决方案。我已经在其他框架或语言中看到了这个问题的答案,但我不明白如何在颤振中实现它。我需要使用批量写入更新我的集合中的文档,但批量写入只能包含 500 个操作。那么如何将 500 多个文档写入 Firestore?我已经将迭代视为解决方案之一,但如何在颤振中实现它?

这就是我执行单批写入的方式:

Future<void> batchUpdate() {
  WriteBatch batch = FirebaseFirestore.instance.batch();

  return users
      .get()
      .then((snapshot) {
        for (DocumentSnapshot document in snapshot.docs) {
          document.reference.update(
            {
              'totalScore': 0,
            },
          );
        }
        return batch.commit();
      })
      .then((value) => ScaffoldMessenger.of(context).showSnackBar(snackBar))
      .catchError(
        (error) => ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(error),
          ),
        ),
      );
}

【问题讨论】:

    标签: firebase flutter google-cloud-firestore


    【解决方案1】:

    批量写入有一个limit of 500 operations,这是一个硬限制。您可以简单地执行多个批处理,以添加更多文档,但不能超过限制。

    您可以做的一件事是,通过创建一个计数器变量并在每次对批处理启动操作时递增其值。然后创建一个 if 语句,每次递增计数器时,检查它是否达到 500。那时,提交当前批次,重置计数器并开始一个新批次,从你离开的地方开始。执行此操作,直到完成所有批量写入。 或者我们可以通过调用batch() 创建一个BatchedWrite 对象,将其填充到最大容量为500 个文档,然后将其写入Firestore。

    使用这种方法写入 1,000 个文档大约需要 2.8 秒,因此吞吐量约为每秒 357 个文档写入。

    你可以尝试在 Flutter 代码中实现上述逻辑,因为我对 Flutter 不太熟悉。但是任何正在寻找上述逻辑的 JS 代码的人都可以看到下面的代码参考:

    async function testBatchedWrites(datas) { 
    let batch = admin.firestore().batch(); 
    let count = 0; 
    while (datas.length) {
     batch.set(collection.doc(Math.random().toString(36).substring(2, 15)), datas.shift()); 
    if (++count >= 500 || !datas.length) { 
    await batch.commit(); 
    batch = admin.firestore().batch(); 
    count = 0;
     } } }
    

    或者这个,

    // prepare the batch 
    let currentBatch = firebase.firestore().batch(); 
    let currentBatchSize = 0; 
    const batches = [ currentBatch ]; // add each doc's deletion to the batch 
    docs.forEach((doc) => { 
    // when batch is too large, start a new one 
    if (++currentBatchSize >= 500) { 
    currentBatch = firebase.firestore.batch(); 
    batches.push(currentBatch); 
    currentBatchSize = 1; } // add operation to batch 
    currentBatch.delete(doc.ref); }) // commit the changes 
    await Promise.all(batches.map(batch => batch.commit()));
    

    【讨论】:

      【解决方案2】:

      我认为这是一个非常好的问题,值得更多关注。

      解决此问题的一种方法是将文档分组为 500 个(或更少)的批次,并为每个组分配一个批次。

      import { DocumentData, DocumentSnapshot, Firestore, WriteBatch } from "@google-cloud/firestore"
      import group from 'core-js-pure/actual/array/group'
      export const groupBy = group
      
      
      export interface IndexedDoc<T> {
        index: number,
        doc: T
      }
      
      export declare type BatchWriteCallback = (doc: DocumentSnapshot<DocumentData>, index: number, batch: WriteBatch) => Promise<void>
      
      
      export const batchWrites = async (db: Firestore, documents: DocumentSnapshot<DocumentData>[], callback: BatchWriteCallback, countPerBatch = 500) => {
        if (countPerBatch > 500) {
          throw new Error('A Firestore WriteBatch cannot exceed 500 writes per batch')
        }
        const docs = documents.map((doc, index) => ({
          doc,
          index
        }) as IndexedDoc<DocumentSnapshot<DocumentData>>)
      
        const groups = groupBy(docs, (doc: IndexedDoc<DocumentSnapshot<DocumentData>>, index: number) => Math.floor(index/countPerBatch))
        const indexedGroups = Object.keys(groups).map((group) => ({
          batch: db.batch(),
          docs: groups[group] as IndexedDoc<DocumentSnapshot<DocumentData>>[]
        }))
        await Promise.all(indexedGroups.map(async group => {
          await Promise.all(group.docs.map(async indexedDoc => {
            await callback(indexedDoc.doc, indexedDoc.index, group.batch)
          }))
          await group.batch.commit()
        }))
      }
      
      

      使用功能:

         const db = getFirestore(...)
         const docs = (await db.collection(...).get()).docs // can be QueryDocumentSnapshot<DocumentData>[]
      
         await batchedWrites(db, docs, ((doc, index, batch) => {
            batch.set(doc.ref, { ... })
         }, 500)
      
      

      【讨论】:

        猜你喜欢
        • 2021-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-17
        • 1970-01-01
        • 1970-01-01
        • 2019-05-24
        • 2019-07-07
        相关资源
        最近更新 更多