Увеличьте присутствие в Cloud Firestore

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

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

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

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

Чтобы подключить Cloud Firestore к встроенной функции присутствия Firebase 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 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, поскольку запись нашего «автономного» статуса выполняется только локально и не будет синхронизироваться при восстановлении соединения. Чтобы противостоять этому, мы будем использовать функцию Cloud , которая следит за status/{uid} путь в реальном времени базы данных. Когда значение 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);
                // ...
            }
        });
    });

Ограничения

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

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