Google cloud firestore 实现firestore无限查询列表,该列表在集合更改时更新

Google cloud firestore 实现firestore无限查询列表,该列表在集合更改时更新,google-cloud-firestore,bloc,Google Cloud Firestore,Bloc,我想说什么? 我目前在firestore后端实现实时更新的无限滚动列表时遇到了一系列问题 在我的应用程序中,我希望向用户显示评论(如YouTube或其他社交媒体网站)。由于集合中的注释数量可能相当大,我看到了一个选项,可以对集合进行分页,同时接收基于快照的实时更新。因此,我最初加载x注释时,只要用户按下按钮,就可以加载最多x更多项目。在下图中x=3 标准溶液 基于其他SO问题,我发现应该使用.limit()和.startAfter()方法来实现这种行为。 因此,第一页加载为: query =

我想说什么?

我目前在firestore后端实现实时更新的无限滚动列表时遇到了一系列问题

在我的应用程序中,我希望向用户显示评论(如YouTube或其他社交媒体网站)。由于集合中的注释数量可能相当大,我看到了一个选项,可以对集合进行分页,同时接收基于快照的实时更新。因此,我最初加载
x
注释时,只要用户按下按钮,就可以加载最多
x
更多项目。在下图中
x=3

标准溶液

基于其他SO问题,我发现应该使用
.limit()
.startAfter()
方法来实现这种行为。 因此,第一页加载为:

query = this
    .collection
    .orderBy('date', descending: true)
    .limit(pageSize);
query.snapshots().map((QuerySnapshot snap) {
  lastVisible = snap.documents.last;
  // convert the DocumentSnapshot into model object
});
所有其他页面均加载以下代码:

query = this.collection
        .orderBy('date', descending: true)
        .startAfterDocument(lastVisible)
        .limit(pageSize);
此外,我想补充一点,这段代码位于一个repository类中,该类与BLoC模式一起使用,类似于中所示的代码。 虽然Felix使用一个简单的颤振列表来显示项目,但我有一个页面列表,其中显示了基于其集团提供的数据的评论。请注意,每个BLoC访问一个共享存储库(存储库代码的一部分如下所示)

标准解决方案的问题

通过上面显示的代码,我看到了多个问题:

如果在有序集合的中间插入注释(不重要),则由于快照提供的流,会添加注释。但是,由于查询中的
.limit()
运算符,已存在的另一条注释不再显示。可以将限制增加一个,但我不确定如何编辑快照查询。在无法编辑快照查询的情况下,可以创建一个新的(更大的)查询,但这将需要额外的读取
  • 类似于1。如果中间的注释被删除,快照将返回一个列表,该列表不再包含已删除的注释,但是会出现另一个注释(已经由另一页覆盖)。例如,在上图所示的场景中,加载了5条注释。假设注释3已删除,注释2将显示两次 改进标准溶液

    基于上面讨论的这两个问题,我认为解决方案是不够的,我实现了一个解决方案,通过获取两个“interval”文档,首先加载
    x
    项。然后创建一个查询,该查询使用
    .startAtDocument()
    .endAtDocument()
    以一定的间隔获取所需的项目,从而消除了
    .limit()
    操作符

    DocumentSnapshot pageStartDocument;
    DocumentSnapshot pageEndDocument;
    Future<Stream<List<Comment>>> comments() async {
        // This fetches the first and next Document as initialization
        // (maybe should be implemented in constructor)
        if (pageStartDocument == null) {
          Query query = collection
              .orderBy('date', descending: true)
              .limit(pageSize);
          QuerySnapshot snap = await query.getDocuments();
          pageStartDocument = snap.documents.first;
          pageEndDocument = snap.documents.last;
        } else {
          Query query = collection
              .orderBy('date', descending: true)
              .startAfterDocument(pageEndDocument)
              .limit(pageSize);
          QuerySnapshot snap = await query.getDocuments();
          pageStartDocument = snap.documents.first;
          pageEndDocument = snap.documents.last;
        }
    
        // This fetches a subcollection of elements from the collection
        // with the tradeof of double the reads
        Query query = this
            .collection
            .orderBy('date', descending: true)
            .startAtDocument(pageStartDocument)
            .endAtDocument(pageEndDocument);
    
        return query.snapshots().asyncMap((QuerySnapshot snap) async {
          // convert the QuerySnapshot into model objects
        });
    
    文档快照页面开始文档;
    文档快照页面结束文档;
    Future comments()异步{
    //这将获取第一个和下一个文档作为初始化
    //(可能应该在构造函数中实现)
    如果(pageStartDocument==null){
    查询=集合
    .orderBy('date',降序:true)
    .限制(页面大小);
    QuerySnapshot snap=wait query.getDocuments();
    pageStartDocument=snap.documents.first;
    pageEndDocument=snap.documents.last;
    }否则{
    查询=集合
    .orderBy('date',降序:true)
    .startAfterDocument(pageEndDocument)
    .限制(页面大小);
    QuerySnapshot snap=wait query.getDocuments();
    pageStartDocument=snap.documents.first;
    pageEndDocument=snap.documents.last;
    }
    //这将从集合中获取元素的子集合
    //以加倍的阅读量为代价
    Query=this
    收集
    .orderBy('date',降序:true)
    .StartDocument(pageStartDocument)
    .endAtDocument(pageEndDocument);
    返回query.snapshots().asyncMap((QuerySnapshot snap)async{
    //将QuerySnapshot转换为模型对象
    });
    
    如代码中所述,此解决方案有以下缺点:

  • 由于获取
    pageStartDocument
    pageEndDocument
    需要一个查询,因此读取的次数增加了一倍,因为在创建第二个查询时会再次读取所有数据。由于我相信数据是兑现的,所以性能影响可能可以忽略,但是,具有2倍的数据库读取成本可能会非常大 问题:

    由于我不仅实现分页,而且还实现实时更新(使用集合插入),因此
    .limit()
    操作符在我的情况下似乎不起作用

    如何实现带有实时更新的分页(没有双重读取)

    旁注:

    我看了怎么做,但在视频中它似乎并不是那么琐碎(有人提出,可能需要权衡)

    如果需要我方的进一步代码,请在评论中说明


    对于注释场景,将项目插入到(排序的)注释的中间是没有意义的收集。但是,如果场景需要这样的功能,我想了解应该如何实现它。

    这可能是一个很晚的答案。OP可能不再需要帮助,但是对于任何偶然发现这一点的人,我编写了一个教程,其中包含一个部分解决此问题的解决方案:

    • Bloc保留流订阅列表,以跟踪列表的实时更新
    • 然而,关于插入问题,因为当您将有基于文档光标的分页流时,在插入或删除时,您必须重置分页流订阅,除非它是最后一页。 因此,我的解决方案是在发生修改时更新列表,但在发生修改时重置列表