Bloc: Merge separate queries results in Todos Firestore example

Created on 10 Sep 2019  ·  3Comments  ·  Source: felangel/bloc

I am trying to make 2 queries to Firestore and merge the results into one in order to simulate a Firestore OR query.

I got back the Firestore todos example and particularly the FirebaseTodosRepository class.

class FirebaseTodosRepository implements TodosRepository {
  final todoCollection = Firestore.instance.collection('todos');
  // ….
  @override
  Stream<List<Todo>> todos() {

     return todoCollection.where("owners", arrayContains: userId).snapshots().map((snapshot) {
  return snapshot.documents.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
      .toList();

  });

}

I would like to merge the result of 2 queries but I don't know how to proceed despite some unsuccessful researchs.


 todoCollection.where("owners", arrayContains: userId).snapshots().map((snapshot) {
      return snapshot.documents.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
          .toList();
    });

 todoCollection.where("contributors", arrayContains: userId).snapshots().map((snapshot) {
      return snapshot.documents.map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
          .toList();
    });

Thanks again for your time.

question

Most helpful comment

I post my solution for those who need to working deeper on FirestoreTodos example.

///private method to zip QuerySnapshot streams
Stream<List<QuerySnapshot>> _combineStreams(String userId) {

    var stream1 = todosCollection
        .where("owners", arrayContains: userId)
        .snapshots();

    var stream2 = todosCollection
      .where("contributors", arrayContains: userId)
      .snapshots();

    return StreamZip(([stream1, stream2])).asBroadcastStream();
  }

///exposed method to be consumed by repository
Stream<List<Todo>> todos(String userId) {

    var controller = StreamController<List<Todo>>();

    _combineStreams(userId).listen((snapshots) {

      List<DocumentSnapshot> documents = List<DocumentSnapshot>();

      snapshots.forEach((snapshot) {
        documents.addAll(snapshot.documents);
      });

      final todos = documents.map((doc) {
        return Todo.fromEntity(TodoEntity.fromSnapshot(doc));
      }).toList();

      controller.add(todos);
    });

    return controller.stream;
  }

Thanks again for your time guys.

All 3 comments

if you are using rxdart, you can replace Stream<List<Todo>> todos() function with a function of type
ReplaySubject<Todo>
it will give you all the Todos that were passed

Hi @FTholin 👋
Thanks for opening an issue!

You should be able to do something like:

Future<Stream> combineStreams() async {
  Stream stream1 = todoCollection
      .where("owners", arrayContains: userId)
      .snapshots()
      .map((snapshot) {
    return snapshot.documents
        .map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
        .toList();
  });
  Stream stream2 = todoCollection
      .where("contributors", arrayContains: userId)
      .snapshots()
      .map((snapshot) {
    return snapshot.documents
        .map((doc) => Todo.fromEntity(TodoEntity.fromSnapshot(doc)))
        .toList();
  });

  return StreamZip(([stream1, stream2])).asBroadcastStream();
}

Hope that helps! 👍

Also for future reference please try to avoid posted non-bloc-related questions here, thanks!

I post my solution for those who need to working deeper on FirestoreTodos example.

///private method to zip QuerySnapshot streams
Stream<List<QuerySnapshot>> _combineStreams(String userId) {

    var stream1 = todosCollection
        .where("owners", arrayContains: userId)
        .snapshots();

    var stream2 = todosCollection
      .where("contributors", arrayContains: userId)
      .snapshots();

    return StreamZip(([stream1, stream2])).asBroadcastStream();
  }

///exposed method to be consumed by repository
Stream<List<Todo>> todos(String userId) {

    var controller = StreamController<List<Todo>>();

    _combineStreams(userId).listen((snapshots) {

      List<DocumentSnapshot> documents = List<DocumentSnapshot>();

      snapshots.forEach((snapshot) {
        documents.addAll(snapshot.documents);
      });

      final todos = documents.map((doc) {
        return Todo.fromEntity(TodoEntity.fromSnapshot(doc));
      }).toList();

      controller.add(todos);
    });

    return controller.stream;
  }

Thanks again for your time guys.

Was this page helpful?
0 / 5 - 0 ratings