محرک‌های Cloud Firestore

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

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

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

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

راه‌اندازهای تابع Cloud Firestore

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

Node.js

نوع رویداد ره‌انداز
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 Functions for Firebase (نسل دوم) را بخوانید تا پروژه Cloud Functions for Firebase خود را پیکربندی و راه‌اندازی کنید.

نوشتن توابع راه‌اندازی‌شده Cloud Firestore

تعریف کردن راه‌انداز تابع

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

Node.js

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:

مسیرهای سند می‌توانند به سند خاصی یا الگوی نویسه عام ارجاع دهند.

مشخص کردن یک سند

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

Node.js

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:

گروهی از اسناد را بااستفاده از کارت‌های جوکر مشخص کنید

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

Node.js

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 تغییر می‌کند، با کارت عامی به‌نام userId مطابقت می‌کند.

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

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

Node.js

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} همیشه به یک سند اشاره می‌کند.

راه‌اندازهای رویداد

وقتی سند جدیدی ایجاد می‌شود، تابعی را راه‌اندازی کنید

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

Node.js

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 ...

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

همچنین می‌توانید تابعی را راه‌اندازی کنید تا وقتی سند به‌روز می‌شود اجرا شود. این تابع نمونه درصورتی اجرا می‌شود که کاربر نمایه‌اش را تغییر دهد:

Node.js

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 ...

وقتی سندی حذف می‌شود، تابعی را راه‌اندازی کنید

همچنین می‌توانید وقتی سندی حذف می‌شود، تابعی را راه‌اندازی کنید. این تابع نمونه وقتی کاربر نمایه کاربری‌اش را حذف می‌کند اجرا می‌شود:

Node.js

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 گوش دهید. این تابع نمونه درصورتی اجرا می‌شود که کاربری ایجاد، به‌روزرسانی، یا حذف شود:

Node.js

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 حاوی وضعیت لحظه‌ای سند پس‌از به‌روزرسانی است.

Node.js

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 برای دسترسی به فیلدهای خاص استفاده کنید:

Node.js

// 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 شما مرتبط است. می‌توانید در چیدمانی که به تابع شما برگردانده شده است به آن سند دسترسی پیدا کنید.

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

Node.js

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})

دسترسی به اطلاعات اصالت‌سنجی کاربر

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

Node.js

  • 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

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

Node.js

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 عملیات خواندن و نوشتن انجام دهید:

Node.js

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 Functions درنظر داشته باشید:

  • پیش‌نیاز Cloud Functions (نسل اول) پایگاه داده «(پیش‌فرض)» موجود در حالت بومی Firestore است. از پایگاه‌های داده نام‌گذاری‌شده Cloud Firestore یا حالت Datastore پشتیبانی نمی‌کند. لطفاً از Cloud Functions (نسل دوم) برای پیکربندی رویدادها در چنین مواردی استفاده کنید.
  • راه‌اندازی بین پروژه‌ای با Cloud Functions و Cloud Firestore محرک محدودیت است. برای راه‌اندازی Cloud Firestore راه‌انداز، Cloud Functions باید در همان پروژه باشد.
  • ترتیب تضمین نمی‌شود. تغییرات سریع می‌تواند فراخوانی‌های تابع را در ترتیبی غیرمنتظره فعال کند.
  • رویدادها حداقل یک‌بار ارائه می‌شوند، اما یک رویداد ممکن است منجر به چندین فراخوانی تابع شود. از اتکا به سازوکارهای دقیقاً یک‌بار پرهیز کنید و توابع خودتوان بنویسید.
  • Cloud Firestore در حالت Datastore به Cloud Functions (نسل دوم) نیاز دارد. ‫Cloud Functions (نسل اول) از حالت Datastore پشتیبانی نمی‌کند.
  • راه‌انداز با یک پایگاه داده واحد مرتبط است. نمی‌توانید محرکی ایجاد کنید که با چندین پایگاه داده مطابقت داشته باشد.
  • حذف پایگاه داده به‌طور خودکار باعث حذف هیچ‌یک از محرک‌های آن پایگاه داده نمی‌شود. راه‌انداز ارسال رویدادها را متوقف می‌کند اما تا زمانی که راه‌انداز را حذف کنید همچنان وجود دارد.
  • اگر رویداد منطبق از حداکثر اندازه درخواست فراتر رود، ممکن است رویداد به Cloud Functions (نسل اول) تحویل داده نشود.
    • رویدادهایی که به‌دلیل اندازه درخواست تحویل داده نشده‌اند در گزارش‌های پلاتفرم ثبت می‌شوند و در شمارش استفاده از گزارش برای پروژه لحاظ می‌شوند.
    • می‌توانید این گزارش‌ها را در «کاوشگر گزارش‌ها» با پیام «رویداد نمی‌تواند به تابع Cloud تحویل داده شود زیرا اندازه از حد مجاز برای نسل اول فراتر رفته است…» با شدت error پیدا کنید. می‌توانید نام تابع را در فیلد functionName پیدا کنید. اگر فیلد receiveTimestamp هنوز در یک ساعت آینده است، می‌توانید با خواندن سند موردنظر با یک عکس آنی قبل و بعداز مُهر زمان، محتوای رویداد واقعی را استنباط کنید.
    • برای جلوگیری از چنین آهنگ کلامی، می‌توانید:
      • انتقال و ارتقا به Cloud Functions (نسل دوم)
      • کوچک کردن سند
      • حذف Cloud Functions مورد بحث
    • می‌توانید خود گزارش‌گیری را بااستفاده از استثناها خاموش کنید اما توجه داشته باشید که رویدادهای تخلف‌آمیز همچنان ارائه نخواهند شد.