使用 Firestore 事务更新多个文档

Updating multiple documents using Firestore Transaction

用例: 我有多个笔记,每个笔记都绑定了一个标签。如果用户删除标签,则与该标签关联的所有注释都绑定到默认标签。所以为了让它工作,我首先需要找出所有带有该标签的笔记,用默认标签的 id 替换它们的标签 id,最后删除所选标签。所有这些都需要是原子的,因此 Firestore Transaction.

出现问题是因为我想通过触发 queryget() 多个文档。由于这是一个 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,然后 运行 getOnSuccessListener 中的事务(甚至是批量写入,因为 get() 是已经完成),但这并不能保证 get() 和我的 update() 之间的数据一致性,是吗?

因此,我想知道应该怎么做。如何 getupdate 多个文档使用 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".

的数据

只有当您需要阅读文档的内容以便在编写文档之前做出决定时,事务才有意义。在您的情况下,您实际上并没有使用每个文档的内容来决定如何处理它。在这种情况下,批量写入就足够了。