如何在Flutter中迭代firestore批量写入执行500多个操作?

问题描述 投票:0回答:3

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

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

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
3个回答
0
投票

批量写入的限制为 500 次操作,这是一个硬性限制。您可以简单地执行多个批次,以添加更多文档,但不能超过限制。

您可以做的一件事是,创建一个计数器变量并在每次启动批处理操作时增加其值。然后创建一个 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()));

0
投票

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

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

注意:此代码利用了 core-js 中的

group
,因为截至目前它还不是 ES 库的一部分

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)

注意:如果您要对每个文档进行多次批量写入,请减少

countPerBatch
进行补偿。例如,如果您对每个文档进行(最多)两次写入,则将
countPerBatch
减少到 250。这是为了确保每批的总写入次数保持在 500 以内。


0
投票

仅供 2023 年之后看到此内容的任何人参考,自

2023 年 3 月 29 日
🤙🏻 起不再有 commit

限制
© www.soinside.com 2019 - 2024. All rights reserved.