Otrzymywanie aktualizacji w czasie rzeczywistym w Cloud Firestore

Możesz odsłuchiwać dokument przy użyciu metody onSnapshot(). Początkowy tekst przy użyciu podanego wywołania zwrotnego tworzy migawkę dokumentu natychmiast z użyciem bieżącą zawartość pojedynczego dokumentu. Potem, za każdym razem, gdy zmieni się zawartość, kolejne wywołanie zaktualizuje migawkę dokumentu.

Web

import { doc, onSnapshot } from "firebase/firestore";

const unsub = onSnapshot(doc(db, "cities", "SF"), (doc) => {
    console.log("Current data: ", doc.data());
});

Web

db.collection("cities").doc("SF")
    .onSnapshot((doc) => {
        console.log("Current data: ", doc.data());
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
db.collection("cities").document("SF")
  .addSnapshotListener { documentSnapshot, error in
    guard let document = documentSnapshot else {
      print("Error fetching document: \(error!)")
      return
    }
    guard let data = document.data() else {
      print("Document data was empty.")
      return
    }
    print("Current data: \(data)")
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching document: %@", error);
        return;
      }
      NSLog(@"Current data: %@", snapshot.data);
    }];

Kotlin+KTX

val docRef = db.collection("cities").document("SF")
docRef.addSnapshotListener { snapshot, e ->
    if (e != null) {
        Log.w(TAG, "Listen failed.", e)
        return@addSnapshotListener
    }

    if (snapshot != null && snapshot.exists()) {
        Log.d(TAG, "Current data: ${snapshot.data}")
    } else {
        Log.d(TAG, "Current data: null")
    }
}

Java

final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        if (e != null) {
            Log.w(TAG, "Listen failed.", e);
            return;
        }

        if (snapshot != null && snapshot.exists()) {
            Log.d(TAG, "Current data: " + snapshot.getData());
        } else {
            Log.d(TAG, "Current data: null");
        }
    }
});

Dart

final docRef = db.collection("cities").doc("SF");
docRef.snapshots().listen(
      (event) => print("current data: ${event.data()}"),
      onError: (error) => print("Listen failed: $error"),
    );

Często chcesz, aby interfejs reagował na zmiany w zawartości Firestore. dokumentu lub kolekcji. Możesz to zrobić za pomocą usługi StreamBuilder widżet, który wykorzystuje strumień zrzutu Firestore:

class UserInformation extends StatefulWidget {
  @override
  _UserInformationState createState() => _UserInformationState();
}

class _UserInformationState extends State<UserInformation> {
  final Stream<QuerySnapshot> _usersStream =
      FirebaseFirestore.instance.collection('users').snapshots();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<QuerySnapshot>(
      stream: _usersStream,
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
        if (snapshot.hasError) {
          return const Text('Something went wrong');
        }

        if (snapshot.connectionState == ConnectionState.waiting) {
          return const Text("Loading");
        }

        return ListView(
          children: snapshot.data!.docs
              .map((DocumentSnapshot document) {
                Map<String, dynamic> data =
                    document.data()! as Map<String, dynamic>;
                return ListTile(
                  title: Text(data['full_name']),
                  subtitle: Text(data['company']),
                );
              })
              .toList()
              .cast(),
        );
      },
    );
  }
}
Java
DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(
    new EventListener<DocumentSnapshot>() {
      @Override
      public void onEvent(@Nullable DocumentSnapshot snapshot, @Nullable FirestoreException e) {
        if (e != null) {
          System.err.println("Listen failed: " + e);
          return;
        }

        if (snapshot != null && snapshot.exists()) {
          System.out.println("Current data: " + snapshot.getData());
        } else {
          System.out.print("Current data: null");
        }
      }
    });
Python

# Create an Event for notifying main thread.
callback_done = threading.Event()

# Create a callback on_snapshot function to capture changes
def on_snapshot(doc_snapshot, changes, read_time):
    for doc in doc_snapshot:
        print(f"Received document snapshot: {doc.id}")
    callback_done.set()

doc_ref = db.collection("cities").document("SF")

# Watch the document
doc_watch = doc_ref.on_snapshot(on_snapshot)
C++
DocumentReference doc_ref = db->Collection("cities").Document("SF");
doc_ref.AddSnapshotListener(
    [](const DocumentSnapshot& snapshot, Error error, const std::string& errorMsg) {
      if (error == Error::kErrorOk) {
        if (snapshot.exists()) {
          std::cout << "Current data: " << snapshot << std::endl;
        } else {
          std::cout << "Current data: null" << std::endl;
        }
      } else {
        std::cout << "Listen failed: " << error << std::endl;
      }
    });
Node.js
const doc = db.collection('cities').doc('SF');

const observer = doc.onSnapshot(docSnapshot => {
  console.log(`Received doc snapshot: ${docSnapshot}`);
  // ...
}, err => {
  console.log(`Encountered error: ${err}`);
});
Go
import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/firestore"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// listenDocument listens to a single document.
func listenDocument(ctx context.Context, w io.Writer, projectID, collection string) error {
	// projectID := "project-id"
	// Сontext with timeout stops listening to changes.
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	it := client.Collection(collection).Doc("SF").Snapshots(ctx)
	for {
		snap, err := it.Next()
		// DeadlineExceeded will be returned when ctx is cancelled.
		if status.Code(err) == codes.DeadlineExceeded {
			return nil
		}
		if err != nil {
			return fmt.Errorf("Snapshots.Next: %w", err)
		}
		if !snap.Exists() {
			fmt.Fprintf(w, "Document no longer exists\n")
			return nil
		}
		fmt.Fprintf(w, "Received document snapshot: %v\n", snap.Data())
	}
}
PHP
// Not supported in the PHP client library
Unity
DocumentReference docRef = db.Collection("cities").Document("SF");
docRef.Listen(snapshot => {
    Debug.Log("Callback received document snapshot.");
    Debug.Log(String.Format("Document data for {0} document:", snapshot.Id));
    Dictionary<string, object> city = snapshot.ToDictionary();
    foreach (KeyValuePair<string, object> pair in city) {
        Debug.Log(String.Format("{0}: {1}", pair.Key, pair.Value));
    }
});
C#
DocumentReference docRef = db.Collection("cities").Document("SF");
FirestoreChangeListener listener = docRef.Listen(snapshot =>
{
    Console.WriteLine("Callback received document snapshot.");
    Console.WriteLine("Document exists? {0}", snapshot.Exists);
    if (snapshot.Exists)
    {
        Console.WriteLine("Document data for {0} document:", snapshot.Id);
        Dictionary<string, object> city = snapshot.ToDictionary();
        foreach (KeyValuePair<string, object> pair in city)
        {
            Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
        }
    }
});
Ruby
doc_ref = firestore.col(collection_path).doc document_path
snapshots = []

# Watch the document.
listener = doc_ref.listen do |snapshot|
  puts "Received document snapshot: #{snapshot.document_id}"
  snapshots << snapshot
end

Wydarzenia w przypadku zmian lokalnych

Lokalne zapisy w aplikacji spowodują natychmiastowe wywołanie detektorów zrzutów. Wynika to z działania funkcji o nazwie „kompensacja opóźnienia”. Gdy wykonasz zapis, słuchacze otrzymają powiadomienie dane przed wysłaniem ich do backendu.

Pobrane dokumenty mają właściwość metadata.hasPendingWrites, która wskazuje czy dokument zawiera zmiany lokalne, które nie zostały zapisane w backendzie . Za pomocą tej właściwości możesz określić źródło zdarzeń otrzymanych przez detektor zrzutu:

Web

import { doc, onSnapshot } from "firebase/firestore";

const unsub = onSnapshot(doc(db, "cities", "SF"), (doc) => {
  const source = doc.metadata.hasPendingWrites ? "Local" : "Server";
  console.log(source, " data: ", doc.data());
});

Web

db.collection("cities").doc("SF")
    .onSnapshot((doc) => {
        var source = doc.metadata.hasPendingWrites ? "Local" : "Server";
        console.log(source, " data: ", doc.data());
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
db.collection("cities").document("SF")
  .addSnapshotListener { documentSnapshot, error in
    guard let document = documentSnapshot else {
      print("Error fetching document: \(error!)")
      return
    }
    let source = document.metadata.hasPendingWrites ? "Local" : "Server"
    print("\(source) data: \(document.data() ?? [:])")
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching document: %@", error);
        return;
      }
      NSString *source = snapshot.metadata.hasPendingWrites ? @"Local" : @"Server";
      NSLog(@"%@ data: %@", source, snapshot.data);
    }];

Kotlin+KTX

val docRef = db.collection("cities").document("SF")
docRef.addSnapshotListener { snapshot, e ->
    if (e != null) {
        Log.w(TAG, "Listen failed.", e)
        return@addSnapshotListener
    }

    val source = if (snapshot != null && snapshot.metadata.hasPendingWrites()) {
        "Local"
    } else {
        "Server"
    }

    if (snapshot != null && snapshot.exists()) {
        Log.d(TAG, "$source data: ${snapshot.data}")
    } else {
        Log.d(TAG, "$source data: null")
    }
}

Java

final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        if (e != null) {
            Log.w(TAG, "Listen failed.", e);
            return;
        }

        String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
                ? "Local" : "Server";

        if (snapshot != null && snapshot.exists()) {
            Log.d(TAG, source + " data: " + snapshot.getData());
        } else {
            Log.d(TAG, source + " data: null");
        }
    }
});

Dart

final docRef = db.collection("cities").doc("SF");
docRef.snapshots().listen(
  (event) {
    final source = (event.metadata.hasPendingWrites) ? "Local" : "Server";
    print("$source data: ${event.data()}");
  },
  onError: (error) => print("Listen failed: $error"),
);
Java
# Not yet supported in the Java client library
Python
// Not yet supported in Python client library
C++
DocumentReference doc_ref = db->Collection("cities").Document("SF");
doc_ref.AddSnapshotListener([](const DocumentSnapshot& snapshot,
                               Error error, const std::string& errorMsg) {
  if (error == Error::kErrorOk) {
    const char* source =
        snapshot.metadata().has_pending_writes() ? "Local" : "Server";
    if (snapshot.exists()) {
      std::cout << source << " data: " << snapshot.Get("name").string_value()
                << std::endl;
    } else {
      std::cout << source << " data: null" << std::endl;
    }
  } else {
    std::cout << "Listen failed: " << error << std::endl;
  }
});
Node.js
// Not yet supported in the Node.js client library
Go
// Not yet supported in the Go client library
PHP
// Not supported in the PHP client library
Unity
DocumentReference docRef = db.Collection("cities").Document("SF");
docRef.Listen(
  snapshot =>
  {
      string source = (snapshot != null && snapshot.Metadata.HasPendingWrites) ? "Local" : "Server";
      string snapshotData = "null";
      if (snapshot != null && snapshot.Exists)
      {
          System.Text.StringBuilder builder = new System.Text.StringBuilder();
          IDictionary<string, object> dict = snapshot.ToDictionary();
          foreach (var KVPair in dict)
          {
              builder.Append($"{KVPair.Key}: {KVPair.Value}\n");
          }
          snapshotData = builder.ToString();
      }
      Debug.Log($"{source} data: ${snapshotData}");
  });
C#
// Not yet supported in the C# client library
Ruby
// Not yet supported in the Ruby client library

Zdarzenia związane ze zmianami metadanych

Podczas nasłuchiwania zmian w dokumencie, kolekcji lub zapytaniu możesz przekazać aby określić szczegółowość zdarzeń odebranych przez odbiornik.

Domyślnie detektory nie są powiadamiani o zmianach, które mają wpływ tylko na metadane. Zastanów się, co się stanie, gdy aplikacja napisze nowy dokument:

  1. Zdarzenie zmiany jest natychmiast wywoływane z nowymi danymi. Dokument nie została jeszcze zapisana w backendzie, więc „oczekujące zapisy” flaga to true.
  2. Dokument jest zapisywany w backendzie.
  3. Backend powiadamia klienta o udanym zapisie. Brak w danych dokumentu, ale zmieniły się metadane, „oczekujące zapisy” flaga to teraz false.

Jeśli chcesz otrzymywać zdarzenia zrzutu, gdy metadane dokumentu lub zapytania zmian, podczas dołączania odbiornika przekaż obiekt opcji nasłuchiwania.

Web

import { doc, onSnapshot } from "firebase/firestore";

const unsub = onSnapshot(
  doc(db, "cities", "SF"), 
  { includeMetadataChanges: true }, 
  (doc) => {
    // ...
  });

Web

db.collection("cities").doc("SF")
    .onSnapshot({
        // Listen for document metadata changes
        includeMetadataChanges: true
    }, (doc) => {
        // ...
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
// Listen to document metadata.
db.collection("cities").document("SF")
  .addSnapshotListener(includeMetadataChanges: true) { documentSnapshot, error in
    // ...
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
// Listen for metadata changes.
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListenerWithIncludeMetadataChanges:YES
                                         listener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
   // ...
}];

Kotlin+KTX

// Listen for metadata changes to the document.
val docRef = db.collection("cities").document("SF")
docRef.addSnapshotListener(MetadataChanges.INCLUDE) { snapshot, e ->
    // ...
}

Java

// Listen for metadata changes to the document.
DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(MetadataChanges.INCLUDE, new EventListener<DocumentSnapshot>() {
    @Override
    public void onEvent(@Nullable DocumentSnapshot snapshot,
                        @Nullable FirebaseFirestoreException e) {
        // ...
    }
});

Dart

final docRef = db.collection("cities").doc("SF");
docRef.snapshots(includeMetadataChanges: true).listen((event) {
  // ...
});
Java
// Not yet supported in the Java client library
Python
// Not yet supported in Python client library
C++
DocumentReference doc_ref = db->Collection("cities").Document("SF");
doc_ref.AddSnapshotListener(
    MetadataChanges::kInclude,
    [](const DocumentSnapshot& snapshot, Error error, const std::string& errorMsg) { /* ... */ });
Node.js
// Not yet supported the Node.js client library
Go
// Not yet supported in the Go client library
PHP
// Not supported in the PHP client library
Unity
DocumentReference docRef = db.Collection("cities").Document("SF");
docRef.Listen(MetadataChanges.Include, snapshot =>
{
    // ...
});
C#
// Not yet supported in the C# client library
Ruby
// Not yet supported in the Ruby client library

Skonfiguruj detektory tylko dla zmian lokalnych

Detektory zrzutów w Cloud Firestore wykonują początkowy zrzut z lokalnej pamięci podręcznej i jednocześnie pobierać odpowiednie dane z serwera.

W niektórych przypadkach możesz nie chcieć, aby dodatkowe pobrania z serwera były pobierane. Pakiety SDK klienta umożliwiają skonfigurowanie detektorów tak, aby uruchamiały się tylko w odniesieniu do danych w lokalnym pamięci podręcznej. Pozwala to uniknąć zbędnych wywołań serwera i ich kosztów. wykorzystują pamięć podręczną po stronie klienta, która odzwierciedla dane lokalne i mutacje.

W tym przypadku opcje zrzutów są ustawione w kodzie klienta tak, aby umożliwić nasłuchiwanie tylko zmian.

Web

const unsubscribe = onSnapshot(
   doc(db, "cities", "SF"),
    { 
      includeMetadataChanges: true,
      source:'cache'
     },
    (documentSnapshot) => {//…}
  );

Web

// Not yet supported in the Web namespaced API

Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
// Set up listener options
let options = SnapshotListenOptions()
    .withSource(ListenSource.cache)
    .withIncludeMetadataChanges(true)
db.collection("cities").document("SF")
  .addSnapshotListener(options: options) { documentSnapshot, error in
    // ...
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
// Set up listener options
FIRSnapshotListenOptions *options = [[FIRSnapshotListenOptions alloc] init];
FIRSnapshotListenOptions *optionsWithSourceAndMetadata = 
                    [[options optionsWithIncludeMetadataChanges:YES] 
                      optionsWithSource:FIRListenSourceCache];
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
  addSnapshotListenerWithOptions:optionsWithSourceAndMetadata
  listener: ^ (FIRDocumentSnapshot * snapshot, NSError * error) {
    //…
  }
];

Kotlin+KTX

// Set up listener options
val options = SnapshotListenOptions.Builder()
  .setMetadataChanges(MetadataChanges.INCLUDE)
  .setSource(ListenSource.CACHE)
  .build();
db.collection("cities").document("SF")
  .addSnapshotListener(options) { snapshot, error ->
    //…
  }

Java

// Set up listener options
SnapshotListenOptions options = new SnapshotListenOptions.Builder()
  .setMetadataChanges(MetadataChanges.INCLUDE)
  .setSource(ListenSource.CACHE)
  .build();
db.collection("cities").document("SF").addSnapshotListener(options, new EventListener<DocumentSnapshot>() {
    //…
  });

Dart

// Not yet supported in this client library

Java
# Not yet supported in the Java client library
Python
// Not yet supported in Python client library
C++
// Not yet supported in the C++ client library
Node.js
// Not yet supported in the Node.js client library
Go
// Not yet supported in the Go client library
PHP
// Not yet supported in the PHP client library
Unity
// Not yet supported in the Unity client library
C#
// Not yet supported in the C# client library
Ruby
// Not yet supported in the Ruby client library

Słuchanie wielu dokumentów z kolekcji

Tak jak w przypadku dokumentów, możesz użyć onSnapshot() zamiast get(), aby nasłuchiwać w wynikach zapytania. Spowoduje to utworzenie zrzutu zapytania. Na przykład, aby posłuchać dokumenty o stanie CA:

Web

import { collection, query, where, onSnapshot } from "firebase/firestore";

const q = query(collection(db, "cities"), where("state", "==", "CA"));
const unsubscribe = onSnapshot(q, (querySnapshot) => {
  const cities = [];
  querySnapshot.forEach((doc) => {
      cities.push(doc.data().name);
  });
  console.log("Current cities in CA: ", cities.join(", "));
});

Web

db.collection("cities").where("state", "==", "CA")
    .onSnapshot((querySnapshot) => {
        var cities = [];
        querySnapshot.forEach((doc) => {
            cities.push(doc.data().name);
        });
        console.log("Current cities in CA: ", cities.join(", "));
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
db.collection("cities").whereField("state", isEqualTo: "CA")
  .addSnapshotListener { querySnapshot, error in
    guard let documents = querySnapshot?.documents else {
      print("Error fetching documents: \(error!)")
      return
    }
    let cities = documents.compactMap { $0["name"] }
    print("Current cities in CA: \(cities)")
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
[[[self.db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching documents: %@", error);
        return;
      }
      NSMutableArray *cities = [NSMutableArray array];
      for (FIRDocumentSnapshot *document in snapshot.documents) {
        [cities addObject:document.data[@"name"]];
      }
      NSLog(@"Current cities in CA: %@", cities);
    }];

Kotlin+KTX

db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener { value, e ->
        if (e != null) {
            Log.w(TAG, "Listen failed.", e)
            return@addSnapshotListener
        }

        val cities = ArrayList<String>()
        for (doc in value!!) {
            doc.getString("name")?.let {
                cities.add(it)
            }
        }
        Log.d(TAG, "Current cites in CA: $cities")
    }

Java

db.collection("cities")
        .whereEqualTo("state", "CA")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot value,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "Listen failed.", e);
                    return;
                }

                List<String> cities = new ArrayList<>();
                for (QueryDocumentSnapshot doc : value) {
                    if (doc.get("name") != null) {
                        cities.add(doc.getString("name"));
                    }
                }
                Log.d(TAG, "Current cites in CA: " + cities);
            }
        });

Dart

db
    .collection("cities")
    .where("state", isEqualTo: "CA")
    .snapshots()
    .listen((event) {
  final cities = [];
  for (var doc in event.docs) {
    cities.add(doc.data()["name"]);
  }
  print("cities in CA: ${cities.join(", ")}");
});
Java
db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener(
        new EventListener<QuerySnapshot>() {
          @Override
          public void onEvent(
              @Nullable QuerySnapshot snapshots, @Nullable FirestoreException e) {
            if (e != null) {
              System.err.println("Listen failed:" + e);
              return;
            }

            List<String> cities = new ArrayList<>();
            for (DocumentSnapshot doc : snapshots) {
              if (doc.get("name") != null) {
                cities.add(doc.getString("name"));
              }
            }
            System.out.println("Current cites in CA: " + cities);
          }
        });
Python

# Create an Event for notifying main thread.
callback_done = threading.Event()

# Create a callback on_snapshot function to capture changes
def on_snapshot(col_snapshot, changes, read_time):
    print("Callback received query snapshot.")
    print("Current cities in California:")
    for doc in col_snapshot:
        print(f"{doc.id}")
    callback_done.set()

col_query = db.collection("cities").where(filter=FieldFilter("state", "==", "CA"))

# Watch the collection query
query_watch = col_query.on_snapshot(on_snapshot)
C++
db->Collection("cities")
    .WhereEqualTo("state", FieldValue::String("CA"))
    .AddSnapshotListener([](const QuerySnapshot& snapshot, Error error, const std::string& errorMsg) {
      if (error == Error::kErrorOk) {
        std::vector<std::string> cities;
        std::cout << "Current cities in CA: " << error << std::endl;
        for (const DocumentSnapshot& doc : snapshot.documents()) {
          cities.push_back(doc.Get("name").string_value());
          std::cout << "" << cities.back() << std::endl;
        }
      } else {
        std::cout << "Listen failed: " << error << std::endl;
      }
    });
Node.js
const query = db.collection('cities').where('state', '==', 'CA');

const observer = query.onSnapshot(querySnapshot => {
  console.log(`Received query snapshot of size ${querySnapshot.size}`);
  // ...
}, err => {
  console.log(`Encountered error: ${err}`);
});
Go
import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/firestore"
	"google.golang.org/api/iterator"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// listenMultiple listens to a query, returning the names of all cities
// for a state.
func listenMultiple(ctx context.Context, w io.Writer, projectID, collection string) error {
	// projectID := "project-id"
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	it := client.Collection(collection).Where("state", "==", "CA").Snapshots(ctx)
	for {
		snap, err := it.Next()
		// DeadlineExceeded will be returned when ctx is cancelled.
		if status.Code(err) == codes.DeadlineExceeded {
			return nil
		}
		if err != nil {
			return fmt.Errorf("Snapshots.Next: %w", err)
		}
		if snap != nil {
			for {
				doc, err := snap.Documents.Next()
				if err == iterator.Done {
					break
				}
				if err != nil {
					return fmt.Errorf("Documents.Next: %w", err)
				}
				fmt.Fprintf(w, "Current cities in California: %v\n", doc.Ref.ID)
			}
		}
	}
}
PHP
// Not supported in the PHP client library
Unity
Query query = db.Collection("cities").WhereEqualTo("State", "CA");

ListenerRegistration listener = query.Listen(snapshot => {
  Debug.Log("Callback received query snapshot.");
  Debug.Log("Current cities in California:");
  foreach (DocumentSnapshot documentSnapshot in snapshot.Documents) {
    Debug.Log(documentSnapshot.Id);
  }
});
C#
CollectionReference citiesRef = db.Collection("cities");
Query query = db.Collection("cities").WhereEqualTo("State", "CA");

FirestoreChangeListener listener = query.Listen(snapshot =>
{
    Console.WriteLine("Callback received query snapshot.");
    Console.WriteLine("Current cities in California:");
    foreach (DocumentSnapshot documentSnapshot in snapshot.Documents)
    {
        Console.WriteLine(documentSnapshot.Id);
    }
});
Ruby
query = firestore.col(collection_path).where :state, :==, "CA"
docs = []

# Watch the collection query.
listener = query.listen do |snapshot|
  puts "Callback received query snapshot."
  puts "Current cities in California:"
  snapshot.docs.each do |doc|
    puts doc.document_id
    docs << doc
  end
end

Moduł obsługi zrzutów otrzyma nowy zrzut zapytania za każdym razem, gdy zapytanie zmian (tzn. kiedy dokument zostanie dodany, usunięty lub zmodyfikowany).

Wyświetlanie zmian między zrzutami

Często warto zobaczyć rzeczywiste zmiany w wynikach zapytań między zapytaniami zamiast używać całego zrzutu zapytania. Na przykład: warto zachować pamięć podręczną podczas dodawania, usuwania poszczególnych dokumentów zmodyfikowane.

Web

import { collection, query, where, onSnapshot } from "firebase/firestore";

const q = query(collection(db, "cities"), where("state", "==", "CA"));
const unsubscribe = onSnapshot(q, (snapshot) => {
  snapshot.docChanges().forEach((change) => {
    if (change.type === "added") {
        console.log("New city: ", change.doc.data());
    }
    if (change.type === "modified") {
        console.log("Modified city: ", change.doc.data());
    }
    if (change.type === "removed") {
        console.log("Removed city: ", change.doc.data());
    }
  });
});

Web

db.collection("cities").where("state", "==", "CA")
    .onSnapshot((snapshot) => {
        snapshot.docChanges().forEach((change) => {
            if (change.type === "added") {
                console.log("New city: ", change.doc.data());
            }
            if (change.type === "modified") {
                console.log("Modified city: ", change.doc.data());
            }
            if (change.type === "removed") {
                console.log("Removed city: ", change.doc.data());
            }
        });
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
db.collection("cities").whereField("state", isEqualTo: "CA")
  .addSnapshotListener { querySnapshot, error in
    guard let snapshot = querySnapshot else {
      print("Error fetching snapshots: \(error!)")
      return
    }
    snapshot.documentChanges.forEach { diff in
      if (diff.type == .added) {
        print("New city: \(diff.document.data())")
      }
      if (diff.type == .modified) {
        print("Modified city: \(diff.document.data())")
      }
      if (diff.type == .removed) {
        print("Removed city: \(diff.document.data())")
      }
    }
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
[[[self.db collectionWithPath:@"cities"] queryWhereField:@"state" isEqualTo:@"CA"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (snapshot == nil) {
        NSLog(@"Error fetching documents: %@", error);
        return;
      }
      for (FIRDocumentChange *diff in snapshot.documentChanges) {
        if (diff.type == FIRDocumentChangeTypeAdded) {
          NSLog(@"New city: %@", diff.document.data);
        }
        if (diff.type == FIRDocumentChangeTypeModified) {
          NSLog(@"Modified city: %@", diff.document.data);
        }
        if (diff.type == FIRDocumentChangeTypeRemoved) {
          NSLog(@"Removed city: %@", diff.document.data);
        }
      }
    }];

Kotlin+KTX

db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener { snapshots, e ->
        if (e != null) {
            Log.w(TAG, "listen:error", e)
            return@addSnapshotListener
        }

        for (dc in snapshots!!.documentChanges) {
            when (dc.type) {
                DocumentChange.Type.ADDED -> Log.d(TAG, "New city: ${dc.document.data}")
                DocumentChange.Type.MODIFIED -> Log.d(TAG, "Modified city: ${dc.document.data}")
                DocumentChange.Type.REMOVED -> Log.d(TAG, "Removed city: ${dc.document.data}")
            }
        }
    }

Java

db.collection("cities")
        .whereEqualTo("state", "CA")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot snapshots,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "listen:error", e);
                    return;
                }

                for (DocumentChange dc : snapshots.getDocumentChanges()) {
                    switch (dc.getType()) {
                        case ADDED:
                            Log.d(TAG, "New city: " + dc.getDocument().getData());
                            break;
                        case MODIFIED:
                            Log.d(TAG, "Modified city: " + dc.getDocument().getData());
                            break;
                        case REMOVED:
                            Log.d(TAG, "Removed city: " + dc.getDocument().getData());
                            break;
                    }
                }

            }
        });

Dart

db
    .collection("cities")
    .where("state", isEqualTo: "CA")
    .snapshots()
    .listen((event) {
  for (var change in event.docChanges) {
    switch (change.type) {
      case DocumentChangeType.added:
        print("New City: ${change.doc.data()}");
        break;
      case DocumentChangeType.modified:
        print("Modified City: ${change.doc.data()}");
        break;
      case DocumentChangeType.removed:
        print("Removed City: ${change.doc.data()}");
        break;
    }
  }
});
Java
db.collection("cities")
    .whereEqualTo("state", "CA")
    .addSnapshotListener(
        new EventListener<QuerySnapshot>() {
          @Override
          public void onEvent(
              @Nullable QuerySnapshot snapshots, @Nullable FirestoreException e) {
            if (e != null) {
              System.err.println("Listen failed: " + e);
              return;
            }

            for (DocumentChange dc : snapshots.getDocumentChanges()) {
              switch (dc.getType()) {
                case ADDED:
                  System.out.println("New city: " + dc.getDocument().getData());
                  break;
                case MODIFIED:
                  System.out.println("Modified city: " + dc.getDocument().getData());
                  break;
                case REMOVED:
                  System.out.println("Removed city: " + dc.getDocument().getData());
                  break;
                default:
                  break;
              }
            }
          }
        });
C++
db->Collection("cities")
    .WhereEqualTo("state", FieldValue::String("CA"))
    .AddSnapshotListener([](const QuerySnapshot& snapshot, Error error, const std::string& errorMsg) {
      if (error == Error::kErrorOk) {
        for (const DocumentChange& dc : snapshot.DocumentChanges()) {
          switch (dc.type()) {
            case DocumentChange::Type::kAdded:
              std::cout << "New city: "
                        << dc.document().Get("name").string_value() << std::endl;
              break;
            case DocumentChange::Type::kModified:
              std::cout << "Modified city: "
                        << dc.document().Get("name").string_value() << std::endl;
              break;
            case DocumentChange::Type::kRemoved:
              std::cout << "Removed city: "
                        << dc.document().Get("name").string_value() << std::endl;
              break;
          }
        }
      } else {
        std::cout << "Listen failed: " << error << std::endl;
      }
    });
Python

# Create an Event for notifying main thread.
delete_done = threading.Event()

# Create a callback on_snapshot function to capture changes
def on_snapshot(col_snapshot, changes, read_time):
    print("Callback received query snapshot.")
    print("Current cities in California: ")
    for change in changes:
        if change.type.name == "ADDED":
            print(f"New city: {change.document.id}")
        elif change.type.name == "MODIFIED":
            print(f"Modified city: {change.document.id}")
        elif change.type.name == "REMOVED":
            print(f"Removed city: {change.document.id}")
            delete_done.set()

col_query = db.collection("cities").where(filter=FieldFilter("state", "==", "CA"))

# Watch the collection query
query_watch = col_query.on_snapshot(on_snapshot)
Node.js
const observer = db.collection('cities').where('state', '==', 'CA')
  .onSnapshot(querySnapshot => {
    querySnapshot.docChanges().forEach(change => {
      if (change.type === 'added') {
        console.log('New city: ', change.doc.data());
      }
      if (change.type === 'modified') {
        console.log('Modified city: ', change.doc.data());
      }
      if (change.type === 'removed') {
        console.log('Removed city: ', change.doc.data());
      }
    });
  });
Go
import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/firestore"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// listenChanges listens to a query, returning the list of document changes.
func listenChanges(ctx context.Context, w io.Writer, projectID, collection string) error {
	// projectID := "project-id"
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	it := client.Collection(collection).Where("state", "==", "CA").Snapshots(ctx)
	for {
		snap, err := it.Next()
		// DeadlineExceeded will be returned when ctx is cancelled.
		if status.Code(err) == codes.DeadlineExceeded {
			return nil
		}
		if err != nil {
			return fmt.Errorf("Snapshots.Next: %w", err)
		}
		if snap != nil {
			for _, change := range snap.Changes {
				switch change.Kind {
				case firestore.DocumentAdded:
					fmt.Fprintf(w, "New city: %v\n", change.Doc.Data())
				case firestore.DocumentModified:
					fmt.Fprintf(w, "Modified city: %v\n", change.Doc.Data())
				case firestore.DocumentRemoved:
					fmt.Fprintf(w, "Removed city: %v\n", change.Doc.Data())
				}
			}
		}
	}
}
PHP
// Not supported in the PHP client library
Unity
Query query = db.Collection("cities").WhereEqualTo("State", "CA");

ListenerRegistration listener = query.Listen(snapshot =>
{
    foreach (DocumentChange change in snapshot.GetChanges())
    {
        if (change.ChangeType == DocumentChange.Type.Added)
        {
            Debug.Log(String.Format("New city: {0}", change.Document.Id));
        }
        else if (change.ChangeType == DocumentChange.Type.Modified)
        {
            Debug.Log(String.Format("Modified city: {0}", change.Document.Id));
        }
        else if (change.ChangeType == DocumentChange.Type.Removed)
        {
            Debug.Log(String.Format("Removed city: {0}", change.Document.Id));
        }
    }
});
C#
CollectionReference citiesRef = db.Collection("cities");
Query query = db.Collection("cities").WhereEqualTo("State", "CA");

FirestoreChangeListener listener = query.Listen(snapshot =>
{
    foreach (DocumentChange change in snapshot.Changes)
    {
        if (change.ChangeType.ToString() == "Added")
        {
            Console.WriteLine("New city: {0}", change.Document.Id);
        }
        else if (change.ChangeType.ToString() == "Modified")
        {
            Console.WriteLine("Modified city: {0}", change.Document.Id);
        }
        else if (change.ChangeType.ToString() == "Removed")
        {
            Console.WriteLine("Removed city: {0}", change.Document.Id);
        }
    }
});
Ruby
query = firestore.col(collection_path).where :state, :==, "CA"
added = []
modified = []
removed = []

# Watch the collection query.
listener = query.listen do |snapshot|
  puts "Callback received query snapshot."
  puts "Current cities in California:"
  snapshot.changes.each do |change|
    if change.added?
      puts "New city: #{change.doc.document_id}"
      added << snapshot
    elsif change.modified?
      puts "Modified city: #{change.doc.document_id}"
      modified << snapshot
    elsif change.removed?
      puts "Removed city: #{change.doc.document_id}"
      removed << snapshot
    end
  end
end

Stan początkowy może pochodzić bezpośrednio z serwera lub z lokalnego pamięci podręcznej. Jeśli w lokalnej pamięci podręcznej dostępny jest stan, zrzut zapytania została początkowo wypełniona danymi z pamięci podręcznej, a następnie zaktualizowany do danych serwera, gdy klient dogonił stan serwera.

Odłączanie detektora

Jeśli nie chcesz już nasłuchiwać danych, w odbiorniku, dzięki czemu wywołania zwrotne zdarzeń przestaną być wywoływane. Dzięki temu funkcja przestają używać przepustowości do odbierania aktualizacji. Przykład:

Web

import { collection, onSnapshot } from "firebase/firestore";

const unsubscribe = onSnapshot(collection(db, "cities"), () => {
  // Respond to data
  // ...
});

// Later ...

// Stop listening to changes
unsubscribe();

Web

var unsubscribe = db.collection("cities")
    .onSnapshot(() => {
      // Respond to data
      // ...
    });

// Later ...

// Stop listening to changes
unsubscribe();
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in
  // ...
}

// ...

// Stop listening to changes
listener.remove()
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
id<FIRListenerRegistration> listener = [[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      // ...
}];

// ...

// Stop listening to changes
[listener remove];

Kotlin+KTX

val query = db.collection("cities")
val registration = query.addSnapshotListener { snapshots, e ->
    // ...
}

// ...

// Stop listening to changes
registration.remove()

Java

Query query = db.collection("cities");
ListenerRegistration registration = query.addSnapshotListener(
        new EventListener<QuerySnapshot>() {
            // ...
        });

// ...

// Stop listening to changes
registration.remove();

Dart

final collection = db.collection("cities");
final listener = collection.snapshots().listen((event) {
  // ...
});
listener.cancel();
Java
Query query = db.collection("cities");
ListenerRegistration registration =
    query.addSnapshotListener(
        new EventListener<QuerySnapshot>() {
          // ...
        });

// ...

// Stop listening to changes
registration.remove();
Python
# Terminate watch on a document
doc_watch.unsubscribe()
C++
// Add a listener
Query query = db->Collection("cities");
ListenerRegistration registration = query.AddSnapshotListener(
    [](const QuerySnapshot& snapshot, Error error, const std::string& errorMsg) { /* ... */ });
// Stop listening to changes
registration.Remove();
Node.js
const unsub = db.collection('cities').onSnapshot(() => {
});

// ...

// Stop listening for changes
unsub();
Go
// Сontext with timeout stops listening to changes.
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
PHP
// Not supported in the PHP client library
Unity
listener.Stop();
C#
await listener.StopAsync();
Ruby
listener.stop

Obsługa błędów słuchania

Odsłuchanie może czasami się nie udać – na przykład ze względu na uprawnienia próbowałeś nasłuchiwać nieprawidłowego zapytania. (Dowiedz się więcej o poprawnych i nieprawidłowych zapytań). Aby rozwiązać ten problem: błędów, możesz podać wywołanie zwrotne w trakcie załączenia zrzutu słuchacz. Jeśli wystąpi błąd, detektor nie będzie już otrzymywać żadnych zdarzeń, nie musisz odłączać detektora.

Web

import { collection, onSnapshot } from "firebase/firestore";

const unsubscribe = onSnapshot(
  collection(db, "cities"), 
  (snapshot) => {
    // ...
  },
  (error) => {
    // ...
  });

Web

db.collection("cities")
    .onSnapshot((snapshot) => {
        // ...
    }, (error) => {
        // ...
    });
Swift
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
db.collection("cities")
  .addSnapshotListener { querySnapshot, error in
    if let error = error {
      print("Error retreiving collection: \(error)")
    }
  }
Objective-C
Uwaga: ten produkt nie jest dostępny na urządzeniach docelowych watchOS i wycinków aplikacji.
[[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (error != nil) {
        NSLog(@"Error retreving collection: %@", error);
      }
    }];

Kotlin+KTX

db.collection("cities")
    .addSnapshotListener { snapshots, e ->
        if (e != null) {
            Log.w(TAG, "listen:error", e)
            return@addSnapshotListener
        }

        for (dc in snapshots!!.documentChanges) {
            if (dc.type == DocumentChange.Type.ADDED) {
                Log.d(TAG, "New city: ${dc.document.data}")
            }
        }
    }

Java

db.collection("cities")
        .addSnapshotListener(new EventListener<QuerySnapshot>() {
            @Override
            public void onEvent(@Nullable QuerySnapshot snapshots,
                                @Nullable FirebaseFirestoreException e) {
                if (e != null) {
                    Log.w(TAG, "listen:error", e);
                    return;
                }

                for (DocumentChange dc : snapshots.getDocumentChanges()) {
                    if (dc.getType() == Type.ADDED) {
                        Log.d(TAG, "New city: " + dc.getDocument().getData());
                    }
                }

            }
        });

Dart

final docRef = db.collection("cities");
docRef.snapshots().listen(
      (event) => print("listener attached"),
      onError: (error) => print("Listen failed: $error"),
    );
Java
db.collection("cities")
    .addSnapshotListener(
        new EventListener<QuerySnapshot>() {
          @Override
          public void onEvent(
              @Nullable QuerySnapshot snapshots, @Nullable FirestoreException e) {
            if (e != null) {
              System.err.println("Listen failed: " + e);
              return;
            }

            for (DocumentChange dc : snapshots.getDocumentChanges()) {
              if (dc.getType() == Type.ADDED) {
                System.out.println("New city: " + dc.getDocument().getData());
              }
            }
          }
        });
Python
// Snippet coming soon
C++
// Snippet coming soon.
Node.js
db.collection('cities')
  .onSnapshot((snapshot) => {
    //...
  }, (error) => {
    //...
  });
Go
import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/firestore"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

// listenErrors demonstrates how to handle listening errors.
func listenErrors(ctx context.Context, w io.Writer, projectID, collection string) error {
	// projectID := "project-id"
	ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
	defer cancel()

	client, err := firestore.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("firestore.NewClient: %w", err)
	}
	defer client.Close()

	it := client.Collection(collection).Snapshots(ctx)
	for {
		snap, err := it.Next()
		// Canceled will be returned when ctx is cancelled and DeadlineExceeded will
		// be returned when ctx reaches its deadline.
		if e := status.Code(err); e == codes.Canceled || e == codes.DeadlineExceeded {
			return nil
		}
		if err != nil {
			return fmt.Errorf("Snapshots.Next: %w", err)
		}
		if snap != nil {
			for _, change := range snap.Changes {
				if change.Kind == firestore.DocumentAdded {
					fmt.Fprintf(w, "New city: %v\n", change.Doc.Data())
				}
			}
		}
	}
}
PHP
// Not supported in the PHP client library
Unity
ListenerRegistration registration =
db.Collection("cities").Listen(
  querySnapshot =>
  {
      // ...
  });

registration.ListenerTask.ContinueWithOnMainThread(
    listenerTask =>
    {
        if (listenerTask.IsFaulted)
        {
            Debug.LogError($"Listen failed: {listenerTask.Exception}");
            // ...
            // Handle the listener error.
            // ...
        }
    });
C#
// Snippet coming soon
Ruby
listener = firestore.col(collection_path).listen do |snapshot|
  snapshot.changes.each do |change|
    puts "New city: #{change.doc.document_id}" if change.added?
  end
end

# Register to be notified when unhandled errors occur.
listener.on_error do |error|
  puts "Listen failed: #{error.message}"
end

Co dalej?