在 Cloud Firestore 中建構產品

視您建構的應用程式類型而定,偵測哪些使用者或裝置目前處於連線狀態 (又稱為偵測「在線狀態」),可能對您有所幫助。

舉例來說,如果您要建構社交網路等應用程式,或是部署 IoT 裝置群組,就可以使用這項資訊顯示線上且可供即時通訊的好友清單,或是依「上次上線時間」排序 IoT 裝置。

Cloud Firestore 本身不支援狀態,但您可以運用其他 Firebase 產品建構狀態系統。

解決方案:搭配即時資料庫使用 Cloud Functions

如要將 Cloud Firestore 連線至 Firebase 即時資料庫的原生在線狀態功能,請使用 Cloud Functions。

使用即時資料庫回報連線狀態,然後使用 Cloud Functions 將該資料鏡像到 Cloud Firestore

在即時資料庫中使用 Presence

首先,請考量傳統的在線狀態系統在即時資料庫中的運作方式。

網頁

// 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 中實作類似解決方案,請使用相同的即時資料庫程式碼,然後使用 Cloud Functions 讓即時資料庫和 Cloud Firestore 保持同步。

如果尚未將即時資料庫新增至專案,請先新增, 並加入上述在線狀態解決方案。

接著,您將透過下列方法,將在場狀態同步至 Cloud Firestore

  1. 在離線裝置的 Cloud Firestore 快取中,讓應用程式知道裝置處於離線狀態。
  2. 在全球範圍內使用 Cloud Function,讓存取Cloud Firestore的所有其他裝置知道這個特定裝置已離線。

本教學課程中建議使用的函式無法在用戶端應用程式中執行, 必須部署至 Cloud Functions for Firebase,且需要 Firebase Admin SDK 的伺服器端邏輯。 如需詳細指引,請參閱 Cloud Functions 說明文件

更新 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);
    });
});

完成這些變更後,我們現在可確保「local」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} 路徑。當即時資料庫值變更時,該值會同步至 Cloud Firestore,確保所有使用者的狀態正確無誤。

Node.js

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

部署這項函式後,您就能使用 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);
                // ...
            }
        });
    });

限制

使用即時資料庫為 Cloud Firestore 應用程式新增在線狀態功能,不僅有效率且可擴充,但仍有幾項限制:

  • 去抖動 - 監聽 Cloud Firestore 的即時變更時,這個解決方案可能會觸發多項變更。如果這些變更觸發的事件超出預期,請手動清除 Cloud Firestore 事件。
  • 連線 - 這項實作措施會評估與 Realtime Database 的連線,而非與 Cloud Firestore 的連線。如果每個資料庫的連線狀態不同,這項解決方案可能會回報錯誤的在場狀態。
  • Android - 在 Android 上,即時資料庫會在閒置 60 秒後與後端中斷連線。閒置狀態是指沒有開啟的接聽程式或待處理作業。如要保持連線開啟,建議您在 .info/connected 以外的路徑中新增值事件監聽器。舉例來說,您可以在每個工作階段開始時執行 FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced()。詳情請參閱「偵測連線狀態」。