使用 Firestore 事务更新多个文档
Updating multiple documents using Firestore Transaction
用例: 我有多个笔记,每个笔记都绑定了一个标签。如果用户删除标签,则与该标签关联的所有注释都绑定到默认标签。所以为了让它工作,我首先需要找出所有带有该标签的笔记,用默认标签的 id 替换它们的标签 id,最后删除所选标签。所有这些都需要是原子的,因此 Firestore Transaction
.
出现问题是因为我想通过触发 query
来 get()
多个文档。由于这是一个 async
操作,因此交易的整个目的就消失了。我在文档中看到的示例是关于单个文档的。
FirebaseConstants.getRootRef() // db reference
.runTransaction((Transaction.Function<Void>) transaction -> {
FirebaseConstants.getNotesRef() // Notes collection reference
.whereEqualTo("labelDetails.id", "<old_label_id>")
.get()
.addOnSuccessListener(snapshots -> {
ArrayList<String> listIds = new ArrayList<>();
for (DocumentSnapshot snap : snapshots.getDocuments()) {
listIds.add(snap.getId());
}
for (String id : listIds) {
FirebaseConstants.getNotesRef()
.document(id)
.update("labelDetails.id", "<default_label_id>");
}
// Labels collection reference
FirebaseConstants.getLabelsRef().document("<old_label_id>").delete();
});
return null;
})
.addOnSuccessListener(snapshots -> {
Toast.makeText(this, "transaction success", Toast.LENGTH_SHORT).show();
})
.addOnFailureListener(e -> {
Toast.makeText(this, "transaction failed", Toast.LENGTH_SHORT).show();
});
所以,我很想知道如何处理这种情况。我知道,我可以先 get()
匹配的 ID,然后 运行 get
的 OnSuccessListener
中的事务(甚至是批量写入,因为 get()
是已经完成),但这并不能保证 get()
和我的 update()
之间的数据一致性,是吗?
因此,我想知道应该怎么做。如何 get
和 update
多个文档使用 Transaction
?
I can get() the matching ids first and then run the transaction inside get's OnSuccessListener (or even a Batch Write, since get() is already done), but that doesn't guarantee data consistency between get() and my update(), does it?
是的,批量写入可以解决您的问题。在这种情况下不存在一致性问题,因为批量写入是原子操作,要么所有操作都成功,要么应用其中 none 个。因此,您应该创建所需的查询,获取所有需要的文档并将它们全部添加到一批更新操作中。最后直接调用commit()
。请注意,每批次有 500 次操作的限制。如果您需要更多,您应该创建自己的机制来以 500 次操作为单位更新文档。
Firestore 不 支持通过查询获取文档的客户端应用程序中的事务一致性。请注意,transaction
对象无法执行查询。您只能对单个文档进行 get() 操作。
为了获得原子更新,您必须使用提供的 Transaction 对象进行所有文档检索和更新。一定要先看文档才能写(但是看了文档就不用写了)
如果您需要处理来自查询结果的文档,您应该在外部 事务中执行查询,并记住每个文档的 ID 以便在交易。如果查询结果在事务完成之前发生变化(例如,将添加或删除文档),那么您的事务将处理 "out of date".
的数据
只有当您需要阅读文档的内容以便在编写文档之前做出决定时,事务才有意义。在您的情况下,您实际上并没有使用每个文档的内容来决定如何处理它。在这种情况下,批量写入就足够了。
用例: 我有多个笔记,每个笔记都绑定了一个标签。如果用户删除标签,则与该标签关联的所有注释都绑定到默认标签。所以为了让它工作,我首先需要找出所有带有该标签的笔记,用默认标签的 id 替换它们的标签 id,最后删除所选标签。所有这些都需要是原子的,因此 Firestore Transaction
.
出现问题是因为我想通过触发 query
来 get()
多个文档。由于这是一个 async
操作,因此交易的整个目的就消失了。我在文档中看到的示例是关于单个文档的。
FirebaseConstants.getRootRef() // db reference
.runTransaction((Transaction.Function<Void>) transaction -> {
FirebaseConstants.getNotesRef() // Notes collection reference
.whereEqualTo("labelDetails.id", "<old_label_id>")
.get()
.addOnSuccessListener(snapshots -> {
ArrayList<String> listIds = new ArrayList<>();
for (DocumentSnapshot snap : snapshots.getDocuments()) {
listIds.add(snap.getId());
}
for (String id : listIds) {
FirebaseConstants.getNotesRef()
.document(id)
.update("labelDetails.id", "<default_label_id>");
}
// Labels collection reference
FirebaseConstants.getLabelsRef().document("<old_label_id>").delete();
});
return null;
})
.addOnSuccessListener(snapshots -> {
Toast.makeText(this, "transaction success", Toast.LENGTH_SHORT).show();
})
.addOnFailureListener(e -> {
Toast.makeText(this, "transaction failed", Toast.LENGTH_SHORT).show();
});
所以,我很想知道如何处理这种情况。我知道,我可以先 get()
匹配的 ID,然后 运行 get
的 OnSuccessListener
中的事务(甚至是批量写入,因为 get()
是已经完成),但这并不能保证 get()
和我的 update()
之间的数据一致性,是吗?
因此,我想知道应该怎么做。如何 get
和 update
多个文档使用 Transaction
?
I can get() the matching ids first and then run the transaction inside get's OnSuccessListener (or even a Batch Write, since get() is already done), but that doesn't guarantee data consistency between get() and my update(), does it?
是的,批量写入可以解决您的问题。在这种情况下不存在一致性问题,因为批量写入是原子操作,要么所有操作都成功,要么应用其中 none 个。因此,您应该创建所需的查询,获取所有需要的文档并将它们全部添加到一批更新操作中。最后直接调用commit()
。请注意,每批次有 500 次操作的限制。如果您需要更多,您应该创建自己的机制来以 500 次操作为单位更新文档。
Firestore 不 支持通过查询获取文档的客户端应用程序中的事务一致性。请注意,transaction
对象无法执行查询。您只能对单个文档进行 get() 操作。
为了获得原子更新,您必须使用提供的 Transaction 对象进行所有文档检索和更新。一定要先看文档才能写(但是看了文档就不用写了)
如果您需要处理来自查询结果的文档,您应该在外部 事务中执行查询,并记住每个文档的 ID 以便在交易。如果查询结果在事务完成之前发生变化(例如,将添加或删除文档),那么您的事务将处理 "out of date".
的数据只有当您需要阅读文档的内容以便在编写文档之前做出决定时,事务才有意义。在您的情况下,您实际上并没有使用每个文档的内容来决定如何处理它。在这种情况下,批量写入就足够了。