0

I'm still learning Dart/Flutter so I have a conceptual question. If I run a query that returns 5 documents to me, how can I set up update listeners for each individual document so that when one is updated I don't have to re-run the query and fetch all 5 over again? Basically, I want individual updates and not group updates when only one has actually changed.

Here is my current query code that listens for updates but wastes a lot of Firestore reads.

Firestore.instance
    .collection("lists")
    .where("users", arrayContains: uid)
    .snapshots()
    .listen((data) =>
        lists = Map.fromEntries(data.documents.map((DocumentSnapshot doc) {
          return MapEntry(doc.documentID, TaskList.fromSnapshot(doc));
        })));
Jared
  • 2,029
  • 5
  • 20
  • 39

2 Answers2

0

Only the updated documents will usually be re-read from the server. The other (non-updated) documents normally would be read from the local cache.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • So the excessive reads in this situation won't be counted by firestore? That would solve the issue if true but from looking at the read / write counters online it seems like they all count for reads every time. :/ – Jared Feb 12 '19 at 01:43
  • Usually indeed that won't be the case, although it does depend on how long the listener has been attached. Also see: https://stackoverflow.com/q/48489895, after 30 minutes a full refresh may be required. See https://groups.google.com/forum/#!topic/google-cloud-firestore-discuss/A7vMrtmV4U8 – Frank van Puffelen Feb 12 '19 at 02:44
0

From this answer, we can understand that calling the .snapshots() returns a Stream object, which is what we'll want. This will be possible from Queries, Collection references and document references. You'll need the later.

First, save the reference for each document in the model itself, add it to the constructors as well so you can always pass it when creating the objects from a DocumentSnapshot, like this:

import 'package:cloud_firestore/cloud_firestore.dart';

class TaskList {
  /* attributes */

  DocumentReference reference; //Add this

  // If you followed the default firebase guide, you'll have the following methods.
  // Add the reference to your constructors

  TaskList.fromMap(Map<String, dynamic> map, {this.reference}) //add this reference
      : //normal attribute initializations;

  TaskList.fromSnapshot(DocumentSnapshot snapshot)
      : this.fromMap(snapshot.data, reference: snapshot.reference); //add this reference as well

   ...
} 

Now, for each document, you'll use the reference attribute, get the snapshots, and listen to this stream:

TaskList taskList = TaskList.fromSnapshot(doc); //normal initialization
taskList.reference.snapshots().listen((updatedDoc){ //listen to the stream
    print("Document was updated:");
    print(updatedDoc.data);
    // notice that this will return the first time with the object itself
    // which can be resource consuming
});
return MapEntry(doc.documentID, taskList);
George
  • 6,886
  • 3
  • 44
  • 56