如何从 Firestore 制作父/子对象流

Posted

技术标签:

【中文标题】如何从 Firestore 制作父/子对象流【英文标题】:How can I make a stream of parent/child objects from firestore 【发布时间】:2020-03-17 16:06:49 【问题描述】:

我有一个具有层次关系的 Firestore 数据库。 “父”文档具有子对象的集合(“子”)。我想要一连串家长的反对意见。因此,如果 Fire 存储中的父级发生更改,则流应该提供一个新的 Parent 对象,其中所有子级都已加载。

下面的代码是我想要实现的。问题出现在标记为“====> 问题”的行中,该行已插入代码中。

编译器说:返回类型 'List>' 不是匿名closure.dart(return_of_invalid_type_from_closure) 定义的'List'。

我不知道如何将此流转换为“映射”调用到异步调用以获取 List 而不是 Future

有人知道 Flutter/Dart 中分层 Firestore 数据的好示例吗?

import 'package:cloud_firestore/cloud_firestore.dart';

final parentsCollection = Firestore.instance.collection('parents');

Stream<List<Parent>> jots() 
  return parentsCollection.snapshots().map((snapshot) 

 //====================================================
        return snapshot.documents //  <===== Problem
 //====================================================
        .map((doc) async  
          var parentEntity = await ParentEntity.fromSnapshot(doc);
          return Parent.fromEntity(parentEntity);
        ).toList();
  );


class Parent 
  final String id;
  final String data;
  final List<Child> children ;

  Parent(this.data, 
    String id, 
    List<Child> children
  )
      : this.id = id ?? '',
        this.children = children ?? List<Child>()
    ;

  static Parent fromEntity(ParentEntity entity) 
    var parent = Parent(
      entity.data,
      id: entity.id,
      children: entity.children.map((c)=>Child.fromEntity(c))
    );
    return parent;
  


class Child 
  final String id;
  final String label;

  Child(this.label, String id)
      : this.id = id ?? '';

  static Child fromEntity(ChildEntity entity) 
    var child = Child(
      entity.label,
      id: entity.id,
    );
    return child;
  


class ParentEntity 
  final String id;
  final String data;
  final List<ChildEntity> children;

  ParentEntity( this.id, this.data, this.children );

  static Future<ParentEntity> fromSnapshot(DocumentSnapshot snapshot) async 
    var children = await _childrenFromSnapshot(snapshot);
    return ParentEntity( 
      snapshot.documentID, 
      snapshot.data['data'], 
      children
    );
  

  static Future<List<ChildEntity>> _childrenFromSnapshot(
      DocumentSnapshot snapshot) async 
    var childCollection = snapshot.reference.collection("children");
    QuerySnapshot docs = await childCollection.getDocuments();
    return docs.documents.map((doc) 
      return ChildEntity( doc.documentID, doc.data["label"]);
    );
  


class ChildEntity 
  final String id;
  final String label;

  ChildEntity( this.id, this.label );

  static ChildEntity fromSnapshot(DocumentSnapshot snapshot) 
    return ChildEntity( 
      snapshot.documentID,
      snapshot.data['label']);
  

【问题讨论】:

【参考方案1】:

我想这就是你想要的:

Stream<List<Parent>> jots2() 
  return parentsCollection.snapshots().asyncMap((snapshot) async 
    return Future.wait(snapshot.documents.map((doc) async 
      var parentEntity = await ParentEntity.fromSnapshot(doc);
      return Parent.fromEntity(parentEntity);
    ).toList());
  );

诀窍是使用asyncMap(类似于map,但异步)和Future.wait(等待所有Futures 完成)来异步处理QuerySnapshot。

几点说明:

你可能错过了.toList() 毕竟.map 使用此方法,如果仅更改子项的值,您的 Stream 不会发出新值,但如果您更新父项,它会发送更新的子项 因为这样您总是会阅读父级的完整子级集合,这可能会影响您的 Firestore 使用,因为它是基于每个文档的

您还要求提供“好”样品。不幸的是 - 这取决于您的数据和应用程序。 Firebase 提出了一个关于结构化数据的whole page。有时最好避免子集合,有时则不然。

在您的情况下,将“孩子”作为数据字段 (children2) 而不是子集合似乎更有意义,但我不完全知道您在创建什么。

【讨论】:

【参考方案2】:

您将Stream&lt;List&lt;Parent&gt;&gt; 声明为类型,但.map().toList() 组合返回List&lt;dynamic&gt;。您需要在地图上声明类型:

return Future.wait(snapshot.documents.map<Parent>((doc) async 

【讨论】:

以上是关于如何从 Firestore 制作父/子对象流的主要内容,如果未能解决你的问题,请参考以下文章

Java流对象:InputStreamOutputStreamReaderWriter

如何从For循环中制作流

即使流正在返回对象,Flutter StreamProvider 也会返回 null 对象

CGBTN2108-DAY13总结复习

FLUTTER,FIRESTORE:我有一个流构建器,它从集合中的所有文档中返回某个字段..如何添加查询?

在 Dart 中保存流的最后发出的值