Check out what’s new from Firebase at Google I/O 2022. Learn more

สร้างสถานะใน Cloud Firestore

ขึ้นอยู่กับประเภทของแอปที่คุณกำลังสร้าง คุณอาจพบว่ามีประโยชน์ในการตรวจสอบว่าผู้ใช้หรืออุปกรณ์ใดของคุณกำลังออนไลน์อยู่ หรือที่เรียกว่าการตรวจจับ "การแสดงตน"

ตัวอย่างเช่น หากคุณกำลังสร้างแอป เช่น โซเชียลเน็ตเวิร์ก หรือปรับใช้กลุ่มอุปกรณ์ IoT คุณสามารถใช้ข้อมูลนี้เพื่อแสดงรายการเพื่อนที่ออนไลน์และสนทนาได้ฟรี หรือจัดเรียงอุปกรณ์ IoT ของคุณตาม "ดูล่าสุด" ."

Cloud Firestore ไม่รองรับการแสดงตนโดยกำเนิด แต่คุณสามารถใช้ประโยชน์จากผลิตภัณฑ์ Firebase อื่นๆ เพื่อสร้างระบบการแสดงตนได้

วิธีแก้ไข: ฟังก์ชันคลาวด์พร้อมฐานข้อมูลแบบเรียลไทม์

หากต้องการเชื่อมต่อ Cloud Firestore กับฟีเจอร์การแสดงตนดั้งเดิมของฐานข้อมูล Firebase Realtime ให้ใช้ Cloud Functions

ใช้ Realtime Database เพื่อรายงานสถานะการเชื่อมต่อ จากนั้นใช้ Cloud Functions เพื่อมิเรอร์ข้อมูลนั้นไปยัง Cloud Firestore

การใช้สถานะในฐานข้อมูลเรียลไทม์

ขั้นแรก ให้พิจารณาว่าระบบการแสดงตนแบบเดิมทำงานอย่างไรในฐานข้อมูลเรียลไทม์

เว็บ

// 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);
    });
});

ตัวอย่างนี้คือระบบการแสดงตนของฐานข้อมูลเรียลไทม์ที่สมบูรณ์ มันจัดการการตัดการเชื่อมต่อหลายครั้ง การขัดข้องและอื่น ๆ

กำลังเชื่อมต่อกับ Cloud Firestore

ในการใช้โซลูชันที่คล้ายกันใน Cloud Firestore ให้ใช้โค้ด Realtime Database เดียวกัน จากนั้นใช้ Cloud Functions เพื่อให้ Realtime Database และ Cloud Firestore ซิงค์กัน

หากคุณยังไม่ได้ดำเนินการ ให้เพิ่ม ฐานข้อมูลเรียลไทม์ ในโครงการของคุณและรวมโซลูชันการแสดงตนด้านบน

ถัดไป คุณจะต้องซิงโครไนซ์สถานะการแสดงตนกับ Cloud Firestore โดยใช้วิธีการต่อไปนี้:

  1. ภายในเครื่องไปยังแคช Cloud Firestore ของอุปกรณ์ออฟไลน์เพื่อให้แอปรู้ว่าออฟไลน์อยู่
  2. ทั่วโลกโดยใช้ Cloud Function เพื่อให้อุปกรณ์อื่นๆ ทั้งหมดที่เข้าถึง 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} และใช้ข้อมูลเพื่อเปลี่ยน UI ของคุณเพื่อสะท้อนสถานะการเชื่อมต่อ

เว็บ

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

กำลังอัปเดต Cloud Firestore ทั่วโลก

แม้ว่าแอปพลิเคชันของเราจะรายงานสถานะออนไลน์ของตัวเองอย่างถูกต้อง แต่สถานะนี้จะไม่ถูกต้องในแอป Cloud Firestore อื่น ๆ เนื่องจากการเขียนสถานะ "ออฟไลน์" ของเราอยู่ในเครื่องเท่านั้นและจะไม่ซิงค์เมื่อมีการกู้คืนการเชื่อมต่อ เพื่อตอบโต้ เราจะใช้ Cloud Function ซึ่งคอยดู status/{uid} เส้นทางใน Realtime Database เมื่อค่า 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 ของคุณนั้นสามารถปรับขนาดได้และมีประสิทธิภาพ แต่มีข้อจำกัดบางประการ:

  • Debouncing - เมื่อรับฟังการเปลี่ยนแปลงแบบเรียลไทม์ใน Cloud Firestore โซลูชันนี้มีแนวโน้มที่จะทริกเกอร์การเปลี่ยนแปลงหลายรายการ หากการเปลี่ยนแปลงเหล่านี้ทริกเกอร์เหตุการณ์มากกว่าที่คุณต้องการ ให้ดีบักเหตุการณ์ Cloud Firestore ด้วยตนเอง
  • การ เชื่อม ต่อ - การใช้งานนี้จะวัดการเชื่อมต่อกับฐานข้อมูลเรียลไทม์ ไม่ใช่ Cloud Firestore ถ้าสถานะการเชื่อมต่อกับแต่ละฐานข้อมูลไม่เหมือนกัน โซลูชันนี้อาจรายงานสถานะการแสดงตนที่ไม่ถูกต้อง
  • Android - บน Android ฐานข้อมูลเรียลไทม์จะยกเลิกการเชื่อมต่อจากแบ็กเอนด์หลังจากไม่มีการใช้งาน 60 วินาที การไม่ใช้งานหมายความว่าไม่มีผู้ฟังที่เปิดอยู่หรือการดำเนินการที่รอดำเนินการ เพื่อให้การเชื่อมต่อเปิดอยู่เสมอ เราขอแนะนำให้คุณเพิ่มตัวฟังเหตุการณ์ที่มีคุณค่าให้กับพาธนอกเหนือจาก .info/connected ตัวอย่างเช่น คุณสามารถทำ FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() ที่จุดเริ่มต้นของแต่ละเซสชัน สำหรับข้อมูลเพิ่มเติม โปรดดูที่ การตรวจจับสถานะการเชื่อม ต่อ