با Cloud Firestore به‌روزرسانی‌های بی‌درنگ دریافت کنید

شما می‌توانید با استفاده از متد onSnapshot() به یک سند گوش دهید . یک فراخوانی اولیه با استفاده از فراخوانی که شما ارائه می‌دهید، بلافاصله یک snapshot از سند با محتوای فعلی سند ایجاد می‌کند. سپس، هر بار که محتوا تغییر می‌کند، فراخوانی دیگری snapshot سند را به‌روزرسانی می‌کند.

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());
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
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)")
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
[[[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

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"),
    );

اغلب، شما می‌خواهید رابط کاربری شما به تغییرات در محتوای یک سند یا مجموعه Firestore واکنش نشان دهد. می‌توانید این کار را با یک ویجت StreamBuilder که جریان snapshot 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(),
        );
      },
    );
  }
}
جاوا
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");
        }
      }
    });
پایتون

# 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)
سی++
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;
      }
    });
نود جی اس
const doc = db.collection('cities').doc('SF');

const observer = doc.onSnapshot(docSnapshot => {
  console.log(`Received doc snapshot: ${docSnapshot}`);
  // ...
}, err => {
  console.log(`Encountered error: ${err}`);
});
برو
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())
	}
}
پی اچ پی
// Not supported in the PHP client library
وحدت
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));
    }
});
سی شارپ
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);
        }
    }
});
روبی
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

رویدادهایی برای تغییرات محلی

نوشتن‌های محلی در برنامه شما بلافاصله شنونده‌های snapshot را فراخوانی می‌کنند. این به دلیل ویژگی مهمی به نام "جبران تأخیر" است. وقتی نوشتنی انجام می‌دهید، شنونده‌های شما قبل از ارسال داده‌ها به backend، از داده‌های جدید مطلع می‌شوند.

اسناد بازیابی شده دارای خاصیتی به metadata.hasPendingWrites هستند که نشان می‌دهد آیا سند دارای تغییرات محلی است که هنوز در backend نوشته نشده‌اند یا خیر. می‌توانید از این خاصیت برای تعیین منبع رویدادهای دریافت شده توسط snapshot listener خود استفاده کنید:

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());
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
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() ?? [:])")
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
[[[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

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"),
);
جاوا
# Not yet supported in the Java client library
پایتون
// Not yet supported in Python client library
سی++
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;
  }
});
نود جی اس
// Not yet supported in the Node.js client library
برو
// Not yet supported in the Go client library
پی اچ پی
// Not supported in the PHP client library
وحدت
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}");
  });
سی شارپ
// Not yet supported in the C# client library
روبی
// Not yet supported in the Ruby client library

رویدادهای مربوط به تغییرات متادیتا

هنگام گوش دادن به تغییرات در یک سند، مجموعه یا پرس و جو، می‌توانید گزینه‌هایی را برای کنترل جزئیات رویدادهایی که شنونده شما دریافت خواهد کرد، ارسال کنید.

به طور پیش‌فرض، شنونده‌ها از تغییراتی که فقط بر ابرداده تأثیر می‌گذارند، مطلع نمی‌شوند. در نظر بگیرید وقتی برنامه شما یک سند جدید می‌نویسد چه اتفاقی می‌افتد:

  1. یک رویداد تغییر بلافاصله با داده‌های جدید اجرا می‌شود. سند هنوز در backend نوشته نشده است، بنابراین پرچم "pending write" true است.
  2. سند در backend نوشته شده است.
  3. بخش مدیریت، کلاینت را از نوشتن موفقیت‌آمیز مطلع می‌کند. هیچ تغییری در داده‌های سند ایجاد نشده است، اما به دلیل اینکه پرچم «در انتظار نوشتن» اکنون false است، تغییری در فراداده رخ داده است.

اگر می‌خواهید هنگام تغییر ابرداده سند یا پرس‌وجو، رویدادهای snapshot را دریافت کنید، هنگام اتصال شنونده خود، یک شیء listen options ارسال کنید.

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) => {
        // ...
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
// Listen to document metadata.
db.collection("cities").document("SF")
  .addSnapshotListener(includeMetadataChanges: true) { documentSnapshot, error in
    // ...
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
// Listen for metadata changes.
[[[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]
    addSnapshotListenerWithIncludeMetadataChanges:YES
                                         listener:^(FIRDocumentSnapshot *snapshot, NSError *error) {
   // ...
}];

Kotlin

// 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) {
  // ...
});
جاوا
// Not yet supported in the Java client library
پایتون
// Not yet supported in Python client library
سی++
DocumentReference doc_ref = db->Collection("cities").Document("SF");
doc_ref.AddSnapshotListener(
    MetadataChanges::kInclude,
    [](const DocumentSnapshot& snapshot, Error error, const std::string& errorMsg) { /* ... */ });
نود جی اس
// Not yet supported the Node.js client library
برو
// Not yet supported in the Go client library
پی اچ پی
// Not supported in the PHP client library
وحدت
DocumentReference docRef = db.Collection("cities").Document("SF");
docRef.Listen(MetadataChanges.Include, snapshot =>
{
    // ...
});
سی شارپ
// Not yet supported in the C# client library
روبی
// Not yet supported in the Ruby client library

پیکربندی شنونده‌ها فقط برای تغییرات محلی

شنوندگان اسنپ‌شات Cloud Firestore یک اسنپ‌شات اولیه از حافظه پنهان محلی می‌گیرند و همزمان داده‌های مربوطه را از سرور دریافت می‌کنند.

در برخی موارد، ممکن است نخواهید واکشی‌های بعدی از سرور انجام شود. SDKهای کلاینت به شما این امکان را می‌دهند که شنونده‌ها را طوری پیکربندی کنید که فقط با توجه به داده‌های موجود در حافظه پنهان محلی اجرا شوند. این به شما کمک می‌کند از فراخوانی‌های غیرضروری سرور و هزینه‌های آنها جلوگیری کنید و از حافظه پنهان سمت کلاینت که داده‌های محلی و جهش‌ها را منعکس می‌کند، بهره ببرید.

در اینجا، گزینه‌های snapshot در کد کلاینت تنظیم شده‌اند تا فقط امکان گوش دادن به تغییرات محلی را فراهم کنند.

Web

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

Web

// Not yet supported in the Web namespaced API

سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
// Set up listener options
let options = SnapshotListenOptions()
    .withSource(ListenSource.cache)
    .withIncludeMetadataChanges(true)
db.collection("cities").document("SF")
  .addSnapshotListener(options: options) { documentSnapshot, error in
    // ...
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
// 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

// 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

جاوا
# Not yet supported in the Java client library
پایتون
// Not yet supported in Python client library
سی++
// Not yet supported in the C++ client library
نود جی اس
// Not yet supported in the Node.js client library
برو
// Not yet supported in the Go client library
پی اچ پی
// Not yet supported in the PHP client library
وحدت
// Not yet supported in the Unity client library
سی شارپ
// Not yet supported in the C# client library
روبی
// Not yet supported in the Ruby client library

گوش دادن به چندین سند در یک مجموعه

همانند اسناد، می‌توانید به جای get() از onSnapshot() برای گوش دادن به نتایج یک پرس‌وجو استفاده کنید. این یک تصویر لحظه‌ای از پرس‌وجو ایجاد می‌کند. به عنوان مثال، برای گوش دادن به اسناد با وضعیت 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(", "));
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
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)")
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
[[[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

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(", ")}");
});
جاوا
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);
          }
        });
پایتون

# 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)
سی++
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;
      }
    });
نود جی اس
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}`);
});
برو
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)
			}
		}
	}
}
پی اچ پی
// Not supported in the PHP client library
وحدت
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);
  }
});
سی شارپ
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);
    }
});
روبی
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

هر بار که نتایج پرس‌وجو تغییر کند (یعنی وقتی یک سند اضافه، حذف یا اصلاح می‌شود)، کنترل‌کننده‌ی اسنپ‌شات، یک اسنپ‌شات پرس‌وجوی جدید دریافت می‌کند.

مشاهده تغییرات بین اسنپ‌شات‌ها

اغلب مفید است که تغییرات واقعی نتایج پرس‌وجو را بین اسنپ‌شات‌های پرس‌وجو مشاهده کنید، نه اینکه صرفاً از کل اسنپ‌شات پرس‌وجو استفاده کنید. به عنوان مثال، ممکن است بخواهید همزمان با اضافه شدن، حذف شدن و تغییر اسناد، یک کش (cache) را حفظ کنید.

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());
            }
        });
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
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())")
      }
    }
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
[[[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

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;
    }
  }
});
جاوا
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;
              }
            }
          }
        });
سی++
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;
      }
    });
پایتون

# 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)
نود جی اس
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());
      }
    });
  });
برو
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())
				}
			}
		}
	}
}
پی اچ پی
// Not supported in the PHP client library
وحدت
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));
        }
    }
});
سی شارپ
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);
        }
    }
});
روبی
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

وضعیت اولیه می‌تواند مستقیماً از سرور یا از یک حافظه پنهان محلی دریافت شود. اگر وضعیتی در حافظه پنهان محلی موجود باشد، تصویر لحظه‌ای پرس‌وجو در ابتدا با داده‌های ذخیره‌شده پر می‌شود و سپس هنگامی که کلاینت به وضعیت سرور دسترسی پیدا کرد، با داده‌های سرور به‌روزرسانی می‌شود.

جدا کردن یک شنونده

وقتی دیگر علاقه‌ای به گوش دادن به داده‌های خود ندارید، باید شنونده خود را جدا کنید تا فراخوانی رویدادهای برگشتی شما متوقف شود. این به کلاینت اجازه می‌دهد تا استفاده از پهنای باند برای دریافت به‌روزرسانی‌ها را متوقف کند. برای مثال:

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();
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
let listener = db.collection("cities").addSnapshotListener { querySnapshot, error in
  // ...
}

// ...

// Stop listening to changes
listener.remove()
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
id<FIRListenerRegistration> listener = [[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      // ...
}];

// ...

// Stop listening to changes
[listener remove];

Kotlin

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();
جاوا
Query query = db.collection("cities");
ListenerRegistration registration =
    query.addSnapshotListener(
        new EventListener<QuerySnapshot>() {
          // ...
        });

// ...

// Stop listening to changes
registration.remove();
پایتون
# Terminate watch on a document
doc_watch.unsubscribe()
سی++
// 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();
نود جی اس
const unsub = db.collection('cities').onSnapshot(() => {
});

// ...

// Stop listening for changes
unsub();
برو
// Сontext with timeout stops listening to changes.
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
پی اچ پی
// Not supported in the PHP client library
وحدت
listener.Stop();
سی شارپ
await listener.StopAsync();
روبی
listener.stop

مدیریت خطاهای شنیداری

ممکن است گاهی اوقات شنود با شکست مواجه شود - برای مثال، به دلیل مجوزهای امنیتی، یا اگر سعی کرده‌اید به یک پرس‌وجوی نامعتبر گوش دهید. (درباره پرس‌وجوهای معتبر و نامعتبر بیشتر بدانید.) برای مدیریت این شکست‌ها، می‌توانید هنگام اتصال شنودگر snapshot خود، یک callback خطا ارائه دهید. پس از یک خطا، شنودگر دیگر هیچ رویدادی را دریافت نمی‌کند و نیازی به جدا کردن شنودگر شما نیست.

Web

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

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

Web

db.collection("cities")
    .onSnapshot((snapshot) => {
        // ...
    }, (error) => {
        // ...
    });
سویفت
توجه: این محصول در watchOS و App Clip موجود نیست.
db.collection("cities")
  .addSnapshotListener { querySnapshot, error in
    if let error = error {
      print("Error retreiving collection: \(error)")
    }
  }
هدف-سی
توجه: این محصول در watchOS و App Clip موجود نیست.
[[self.db collectionWithPath:@"cities"]
    addSnapshotListener:^(FIRQuerySnapshot *snapshot, NSError *error) {
      if (error != nil) {
        NSLog(@"Error retreving collection: %@", error);
      }
    }];

Kotlin

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"),
    );
جاوا
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());
              }
            }
          }
        });
پایتون
// Snippet coming soon
سی++
// Snippet coming soon.
نود جی اس
db.collection('cities')
  .onSnapshot((snapshot) => {
    //...
  }, (error) => {
    //...
  });
برو
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())
				}
			}
		}
	}
}
پی اچ پی
// Not supported in the PHP client library
وحدت
ListenerRegistration registration =
db.Collection("cities").Listen(
  querySnapshot =>
  {
      // ...
  });

registration.ListenerTask.ContinueWithOnMainThread(
    listenerTask =>
    {
        if (listenerTask.IsFaulted)
        {
            Debug.LogError($"Listen failed: {listenerTask.Exception}");
            // ...
            // Handle the listener error.
            // ...
        }
    });
سی شارپ
// Snippet coming soon
روبی
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

قدم بعدی چیست؟