使用Firestore交易更新多个文件

时间:2020-04-16 11:10:56

标签: java android firebase google-cloud-firestore transactions

用例:我有多个注释,每个注释都与一个标签绑定在一起。如果用户删除标签,则与该标签关联的所有注释都将绑定到默认标签。因此,要使其正常工作,我首先需要找出带有该标签的所有注释,用默认标签的ID替换其标签ID,最后删除选定的标签。所有这些都必须是原子的,因此需要Firestore Transaction

出现问题是因为我想通过触发get()query多个文档。由于它是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的{​​{1}}内运行事务(或者甚至是批量写入,因为OnSuccessListener已经完成),但这不能保证get()和我的get()之间的数据一致性吗?

因此,我想知道应该怎么做。如何使用update() getupdate多个文档?

2 个答案:

答案 0 :(得分:1)

我可以先获取()匹配的id,然后在get的OnSuccessListener中运行事务(或者甚至是批写入,因为get()已经完成),但这不能保证get()和我之间的数据一致性update(),对吗?

是的,批量写入将解决您的问题。在这种情况下,不存在一致性问题,因为批处理写入是原子操作,或者所有操作都成功执行,或者都不应用任何操作。因此,您应该创建所需的查询,获取所有需要的文档,并将所有文档添加到一批更新操作中。最后,只需调用commit()。请注意,每批次最多只能进行500次操作。如果您还需要更多,则应创建自己的机制来以500个操作的块来更新文档。

答案 1 :(得分:1)

Firestore 不支持通过查询获取文档的客户端应用中的交易一致性。请注意,transaction对象没有任何执行查询的方法。您只能执行单个文档的get()操作。

为了获得原子更新,必须对所有文档检索和更新使用提供的Transaction对象。您必须先阅读文档,然后才能编写文档(但是,如果您阅读文档,则不必编写文档)。

如果您需要处理来自查询结果的文档,则应在事务的外部中执行查询,并记住要在事务中使用的每个文档的ID。如果查询的结果在事务完成之前发生变化(例如,将添加或删除文档),则您的事务将处理“过时”的数据。

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