محرک های Cloud Firestore

با استفاده Cloud Functions ، می‌توانید رویدادها را در Cloud Firestore بدون نیاز به به‌روزرسانی کد کلاینت مدیریت کنید. می‌توانید تغییرات Cloud Firestore را از طریق رابط عکس فوری سند یا از طریق Admin SDK انجام دهید.

در یک چرخه عمر معمولی، یک تابع Cloud Firestore موارد زیر را انجام می‌دهد:

  1. منتظر تغییرات در یک سند خاص است.
  2. هنگام وقوع یک رویداد فعال می‌شود و وظایف خود را انجام می‌دهد.
  3. یک شیء داده دریافت می‌کند که شامل یک تصویر لحظه‌ای از داده‌های ذخیره شده در سند مشخص شده است. برای رویدادهای نوشتن یا به‌روزرسانی، شیء داده شامل دو تصویر لحظه‌ای است که وضعیت داده را قبل و بعد از رویداد آغازین نشان می‌دهند.

فاصله بین محل قرارگیری نمونه Firestore و محل قرارگیری تابع می‌تواند تأخیر قابل توجهی در شبکه ایجاد کند. برای بهینه‌سازی عملکرد، در صورت لزوم، مشخص کردن محل تابع را در نظر بگیرید.

محرک‌های تابع Cloud Firestore

Cloud Functions for Firebase SDK، محرک‌های رویداد Cloud Firestore زیر را صادر می‌کنند تا به شما امکان دهند کنترل‌کننده‌هایی مرتبط با رویدادهای خاص Cloud Firestore ایجاد کنید:

نود جی اس

نوع رویداد ماشه
onDocumentCreated زمانی که برای اولین بار در یک سند نوشته می‌شود، فعال می‌شود.
onDocumentUpdated زمانی فعال می‌شود که یک سند از قبل وجود داشته باشد و مقداری از آن تغییر کرده باشد.
onDocumentDeleted زمانی که یک سند حذف می‌شود، فعال می‌شود.
onDocumentWritten زمانی فعال می‌شود که onDocumentCreated ، onDocumentUpdated یا onDocumentDeleted فعال شوند.
onDocumentCreatedWithAuthContext onDocumentCreated با اطلاعات احراز هویت اضافی
onDocumentWrittenWithAuthContext onDocumentWritten
onDocumentDeletedWithAuthContext onDocumentDeleted با اطلاعات احراز هویت اضافی
onDocumentUpdatedWithAuthContext onDocumentUpdated با اطلاعات احراز هویت اضافی

پایتون

نوع رویداد ماشه
on_document_created زمانی که برای اولین بار در یک سند نوشته می‌شود، فعال می‌شود.
on_document_updated زمانی فعال می‌شود که یک سند از قبل وجود داشته باشد و مقداری از آن تغییر کرده باشد.
on_document_deleted زمانی که یک سند حذف می‌شود، فعال می‌شود.
on_document_written زمانی فعال می‌شود که on_document_created ، on_document_updated یا on_document_deleted فعال شوند.
on_document_created_with_auth_context on_document_created با اطلاعات احراز هویت اضافی
on_document_updated_with_auth_context on_document_updated با اطلاعات احراز هویت اضافی
on_document_deleted_with_auth_context on_document_deleted با اطلاعات احراز هویت اضافی
on_document_written_with_auth_context on_document_written با اطلاعات احراز هویت اضافی

رویدادهای Cloud Firestore فقط در صورت تغییر سند فعال می‌شوند. به‌روزرسانی یک سند Cloud Firestore که داده‌ها در آن بدون تغییر هستند (نوشتن بدون عملیات) رویداد به‌روزرسانی یا نوشتن ایجاد نمی‌کند. افزودن رویدادها به فیلدهای خاص امکان‌پذیر نیست.

اگر هنوز پروژه‌ای برای Cloud Functions for Firebase فعال نکرده‌اید، برای پیکربندی و راه‌اندازی پروژه Cloud Cloud Functions for Firebase Cloud Functions for Firebase (نسل دوم)» را مطالعه کنید.

نوشتن توابع فعال شده توسط Cloud Firestore

تعریف یک تریگر تابع

برای تعریف یک تریگر Cloud Firestore، یک مسیر سند و یک نوع رویداد را مشخص کنید:

نود جی اس

const {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
   /* ... */ 
});

پایتون

from firebase_functions.firestore_fn import (
  on_document_created,
  on_document_deleted,
  on_document_updated,
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:

مسیرهای سند می‌توانند به یک سند خاص یا یک الگوی wildcard اشاره کنند.

یک سند واحد را مشخص کنید

اگر می‌خواهید برای هرگونه تغییر در یک سند خاص، رویدادی را فعال کنید، می‌توانید از تابع زیر استفاده کنید.

نود جی اس

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/marie", (event) => {
  // Your code here
});

پایتون

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

مشخص کردن گروهی از اسناد با استفاده از کاراکترهای عمومی (wildcards)

اگر می‌خواهید یک تریگر را به گروهی از اسناد، مانند هر سندی در یک مجموعه خاص، پیوست کنید، به جای شناسه سند از {wildcard} استفاده کنید:

نود جی اس

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
  // If we set `/users/marie` to {name: "Marie"} then
  // event.params.userId == "marie"
  // ... and ...
  // event.data.after.data() == {name: "Marie"}
});

پایتون

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie` to {name: "Marie"} then
  event.params["userId"] == "marie"  # True
  # ... and ...
  event.data.after.to_dict() == {"name": "Marie"}  # True

در این مثال، وقتی هر فیلدی در هر سندی در users تغییر می‌کند، با یک wildcard به نام userId مطابقت پیدا می‌کند.

اگر سندی در users دارای زیرمجموعه‌هایی باشد و فیلدی در یکی از اسناد آن زیرمجموعه‌ها تغییر کند، کاراکتر wildcard userId فعال نمی‌شود .

تطبیق‌های Wildcard از مسیر سند استخراج شده و در event.params ذخیره می‌شوند. می‌توانید هر تعداد Wildcard که دوست دارید برای جایگزینی شناسه‌های صریح مجموعه یا سند تعریف کنید، برای مثال:

نود جی اس

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
    // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
    // event.params.userId == "marie";
    // event.params.messageCollectionId == "incoming_messages";
    // event.params.messageId == "134";
    // ... and ...
    // event.data.after.data() == {body: "Hello"}
});

پایتون

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}/{messageCollectionId}/{messageId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
  event.params["userId"] == "marie"  # True
  event.params["messageCollectionId"] == "incoming_messages"  # True
  event.params["messageId"] == "134"  # True
  # ... and ...
  event.data.after.to_dict() == {"body": "Hello"}

تریگر شما باید همیشه به یک سند اشاره کند، حتی اگر از یک کاراکتر جایگزین استفاده می‌کنید. برای مثال، users/{userId}/{messageCollectionId} معتبر نیست زیرا {messageCollectionId} یک مجموعه است. با این حال، users/{userId}/{messageCollectionId}/{messageId} معتبر است زیرا {messageId} همیشه به یک سند اشاره می‌کند.

محرک‌های رویداد

هنگام ایجاد یک سند جدید، یک تابع را فعال می‌کند

شما می‌توانید هر زمان که یک سند جدید در یک مجموعه ایجاد می‌شود، یک تابع را فعال کنید. این تابع مثال هر بار که یک پروفایل کاربری جدید اضافه می‌شود، فعال می‌شود:

نود جی اس

const {
  onDocumentCreated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.createuser = onDocumentCreated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snapshot = event.data;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

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

    // perform more operations ...
});

برای اطلاعات بیشتر در مورد احراز هویت، از onDocumentCreatedWithAuthContext استفاده کنید.

پایتون

from firebase_functions.firestore_fn import (
  on_document_created,
  Event,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

هنگامی که یک سند به‌روزرسانی می‌شود، یک تابع را فعال می‌کند

همچنین می‌توانید هنگام به‌روزرسانی یک سند، یک تابع را فعال کنید. این تابع مثال در صورتی که کاربر پروفایل خود را تغییر دهد، فعال می‌شود:

نود جی اس

const {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const newValue = event.data.after.data();

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

    // perform more operations ...
});

برای اطلاعات احراز هویت بیشتر، از onDocumentUpdatedWithAuthContext استفاده کنید.

پایتون

from firebase_functions.firestore_fn import (
  on_document_updated,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.after.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

هنگام حذف یک سند، یک تابع را فعال می‌کند

همچنین می‌توانید هنگام حذف یک سند، یک تابع را فعال کنید. این تابع نمونه هنگامی که کاربر نمایه کاربری خود را حذف می‌کند، فعال می‌شود:

نود جی اس

const {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snap =  event.data;
    const data =  snap.data();

    // perform more operations ...
});

برای اطلاعات بیشتر در مورد احراز هویت، از onDocumentDeletedWithAuthContext استفاده کنید.

پایتون

from firebase_functions.firestore_fn import (
  on_document_deleted,
  Event,
  DocumentSnapshot,
)

@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
  # Perform more operations ...

فعال کردن یک تابع برای همه تغییرات در یک سند

اگر نوع رویدادی که اجرا می‌شود برایتان مهم نیست، می‌توانید با استفاده از تریگر رویداد «سند نوشته شده» به تمام تغییرات در یک سند Cloud Firestore گوش دهید. این تابع مثال در صورت ایجاد، به‌روزرسانی یا حذف کاربر اجرا می‌شود:

نود جی اس

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const document =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();

    // perform more operations ...
});

برای اطلاعات بیشتر در مورد احراز هویت، از onDocumentWrittenWithAuthContext استفاده کنید.

پایتون

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  # Get an object with the current document values.
  # If the document does not exist, it was deleted.
  document = (event.data.after.to_dict()
              if event.data.after is not None else None)

  # Get an object with the previous document values.
  # If the document does not exist, it was newly created.
  previous_values = (event.data.before.to_dict()
                     if event.data.before is not None else None)

  # Perform more operations ...

خواندن و نوشتن داده‌ها

وقتی یک تابع فعال می‌شود، یک تصویر لحظه‌ای از داده‌های مربوط به رویداد ارائه می‌دهد. می‌توانید از این تصویر لحظه‌ای برای خواندن یا نوشتن در سندی که رویداد را فعال کرده است استفاده کنید، یا از Firebase Admin SDK برای دسترسی به سایر بخش‌های پایگاه داده خود استفاده کنید.

داده‌های رویداد

خواندن داده‌ها

وقتی یک تابع فعال می‌شود، ممکن است بخواهید داده‌هایی را از سندی که به‌روزرسانی شده است دریافت کنید، یا داده‌ها را قبل از به‌روزرسانی دریافت کنید. می‌توانید داده‌های قبلی را با استفاده از event.data.before دریافت کنید، که شامل عکس فوری سند قبل از به‌روزرسانی است. به طور مشابه، event.data.after شامل وضعیت عکس فوری سند پس از به‌روزرسانی است.

نود جی اس

exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const newValues =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();
});

پایتون

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get an object with the current document values.
  new_value = event.data.after.to_dict()

  # Get an object with the previous document values.
  prev_value = event.data.before.to_dict()

شما می‌توانید مانند هر شیء دیگری به ویژگی‌ها دسترسی داشته باشید. به عنوان یک جایگزین، می‌توانید از تابع get برای دسترسی به فیلدهای خاص استفاده کنید:

نود جی اس

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

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

پایتون

# Get the value of a single document field.
age = event.data.after.get("age")

# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]

نوشتن داده‌ها

هر فراخوانی تابع با یک سند خاص در پایگاه داده Cloud Firestore شما مرتبط است. می‌توانید به آن سند در snapshot برگردانده شده به تابع خود دسترسی داشته باشید.

مرجع سند شامل متدهایی مانند update() ، set() و remove() است، بنابراین می‌توانید سندی را که تابع را فعال کرده است، تغییر دهید.

نود جی اس

const {onDocumentUpdated} = require('firebase-functions/v2/firestore');

exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
  // Retrieve the current and previous value
  const data = event.data.after.data();
  const previousData = event.data.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 event.data.after.ref.set({
    name_change_count: count + 1
  }, {merge: true});

});

پایتون

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # We'll only update if the name has changed.
  # This is crucial to prevent infinite loops.
  if new_value.get("name") == prev_value.get("name"):
      return

  # Retrieve the current count of name changes
  count = new_value.to_dict().get("name_change_count", 0)

  # Update the count
  new_value.reference.update({"name_change_count": count + 1})

دسترسی به اطلاعات احراز هویت کاربر

اگر از یکی از انواع رویدادهای زیر استفاده کنید، می‌توانید به اطلاعات احراز هویت کاربر در مورد اصلی که رویداد را آغاز کرده است، دسترسی پیدا کنید. این اطلاعات علاوه بر اطلاعات برگردانده شده در رویداد پایه است.

نود جی اس

  • onDocumentCreatedWithAuthContext
  • onDocumentWrittenWithAuthContext
  • onDocumentDeletedWithAuthContext
  • onDocumentUpdatedWithAuthContext

پایتون

  • on_document_created_with_auth_context
  • on_document_updated_with_auth_context
  • on_document_deleted_with_auth_context
  • on_document_written_with_auth_context

برای اطلاعات مربوط به داده‌های موجود در زمینه احراز هویت، به Auth Context مراجعه کنید. مثال زیر نحوه بازیابی اطلاعات احراز هویت را نشان می‌دهد:

نود جی اس

const {onDocumentWrittenWithAuthContext} = require('firebase-functions/v2/firestore');

exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
    const snapshot = event.data.after;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // retrieve auth context from event
    const { authType, authId } = event;

    let verified = false;
    if (authType === "system") {
      // system-generated users are automatically verified
      verified = true;
    } else if (authType === "unknown" || authType === "unauthenticated") {
      // admin users from a specific domain are verified
      if (authId.endsWith("@example.com")) {
        verified = true;
      }
    }

    return data.after.ref.set({
        created_by: authId,
        verified,
    }, {merge: true}); 
}); 

پایتون

@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # Get the auth context from the event
  user_auth_type = event.auth_type
  user_auth_id = event.auth_id

داده‌های خارج از رویداد ماشه

Cloud Functions در یک محیط قابل اعتماد اجرا می‌شوند. آنها به عنوان یک حساب کاربری سرویس در پروژه شما مجاز هستند و می‌توانید با استفاده از Firebase Admin SDK عملیات خواندن و نوشتن را انجام دهید:

نود جی اس

const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');

initializeApp();
const db = getFirestore();

exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
    db.doc('some/otherdoc').set({ ... });
  });

  exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
    db.doc('some/otherdoc').set({
      // Update otherdoc
    });
  });

پایتون

from firebase_admin import firestore, initialize_app
import google.cloud.firestore

initialize_app()

@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  firestore_client: google.cloud.firestore.Client = firestore.client()
  firestore_client.document("another/doc").set({
      # ...
  })

محدودیت‌ها

به محدودیت‌های زیر برای محرک‌های Cloud Firestore برای توابع Cloud توجه کنید:

  • Cloud Functions (نسل اول) در حالت بومی Firestore، یک پایگاه داده "(پیش‌فرض)" موجود را پیش‌نیاز دارد. این قابلیت از پایگاه‌های داده با نام Cloud Firestore یا حالت Datastore پشتیبانی نمی‌کند. لطفاً در چنین مواردی Cloud Functions (نسل دوم) برای پیکربندی رویدادها استفاده کنید.
  • راه‌اندازی بین پروژه‌ای با Cloud Functions و Cloud Firestore trigger یک محدودیت است. برای راه‌اندازی Cloud Firestore trigger، Cloud Functions باید در یک پروژه باشند.
  • ترتیب اجرا تضمین شده نیست. تغییرات سریع می‌توانند باعث فراخوانی توابع با ترتیب غیرمنتظره شوند.
  • رویدادها حداقل یک بار اجرا می‌شوند، اما یک رویداد واحد ممکن است منجر به فراخوانی چندین تابع شود. از تکیه بر مکانیزم‌های دقیقاً یک‌باره خودداری کنید و توابع خودتوان بنویسید.
  • Cloud Firestore در حالت Datastore به Cloud Functions (نسل دوم) نیاز دارد. Cloud Functions (نسل اول) از حالت Datastore پشتیبانی نمی‌کند.
  • یک تریگر (Trigger) به یک پایگاه داده واحد مرتبط است. شما نمی‌توانید تریگری ایجاد کنید که با چندین پایگاه داده مطابقت داشته باشد.
  • حذف یک پایگاه داده به طور خودکار هیچ تریگر (triggers) را برای آن پایگاه داده حذف نمی‌کند. تریگر ارائه رویدادها را متوقف می‌کند اما تا زمانی که تریگر را حذف نکنید، همچنان وجود خواهد داشت.
  • اگر یک رویداد منطبق از حداکثر اندازه درخواست فراتر رود، ممکن است رویداد به Cloud Functions (نسل اول) تحویل داده نشود.
    • رویدادهایی که به دلیل حجم درخواست تحویل داده نمی‌شوند، در لاگ‌های پلتفرم ثبت می‌شوند و در میزان استفاده از لاگ برای پروژه محاسبه می‌شوند.
    • می‌توانید این گزارش‌ها را در Logs Explorer با پیام "Event cannot deliver to Cloud function due to size exceeding the limit for 1st gen..." از نظر شدت error پیدا کنید. می‌توانید نام تابع را در فیلد functionName پیدا کنید. اگر فیلد receiveTimestamp هنوز تا یک ساعت دیگر باقی مانده باشد، می‌توانید با خواندن سند مورد نظر به همراه یک snapshot قبل و بعد از برچسب زمانی، محتوای واقعی رویداد را استنباط کنید.
    • برای جلوگیری از چنین ریتمی، می‌توانید:
      • مهاجرت و ارتقاء به Cloud Functions (نسل دوم)
      • کوچک کردن حجم سند
      • Cloud Functions مورد نظر را حذف کنید
    • شما می‌توانید با استفاده از استثنائات، خودِ ثبت وقایع را غیرفعال کنید، اما توجه داشته باشید که رویدادهای مشکل‌ساز همچنان اجرا نخواهند شد.