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

时间:2019-11-21 16:42:56

标签: flutter dart google-cloud-firestore stream

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

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

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

我不知道如何将此流转换为“映射”调用,以转换为asyn调用以获取列表而不是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']);
  }
}

2 个答案:

答案 0 :(得分: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(等待所有Future完成)来异步处理QuerySnapshot。

一些注意事项:

  • 您可能根本错过了.toList() .map
  • 使用这种方法,如果仅更改子代的值,您的流不会发出新值,但是如果您更新父代,它会发送更新的子代
  • 因为这样,您始终会读取父级的完整子级集合,因此可能会影响Firestore的使用情况,因为它是基于每个文档的

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

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

Firestore panel

答案 1 :(得分:0)

您将Stream<List<Parent>>声明为类型,但是.map().toList()组合时将返回List<dynamic>。您需要在地图上声明类型:

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