گسترش Cloud Firestore با توابع Cloud (نسل دوم)

با Cloud Functions ، می‌توانید کدی را برای مدیریت رویدادهایی که توسط تغییرات در پایگاه داده Cloud Firestore ایجاد می‌شوند، مستقر کنید. این به شما امکان می دهد تا به راحتی عملکرد سمت سرور را بدون اجرای سرورهای خود به برنامه خود اضافه کنید.

Cloud Functions (نسل دوم)

با پشتیبانی از Cloud Run و Eventarc ، Cloud Functions for Firebase (نسل دوم) زیرساخت قدرتمندتر، کنترل پیشرفته‌تر بر عملکرد و مقیاس‌پذیری، و کنترل بیشتر بر زمان اجرا عملکردها را به شما می‌دهد. برای اطلاعات بیشتر درباره نسل دوم، به Cloud Functions for Firebase (نسل دوم) مراجعه کنید. برای مشاهده اطلاعات بیشتر درباره نسل اول، به Extend Cloud Firestore with Cloud Functions مراجعه کنید.

عملکرد Cloud Firestore فعال می شود

Cloud Functions for Firebase SDK، محرک‌های رویداد 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 Get start with Cloud Functions for Firebase (نسل دوم) را بخوانید.

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

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

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

Node.js

import {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentCreated,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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

import { onDocumentUpdated } from "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 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

import { onDocumentWrittenWithAuthContext } from "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 Firestore در حالت Datastore به Cloud Functions (نسل دوم) نیاز دارد. Cloud Functions (نسل اول) از حالت Datastore پشتیبانی نمی کند.
  • یک ماشه با یک پایگاه داده واحد مرتبط است. شما نمی توانید تریگری ایجاد کنید که با چندین پایگاه داده مطابقت داشته باشد.
  • حذف یک پایگاه داده به طور خودکار هیچ عاملی را برای آن پایگاه داده حذف نمی کند. ماشه تحویل رویدادها را متوقف می کند اما تا زمانی که ماشه را حذف نکنید به وجود خود ادامه می دهد.
  • اگر یک رویداد منطبق از حداکثر اندازه درخواست بیشتر شود، ممکن است رویداد به Cloud Functions (نسل اول) تحویل داده نشود.
    • رویدادهایی که به دلیل اندازه درخواست تحویل نمی‌شوند، در گزارش‌های پلتفرم ثبت می‌شوند و به‌حساب استفاده از گزارش برای پروژه حساب می‌شوند.
    • می‌توانید این گزارش‌ها را در کاوشگر گزارش‌ها با پیام شدت error پیدا کنید "رویداد نمی‌تواند به عملکرد Cloud تحویل داده شود به دلیل اندازه بیش از حد مجاز برای نسل اول...". می توانید نام تابع را در قسمت functionName پیدا کنید. اگر قسمت receiveTimestamp هنوز تا یک ساعت دیگر باقی مانده است، می توانید با خواندن سند مورد نظر با یک عکس فوری قبل و بعد از مهر زمانی، محتوای واقعی رویداد را استنباط کنید.
    • برای جلوگیری از چنین آهنگی، می توانید:
      • مهاجرت و ارتقاء به Cloud Functions (نسل دوم)
      • سند را کوچک کنید
      • Cloud Functions مورد نظر را حذف کنید
    • شما می توانید خود گزارش را با استفاده از استثناها خاموش کنید، اما توجه داشته باشید که رویدادهای توهین آمیز هنوز تحویل داده نمی شوند.