这不是重复的,因为我没有找到任何在 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),
),
),
);
}
批量写入的限制为 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()));
我认为这是一个非常好的问题,值得更多关注。
解决此问题的一种方法是将文档分成 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 以内。