Catch up on highlights from Firebase at Google I/O 2023. Learn more

بناء تواجد في Cloud Firestore

اعتمادًا على نوع التطبيق الذي تنشئه ، قد تجد أنه من المفيد اكتشاف المستخدمين أو الأجهزة النشطة على الإنترنت - وهو ما يُعرف باسم اكتشاف "التواجد".

على سبيل المثال ، إذا كنت تنشئ تطبيقًا مثل شبكة اجتماعية أو تنشر مجموعة من أجهزة إنترنت الأشياء ، فيمكنك استخدام هذه المعلومات لعرض قائمة بالأصدقاء المتصلين بالإنترنت ومجانيين للدردشة ، أو فرز أجهزة إنترنت الأشياء الخاصة بك حسب "آخر ظهور . "

لا يدعم Cloud Firestore التواجد أصلاً ، ولكن يمكنك الاستفادة من منتجات Firebase الأخرى لبناء نظام تواجد.

الحل: وظائف السحابة مع قاعدة بيانات Realtime

لتوصيل Cloud Firestore بميزة التواجد الأصلي لقاعدة بيانات Firebase Realtime ، استخدم وظائف السحابة.

استخدم Realtime Database للإبلاغ عن حالة الاتصال ، ثم استخدم وظائف السحابة لعكس تلك البيانات في Cloud Firestore.

استخدام التواجد في Realtime Database

أولاً ، ضع في اعتبارك كيفية عمل نظام الحضور التقليدي في Realtime Database.

الويب

// Fetch the current user's ID from Firebase Authentication.
var uid = firebase.auth().currentUser.uid;

// Create a reference to this user's specific status node.
// This is where we will store data about being online/offline.
var userStatusDatabaseRef = firebase.database().ref('/status/' + uid);

// We'll create two constants which we will write to 
// the Realtime database when this device is offline
// or online.
var isOfflineForDatabase = {
    state: 'offline',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

var isOnlineForDatabase = {
    state: 'online',
    last_changed: firebase.database.ServerValue.TIMESTAMP,
};

// Create a reference to the special '.info/connected' path in 
// Realtime Database. This path returns `true` when connected
// and `false` when disconnected.
firebase.database().ref('.info/connected').on('value', function(snapshot) {
    // If we're not currently connected, don't do anything.
    if (snapshot.val() == false) {
        return;
    };

    // If we are currently connected, then use the 'onDisconnect()' 
    // method to add a set which will only trigger once this 
    // client has disconnected by closing the app, 
    // losing internet, or any other means.
    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        // The promise returned from .onDisconnect().set() will
        // resolve as soon as the server acknowledges the onDisconnect() 
        // request, NOT once we've actually disconnected:
        // https://firebase.google.com/docs/reference/js/firebase.database.OnDisconnect

        // We can now safely set ourselves as 'online' knowing that the
        // server will mark us as offline once we lose connection.
        userStatusDatabaseRef.set(isOnlineForDatabase);
    });
});

هذا المثال هو نظام وجود قاعدة بيانات Realtime الكامل. إنه يتعامل مع حالات قطع الاتصال المتعددة ، والأعطال وما إلى ذلك.

الاتصال بـ Cloud Firestore

لتنفيذ حل مشابه في Cloud Firestore ، استخدم نفس رمز قاعدة بيانات Realtime ، ثم استخدم وظائف السحابة للحفاظ على مزامنة Realtime Database و Cloud Firestore.

إذا لم تكن قد قمت بذلك بالفعل ، فأضف Realtime Database إلى مشروعك وقم بتضمين حل التواجد أعلاه.

بعد ذلك ، ستقوم بمزامنة حالة التواجد مع Cloud Firestore من خلال الطرق التالية:

  1. محليًا ، إلى ذاكرة التخزين المؤقت Cloud Firestore الخاصة بالجهاز غير المتصل بالإنترنت حتى يعرف التطبيق أنه غير متصل بالإنترنت.
  2. على الصعيد العالمي ، باستخدام وظيفة السحابة حتى تعرف جميع الأجهزة الأخرى التي تصل إلى Cloud Firestore أن هذا الجهاز المحدد غير متصل بالإنترنت.

تحديث ذاكرة التخزين المؤقت المحلية لـ Cloud Firestore

دعنا نلقي نظرة على التغييرات المطلوبة لتحقيق المشكلة الأولى - تحديث ذاكرة التخزين المؤقت المحلية لـ Cloud Firestore.

الويب

// ...
var userStatusFirestoreRef = firebase.firestore().doc('/status/' + uid);

// Firestore uses a different server timestamp value, so we'll 
// create two more constants for Firestore state.
var isOfflineForFirestore = {
    state: 'offline',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

var isOnlineForFirestore = {
    state: 'online',
    last_changed: firebase.firestore.FieldValue.serverTimestamp(),
};

firebase.database().ref('.info/connected').on('value', function(snapshot) {
    if (snapshot.val() == false) {
        // Instead of simply returning, we'll also set Firestore's state
        // to 'offline'. This ensures that our Firestore cache is aware
        // of the switch to 'offline.'
        userStatusFirestoreRef.set(isOfflineForFirestore);
        return;
    };

    userStatusDatabaseRef.onDisconnect().set(isOfflineForDatabase).then(function() {
        userStatusDatabaseRef.set(isOnlineForDatabase);

        // We'll also add Firestore set here for when we come online.
        userStatusFirestoreRef.set(isOnlineForFirestore);
    });
});

من خلال هذه التغييرات ، تأكدنا الآن من أن حالة Cloud Firestore المحلية ستعكس دائمًا حالة الاتصال / عدم الاتصال للجهاز. هذا يعني أنه يمكنك الاستماع إلى مستند /status/{uid} واستخدام البيانات لتغيير واجهة المستخدم لتعكس حالة الاتصال.

الويب

userStatusFirestoreRef.onSnapshot(function(doc) {
    var isOnline = doc.data().state == 'online';
    // ... use isOnline
});

تحديث Cloud Firestore عالميًا

على الرغم من أن تطبيقنا يبلغ بشكل صحيح عن التواجد عبر الإنترنت لنفسه ، إلا أن هذه الحالة لن تكون دقيقة في تطبيقات Cloud Firestore الأخرى حتى الآن لأن كتابة الحالة "غير المتصلة" هي محلية فقط ولن تتم مزامنتها عند استعادة الاتصال. لمواجهة ذلك ، سنستخدم وظيفة السحاب التي تراقب مسار status/{uid} في قاعدة بيانات Realtime. عندما تتغير قيمة Realtime Database ، ستتم مزامنة القيمة مع Cloud Firestore بحيث تكون حالات جميع المستخدمين صحيحة.

Node.js

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();

// Since this code will be running in the Cloud Functions environment
// we call initialize Firestore without any arguments because it
// detects authentication from the environment.
const firestore = admin.firestore();

// Create a new function which is triggered on changes to /status/{uid}
// Note: This is a Realtime Database trigger, *not* Firestore.
exports.onUserStatusChanged = functions.database.ref('/status/{uid}').onUpdate(
    async (change, context) => {
      // Get the data written to Realtime Database
      const eventStatus = change.after.val();

      // Then use other event data to create a reference to the
      // corresponding Firestore document.
      const userStatusFirestoreRef = firestore.doc(`status/${context.params.uid}`);

      // It is likely that the Realtime Database change that triggered
      // this event has already been overwritten by a fast change in
      // online / offline status, so we'll re-read the current data
      // and compare the timestamps.
      const statusSnapshot = await change.after.ref.once('value');
      const status = statusSnapshot.val();
      functions.logger.log(status, eventStatus);
      // If the current timestamp for this data is newer than
      // the data that triggered this event, we exit this function.
      if (status.last_changed > eventStatus.last_changed) {
        return null;
      }

      // Otherwise, we convert the last_changed field to a Date
      eventStatus.last_changed = new Date(eventStatus.last_changed);

      // ... and write it to Firestore.
      return userStatusFirestoreRef.set(eventStatus);
    });

بمجرد نشر هذه الوظيفة ، سيكون لديك نظام تواجد كامل يعمل مع Cloud Firestore. يوجد أدناه مثال للمراقبة لأي مستخدم يتصل بالإنترنت أو غير متصل باستخدام استعلام where() .

الويب

firebase.firestore().collection('status')
    .where('state', '==', 'online')
    .onSnapshot(function(snapshot) {
        snapshot.docChanges().forEach(function(change) {
            if (change.type === 'added') {
                var msg = 'User ' + change.doc.id + ' is online.';
                console.log(msg);
                // ...
            }
            if (change.type === 'removed') {
                var msg = 'User ' + change.doc.id + ' is offline.';
                console.log(msg);
                // ...
            }
        });
    });

محددات

يعد استخدام Realtime Database لإضافة التواجد إلى تطبيق Cloud Firestore الخاص بك قابلاً للتطوير وفعالًا ولكن به بعض القيود:

  • التنبيه - عند الاستماع إلى التغييرات في الوقت الفعلي في Cloud Firestore ، من المحتمل أن يؤدي هذا الحل إلى إجراء تغييرات متعددة. إذا أدت هذه التغييرات إلى حدوث أحداث أكثر مما تريد ، فقم يدويًا بإلغاء أحداث Cloud Firestore.
  • الاتصال - يقيس هذا التنفيذ الاتصال بقاعدة بيانات Realtime ، وليس Cloud Firestore. إذا لم تكن حالة الاتصال بكل قاعدة بيانات هي نفسها ، فقد يُبلغ هذا الحل عن حالة تواجد غير صحيحة.
  • Android - في نظام Android ، يتم قطع اتصال Realtime Database بالواجهة الخلفية بعد 60 ثانية من عدم النشاط. عدم النشاط يعني عدم وجود مستمعين مفتوحين أو عمليات معلقة. للإبقاء على الاتصال مفتوحًا ، نوصيك بإضافة مستمع حدث ذي قيمة إلى مسار بجانب .info/connected . على سبيل المثال ، يمكنك تنفيذ FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() في بداية كل جلسة. لمزيد من المعلومات ، راجع الكشف عن حالة الاتصال .