С помощью Cloud Functions вы можете обрабатывать события в Cloud Firestore, не обновляя код клиента. Изменения в Cloud Firestore можно вносить через интерфейс снимка документа или Admin SDK.
В типичном жизненном цикле функция Cloud Firestore выполняет следующие действия:
- Ожидает изменений в определенном документе.
- Срабатывает при возникновении события и выполняет свои задачи.
- Получает объект данных, содержащий снимок данных, хранящихся в указанном документе. Для событий записи или обновления объект данных содержит два снимка, которые представляют состояние данных до и после инициирующего события.
Расстояние между местоположением экземпляра 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 поступает из Cloud Firestore Node.js SDK и включает такие методы, как 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 (первого поколения) требуется существующая база данных (default) в стандартном режиме Firestore. Именованные базы данных Cloud Firestore и режим хранилища данных не поддерживаются. В таких случаях для настройки событий используйте Cloud Functions (второе поколение).
- Настройка нескольких проектов с триггерами Cloud Functions и Cloud Firestore не поддерживается. Чтобы настроить триггер Cloud Firestore, Cloud Functions должен быть в том же проекте.
- Порядок не гарантируется. Быстрые изменения могут привести к вызову функций в неожиданном порядке.
- События доставляются по крайней мере один раз, но одно событие может привести к нескольким вызовам функций. Не полагайтесь на механику однократного выполнения и пишите идемпотентные функции.
- Для Cloud Firestore в режиме Datastore требуется Cloud Functions (второе поколение). Cloud Functions первого поколения не поддерживает режим Datastore.
- Триггер связан с одной базой данных. Нельзя создать триггер, который соответствует нескольким базам данных.
- При удалении базы данных триггеры для нее не удаляются автоматически. Триггер перестанет передавать события, но будет существовать, пока вы его не удалите.
- Если размер сопоставленного события превышает максимальный размер запроса, событие может не быть доставлено в Cloud Functions (первое поколение).
- События, которые не были доставлены из-за размера запроса, регистрируются в журналах платформы и учитываются при подсчете использования журналов для проекта.
- В Проводнике журналов вы можете найти записи с сообщением "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." (Не удалось доставить событие в функцию Cloud Functions, поскольку его размер превышает ограничение для первого поколения...) и уровнем серьезности
error. Название функции можно найти в полеfunctionName. Если полеreceiveTimestampпо-прежнему находится в пределах часа от текущего момента, вы можете определить фактическое содержимое события, прочитав соответствующий документ со снимком до и после временной метки. - Чтобы избежать этого, вы можете:
- Перейдите на Cloud Functions второго поколения
- Уменьшить размер документа
- Удалите Cloud Functions.
- Вы можете отключить ведение журнала с помощью исключений, но обратите внимание, что события, нарушающие правила, по-прежнему не будут доставляться.