Erhalten Sie Echtzeit-Updates mit Cloud Firestore

Sie können ein Dokument mit der Methode onSnapshot() anhören . Ein erster Aufruf mit dem von Ihnen bereitgestellten Rückruf erstellt sofort einen Dokument-Snapshot mit dem aktuellen Inhalt des einzelnen Dokuments. Jedes Mal, wenn sich der Inhalt ändert, aktualisiert ein weiterer Aufruf den Dokument-Snapshot.

Web modular API

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

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

Web namespaced API

db.collection("cities").doc("SF")
    .onSnapshot((doc) => {
        console.log("Current data: ", doc.data());
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
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)")
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
[[[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"),
    );

Häufig möchten Sie, dass Ihre Benutzeroberfläche auf Änderungen im Inhalt eines Firestore-Dokuments oder einer Firestore-Sammlung reagiert. Sie können dies mit einem StreamBuilder Widget tun, das den Firestore-Snapshot-Stream nutzt:

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}`);
});
Gehen
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
Einheit
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);
        }
    }
});
Rubin
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

Ereignisse für lokale Änderungen

Lokale Schreibvorgänge in Ihrer App rufen sofort Snapshot-Listener auf. Dies liegt an einer wichtigen Funktion namens „Latenzkompensation“. Wenn Sie einen Schreibvorgang durchführen, werden Ihre Listener über die neuen Daten benachrichtigt , bevor die Daten an das Backend gesendet werden.

Abgerufene Dokumente verfügen über eine metadata.hasPendingWrites Eigenschaft, die angibt, ob das Dokument lokale Änderungen aufweist, die noch nicht in das Backend geschrieben wurden. Mit dieser Eigenschaft können Sie die Quelle der von Ihrem Snapshot-Listener empfangenen Ereignisse ermitteln:

Web modular API

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 namespaced API

db.collection("cities").doc("SF")
    .onSnapshot((doc) => {
        var source = doc.metadata.hasPendingWrites ? "Local" : "Server";
        console.log(source, " data: ", doc.data());
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
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() ?? [:])")
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
[[[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
Gehen
// Not yet supported in the Go client library
PHP
// Not supported in the PHP client library
Einheit
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
Rubin
// Not yet supported in the Ruby client library

Ereignisse für Metadatenänderungen

Wenn Sie auf Änderungen an einem Dokument, einer Sammlung oder einer Abfrage warten, können Sie Optionen übergeben, um die Granularität der Ereignisse zu steuern, die Ihr Listener empfängt.

Standardmäßig werden Listener nicht über Änderungen benachrichtigt, die nur Metadaten betreffen. Überlegen Sie, was passiert, wenn Ihre App ein neues Dokument schreibt:

  1. Mit den neuen Daten wird sofort ein Änderungsereignis ausgelöst. Das Dokument wurde noch nicht in das Backend geschrieben, daher ist das Flag „Ausstehende Schreibvorgänge“ auf true gesetzt.
  2. Das Dokument wird ins Backend geschrieben.
  3. Das Backend benachrichtigt den Client über den erfolgreichen Schreibvorgang. Es gibt keine Änderung an den Dokumentdaten, aber es gibt eine Metadatenänderung, da das Flag „Ausstehende Schreibvorgänge“ jetzt false gesetzt ist.

Wenn Sie Snapshot-Ereignisse empfangen möchten, wenn sich das Dokument oder die Abfragemetadaten ändern, übergeben Sie beim Anhängen Ihres Listeners ein Listen-Optionsobjekt:

Web modular API

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

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

Web namespaced API

db.collection("cities").doc("SF")
    .onSnapshot({
        // Listen for document metadata changes
        includeMetadataChanges: true
    }, (doc) => {
        // ...
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
// Listen to document metadata.
db.collection("cities").document("SF")
  .addSnapshotListener(includeMetadataChanges: true) { documentSnapshot, error in
    // ...
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
// 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
Gehen
// Not yet supported in the Go client library
PHP
// Not supported in the PHP client library
Einheit
DocumentReference docRef = db.Collection("cities").Document("SF");
docRef.Listen(MetadataChanges.Include, snapshot =>
{
    // ...
});
C#
// Not yet supported in the C# client library
Rubin
// Not yet supported in the Ruby client library

Hören Sie sich mehrere Dokumente in einer Sammlung an

Wie bei Dokumenten können Sie onSnapshot() anstelle von get() verwenden, um die Ergebnisse einer Abfrage abzuhören. Dadurch wird ein Abfrage-Snapshot erstellt. Um beispielsweise die Dokumente mit der staatlichen CA anzuhören:

Web modular API

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 namespaced API

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(", "));
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
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)")
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
[[[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}`);
});
Gehen
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
Einheit
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);
    }
});
Rubin
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

Der Snapshot-Handler erhält jedes Mal einen neuen Abfrage-Snapshot, wenn sich die Abfrageergebnisse ändern (d. h. wenn ein Dokument hinzugefügt, entfernt oder geändert wird).

Zeigen Sie Änderungen zwischen Snapshots an

Es ist oft nützlich, die tatsächlichen Änderungen an den Abfrageergebnissen zwischen Abfrage-Snapshots zu sehen, anstatt einfach den gesamten Abfrage-Snapshot zu verwenden. Beispielsweise möchten Sie möglicherweise einen Cache verwalten, während einzelne Dokumente hinzugefügt, entfernt und geändert werden.

Web modular API

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 namespaced API

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());
            }
        });
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
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())")
      }
    }
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
[[[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());
      }
    });
  });
Gehen
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
Einheit
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);
        }
    }
});
Rubin
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

Der Ausgangszustand kann direkt vom Server oder aus einem lokalen Cache stammen. Wenn in einem lokalen Cache ein Status verfügbar ist, wird der Abfrage-Snapshot zunächst mit den zwischengespeicherten Daten gefüllt und dann mit den Daten des Servers aktualisiert, wenn der Client den Status des Servers eingeholt hat.

Trennen Sie einen Zuhörer

Wenn Sie nicht mehr daran interessiert sind, Ihre Daten abzuhören, müssen Sie Ihren Listener trennen, damit Ihre Ereignisrückrufe nicht mehr aufgerufen werden. Dadurch kann der Client keine Bandbreite mehr für den Empfang von Updates verwenden. Zum Beispiel:

Web modular API

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

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

// Later ...

// Stop listening to changes
unsubscribe();

Web namespaced API

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

// Later ...

// Stop listening to changes
unsubscribe();
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in
  // ...
}

// ...

// Stop listening to changes
listener.remove()
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
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();
Gehen
// С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
Einheit
listener.Stop();
C#
await listener.StopAsync();
Rubin
listener.stop

Behandeln Sie Abhörfehler

Ein Abhören kann gelegentlich fehlschlagen – beispielsweise aufgrund von Sicherheitsberechtigungen oder wenn Sie versucht haben, eine ungültige Abfrage abzuhören. (Erfahren Sie mehr über gültige und ungültige Abfragen .) Um diese Fehler zu behandeln, können Sie einen Fehlerrückruf bereitstellen, wenn Sie Ihren Snapshot-Listener anhängen. Nach einem Fehler empfängt der Listener keine Ereignisse mehr und es besteht keine Notwendigkeit, Ihren Listener zu trennen.

Web modular API

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

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

Web namespaced API

db.collection("cities")
    .onSnapshot((snapshot) => {
        // ...
    }, (error) => {
        // ...
    });
Schnell
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
db.collection("cities")
  .addSnapshotListener { querySnapshot, error in
    if let error = error {
      print("Error retreiving collection: \(error)")
    }
  }
Ziel c
Hinweis: Dieses Produkt ist nicht auf watchOS- und App Clip-Zielen verfügbar.
[[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) => {
    //...
  });
Gehen
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
Einheit
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
Rubin
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

Was kommt als nächstes