Триггеры Cloud Firestore (1-го поколения)

С помощью Cloud Functions вы можете обрабатывать события в Cloud Firestore без необходимости обновления клиентского кода. Вы можете вносить изменения в Cloud Firestore через интерфейс создания снимков документов или через Admin SDK .

В типичном жизненном цикле функция Cloud Firestore выполняет следующие действия:

  1. Ожидает внесения изменений в конкретный документ.
  2. Запускается при возникновении события и выполняет соответствующие задачи.
  3. Получает объект данных, содержащий снимок данных, хранящихся в указанном документе. Для событий записи или обновления объект данных содержит два снимка, представляющих состояние данных до и после события, вызвавшего событие.

Расстояние между местоположением экземпляра Firestore и местоположением функции может создавать значительную задержку в сети. Для оптимизации производительности рекомендуется указывать местоположение функции там, где это применимо.

Функции Cloud Firestore запускают триггеры

SDK Cloud Functions for Firebase экспортирует объект functions.firestore , который позволяет создавать обработчики, привязанные к конкретным событиям Cloud Firestore.

Тип события Курок
onCreate Срабатывает при первой записи в документ.
onUpdate Срабатывает, когда документ уже существует и в нем изменено какое-либо значение.
onDelete Срабатывает при удалении документа, содержащего данные.
onWrite Срабатывает при вызове onCreate , onUpdate или onDelete .

Если у вас еще нет проекта, поддерживающего Cloud Functions for Firebase , прочтите руководство «Начало работы: написание и развертывание ваших первых функций» , чтобы настроить и подготовить свой проект Cloud Functions for Firebase .

Разработка функций, запускаемых Cloud Firestore.

Определите триггер функции

Для определения триггера Cloud Firestore укажите путь к документу и тип события:

Node.js

const functions = require('firebase-functions');

exports.myFunction = functions.firestore
  .document('my-collection/{docId}')
  .onWrite((change, context) => { /* ... */ });

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

Укажите один документ

Если вы хотите инициировать событие при любом изменении в конкретном документе, вы можете использовать следующую функцию.

Node.js

// Listen for any change on document `marie` in collection `users`
exports.myFunctionName = functions.firestore
    .document('users/marie').onWrite((change, context) => {
      // ... Your code here
    });

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

Если вы хотите привязать триггер к группе документов, например, к любому документу в определенной коллекции, используйте символ {wildcard} вместо идентификатора документа:

Node.js

// Listen for changes in all documents in the 'users' collection
exports.useWildcard = functions.firestore
    .document('users/{userId}')
    .onWrite((change, context) => {
      // If we set `/users/marie` to {name: "Marie"} then
      // context.params.userId == "marie"
      // ... and ...
      // change.after.data() == {name: "Marie"}
    });

В этом примере при изменении любого поля в любом документе в базе данных users используется подстановочный знак userId .

Если документ в коллекции users содержит подколлекции, и поле в документе одной из этих подколлекций изменяется, то символ подстановки userId не срабатывает.

Сопоставление с использованием подстановочных символов извлекается из пути к документу и сохраняется в context.params . Вы можете определить столько подстановочных символов, сколько вам нужно, для замены явных идентификаторов коллекций или документов, например:

Node.js

// Listen for changes in all documents in the 'users' collection and all subcollections
exports.useMultipleWildcards = functions.firestore
    .document('users/{userId}/{messageCollectionId}/{messageId}')
    .onWrite((change, context) => {
      // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
      // context.params.userId == "marie";
      // context.params.messageCollectionId == "incoming_messages";
      // context.params.messageId == "134";
      // ... and ...
      // change.after.data() == {body: "Hello"}
    });

Триггеры событий

Запустить функцию при создании нового документа

Вы можете запустить функцию каждый раз, когда в коллекции создается новый документ, используя обработчик onCreate() с подстановочным знаком . В этом примере функция вызывает createUser каждый раз, когда добавляется новый профиль пользователя:

Node.js

exports.createUser = functions.firestore
    .document('users/{userId}')
    .onCreate((snap, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = snap.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Запустить функцию при обновлении документа

Вы также можете запустить функцию при обновлении документа, используя функцию onUpdate() с подстановочным знаком . В этом примере функция вызывает updateUser если пользователь изменяет свой профиль:

Node.js

exports.updateUser = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the document
      // e.g. {'name': 'Marie', 'age': 66}
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();

      // access a particular field as you would any JS property
      const name = newValue.name;

      // perform desired operations ...
    });

Запустить функцию при удалении документа

Вы также можете вызвать функцию при удалении документа, используя функцию onDelete() с подстановочным знаком . В этом примере функция вызывает deleteUser , когда пользователь удаляет свой профиль:

Node.js

exports.deleteUser = functions.firestore
    .document('users/{userID}')
    .onDelete((snap, context) => {
      // Get an object representing the document prior to deletion
      // e.g. {'name': 'Marie', 'age': 66}
      const deletedValue = snap.data();

      // perform desired operations ...
    });

Запустить функцию для всех изменений в документе.

Если вас не интересует тип генерируемого события, вы можете отслеживать все изменения в документе Cloud Firestore, используя функцию onWrite() с подстановочным знаком . В этом примере функция вызывает modifyUser при создании, обновлении или удалении пользователя:

Node.js

exports.modifyUser = functions.firestore
    .document('users/{userID}')
    .onWrite((change, context) => {
      // Get an object with the current document value.
      // If the document does not exist, it has been deleted.
      const document = change.after.exists ? change.after.data() : null;

      // Get an object with the previous document value (for update or delete)
      const oldDocument = change.before.data();

      // perform desired operations ...
    });

Чтение и запись данных

При срабатывании функции создается снимок данных, связанных с событием. Вы можете использовать этот снимок для чтения или записи в документ, вызвавший событие, или использовать Firebase Admin SDK для доступа к другим частям вашей базы данных.

Данные события

Чтение данных

При срабатывании функции может потребоваться получить данные из обновленного документа или данные, существовавшие до обновления. Предварительные данные можно получить с помощью метода change.before.data() , который содержит снимок состояния документа до обновления. Аналогично, change.after.data() содержит снимок состояния документа после обновления.

Node.js

exports.updateUser2 = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Get an object representing the current document
      const newValue = change.after.data();

      // ...or the previous value before this update
      const previousValue = change.before.data();
    });

Вы можете получать доступ к свойствам так же, как и к любому другому объекту. В качестве альтернативы, вы можете использовать функцию get для доступа к конкретным полям:

Node.js

// Fetch data using standard accessors
const age = snap.data().age;
const name = snap.data()['name'];

// Fetch data using built in accessor
const experience = snap.get('experience');

Запись данных

Каждый вызов функции связан с конкретным документом в вашей базе данных Cloud Firestore. Вы можете получить доступ к этому документу как к объекту DocumentReference в свойстве ref снимка, возвращаемого вашей функции.

Этот DocumentReference входит в состав Node.js SDK Cloud Firestore и включает в себя такие методы, как update() , set() и remove() , позволяющие легко изменять документ, вызвавший данную функцию.

Node.js

// Listen for updates to any `user` document.
exports.countNameChanges = functions.firestore
    .document('users/{userId}')
    .onUpdate((change, context) => {
      // Retrieve the current and previous value
      const data = change.after.data();
      const previousData = change.before.data();

      // We'll only update if the name has changed.
      // This is crucial to prevent infinite loops.
      if (data.name == previousData.name) {
        return null;
      }

      // Retrieve the current count of name changes
      let count = data.name_change_count;
      if (!count) {
        count = 0;
      }

      // Then return a promise of a set operation to update the count
      return change.after.ref.set({
        name_change_count: count + 1
      }, {merge: true});
    });

Данные вне событий запуска

Cloud Functions выполняются в доверенной среде, что означает, что они авторизованы как сервисные учетные записи в вашем проекте. Вы можете выполнять операции чтения и записи с помощью Firebase Admin SDK :

Node.js

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

const db = admin.firestore();

exports.writeToFirestore = functions.firestore
  .document('some/doc')
  .onWrite((change, context) => {
    db.doc('some/otherdoc').set({ ... });
  });

Ограничения

Обратите внимание на следующие ограничения для триггеров Cloud Firestore в Cloud Functions:

  • Для работы Cloud Functions (1-го поколения) требуется наличие существующей базы данных "(по умолчанию)" в собственном режиме Firestore. Она не поддерживает именованные базы данных Cloud Firestore или режим Datastore. В таких случаях для настройки событий используйте Cloud Functions (2-го поколения).
  • Ограничением является возможность настройки Cloud Functions и триггера Cloud Firestore в рамках одного проекта. Для настройки триггера Cloud Firestore необходимо, чтобы Cloud Functions находились в том же проекте.
  • Порядок выполнения не гарантируется. Быстрые изменения могут привести к вызову функций в неожиданном порядке.
  • События доставляются как минимум один раз, но одно событие может привести к нескольким вызовам функций. Избегайте зависимости от механизма "точно один раз" и пишите идемпотентные функции .
  • Для Cloud Firestore в режиме хранилища данных требуется Cloud Functions (2-го поколения). Cloud Functions (1-го поколения) не поддерживает режим хранилища данных.
  • Триггер связан с одной базой данных. Нельзя создать триггер, который бы соответствовал нескольким базам данных.
  • Удаление базы данных не приводит к автоматическому удалению всех триггеров для этой базы данных. Триггер перестает генерировать события, но продолжает существовать до тех пор, пока вы его не удалите .
  • Если размер соответствующего события превышает максимальный размер запроса , событие может быть не доставлено в Cloud Functions (1-го поколения).
    • События, не доставленные из-за недостаточного размера запроса, регистрируются в журналах платформы и учитываются в общем объеме использования журналов для проекта.
    • You can find these logs in the Logs Explorer with the message "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." of error severity. You can find the function name under the functionName field. If the receiveTimestamp field is still within an hour from now, you can infer the actual event content by reading the document in question with a snapshot before and after the timestamp.
    • Чтобы избежать такой монотонности, вы можете:
      • Перейдите на Cloud Functions (2-го поколения) и выполните миграцию.
      • Уменьшить размер документа
      • Удалите соответствующие Cloud Functions .
    • Вы можете отключить само логирование с помощью исключений , но имейте в виду, что проблемные события всё равно не будут доставляться.