Join us in person and online for Firebase Summit on October 18, 2022. Learn how Firebase can help you accelerate app development, release your app with confidence, and scale with ease. Register now

Создайте присутствие в Cloud Firestore

Оптимизируйте свои подборки Сохраняйте и классифицируйте контент в соответствии со своими настройками.

В зависимости от типа приложения, которое вы создаете, вам может быть полезно определить, какие из ваших пользователей или устройств активно подключены к сети, иначе это называется определением «присутствия».

Например, если вы создаете приложение, такое как социальная сеть, или развертываете парк устройств IoT, вы можете использовать эту информацию для отображения списка друзей, которые находятся в сети и могут свободно общаться в чате, или отсортировать устройства IoT по «последнему просмотру». ."

Cloud Firestore изначально не поддерживает присутствие, но вы можете использовать другие продукты Firebase для создания системы присутствия.

Решение: облачные функции с базой данных в реальном времени

Чтобы подключить Cloud Firestore к встроенной функции присутствия базы данных Firebase Realtime, используйте 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);
    });
});

Этот пример представляет собой полную систему присутствия базы данных в реальном времени. Он обрабатывает множественные отключения, сбои и так далее.

Подключение к облачному магазину Firestore

Чтобы реализовать аналогичное решение в Cloud Firestore, используйте тот же код базы данных Realtime Database, а затем используйте Cloud Functions для синхронизации базы данных Realtime Database и Cloud Firestore.

Если вы еще этого не сделали, добавьте базу данных реального времени в свой проект и включите указанное выше решение присутствия.

Далее вы синхронизируете состояние присутствия с 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} в базе данных реального времени. Когда значение базы данных в реальном времени изменится, это значение будет синхронизировано с 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);
                // ...
            }
        });
    });

Ограничения

Использование базы данных в реальном времени для добавления присутствия в ваше приложение Cloud Firestore является масштабируемым и эффективным, но имеет некоторые ограничения:

  • Устранение дребезга — при прослушивании изменений в реальном времени в Cloud Firestore это решение, вероятно, вызовет несколько изменений. Если эти изменения вызывают больше событий, чем вы хотите, вручную отмените дребезг событий Cloud Firestore.
  • Возможность подключения — эта реализация измеряет подключение к базе данных реального времени, а не к Cloud Firestore. Если состояние подключения к каждой базе данных неодинаково, это решение может сообщать о неправильном состоянии присутствия.
  • Android — на Android база данных в реальном времени отключается от серверной части через 60 секунд бездействия. Бездействие означает отсутствие открытых слушателей или ожидающих операций. Чтобы соединение оставалось открытым, мы рекомендуем вам добавить прослушиватель событий значения к пути помимо .info/connected . Например, вы можете сделать FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() в начале каждого сеанса. Дополнительные сведения см. в разделе Определение состояния подключения .
,

В зависимости от типа приложения, которое вы создаете, вам может быть полезно определить, какие из ваших пользователей или устройств активно подключены к сети, иначе это называется определением «присутствия».

Например, если вы создаете приложение, такое как социальная сеть, или развертываете парк устройств IoT, вы можете использовать эту информацию для отображения списка друзей, которые находятся в сети и могут свободно общаться в чате, или отсортировать устройства IoT по «последнему просмотру». ."

Cloud Firestore изначально не поддерживает присутствие, но вы можете использовать другие продукты Firebase для создания системы присутствия.

Решение: облачные функции с базой данных в реальном времени

Чтобы подключить Cloud Firestore к встроенной функции присутствия базы данных Firebase Realtime, используйте 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);
    });
});

Этот пример представляет собой полную систему присутствия базы данных в реальном времени. Он обрабатывает множественные отключения, сбои и так далее.

Подключение к облачному магазину Firestore

Чтобы реализовать аналогичное решение в Cloud Firestore, используйте тот же код базы данных Realtime Database, а затем используйте Cloud Functions для синхронизации базы данных Realtime Database и Cloud Firestore.

Если вы еще этого не сделали, добавьте базу данных реального времени в свой проект и включите указанное выше решение присутствия.

Далее вы синхронизируете состояние присутствия с 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} в базе данных реального времени. Когда значение базы данных в реальном времени изменится, это значение будет синхронизировано с 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);
                // ...
            }
        });
    });

Ограничения

Использование базы данных в реальном времени для добавления присутствия в ваше приложение Cloud Firestore является масштабируемым и эффективным, но имеет некоторые ограничения:

  • Устранение дребезга — при прослушивании изменений в реальном времени в Cloud Firestore это решение, вероятно, вызовет несколько изменений. Если эти изменения вызывают больше событий, чем вы хотите, вручную отмените дребезг событий Cloud Firestore.
  • Возможность подключения — эта реализация измеряет подключение к базе данных реального времени, а не к Cloud Firestore. Если состояние подключения к каждой базе данных неодинаково, это решение может сообщать о неправильном состоянии присутствия.
  • Android — на Android база данных в реальном времени отключается от серверной части через 60 секунд бездействия. Бездействие означает отсутствие открытых слушателей или ожидающих операций. Чтобы соединение оставалось открытым, мы рекомендуем вам добавить прослушиватель событий значения к пути помимо .info/connected . Например, вы можете сделать FirebaseDatabase.getInstance().getReference((new Date()).toString()).keepSynced() в начале каждого сеанса. Дополнительные сведения см. в разделе Определение состояния подключения .