راه‌اندازهای پایگاه داده بی‌درنگ

با Cloud Functions، می‌توانید رویدادها را در Firebase Realtime Database بدون نیاز به به‌روزرسانی کد کارخواه مدیریت کنید. Cloud Functions به شما امکان می‌دهد عملیات Realtime Database را با امتیازهای کامل سرپرست اجرا کنید و تضمین می‌کند که هر تغییر در Realtime Database به‌صورت جداگانه پردازش می‌شود. می‌توانید Firebase Realtime Database تغییر را ازطریق عکس آنی داده‌ها یا ازطریق Admin SDK اعمال کنید.

در چرخه حیات معمول، تابع Firebase Realtime Database کارهای زیر را انجام می‌دهد:

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

می‌توانید در پاسخ به نوشتن، ایجاد، به‌روزرسانی، یا حذف گره‌های پایگاه داده در Firebase Realtime Database، تابعی را راه‌اندازی کنید. برای کنترل زمان راه‌اندازی تابع، یکی از گرداننده‌های رویداد را مشخص کنید و مسیر Realtime Database را که در آن به رویدادها گوش می‌دهد مشخص کنید.

تنظیم مکان عملکرد

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

رسیدگی به رویدادهای Realtime Database

توابع به شما امکان می‌دهند رویدادهای Realtime Database را در دو سطح از ویژگی مدیریت کنید؛ می‌توانید به‌طور خاص فقط برای رویدادهای نوشتن، ایجاد، به‌روزرسانی، یا حذف گوش دهید، یا می‌توانید برای هر نوع تغییری در یک مرجع گوش دهید.

این گرداننده‌ها برای پاسخ دادن به رویدادهای Realtime Database دردسترس هستند:

Node.js

  • onValueWritten() وقتی داده‌ها در Realtime Database ایجاد، به‌روزرسانی، یا حذف می‌شوند، راه‌اندازی می‌شود.
  • onValueCreated() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database ایجاد شده باشد.
  • onValueUpdated() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database به‌روزرسانی شود.
  • onValueDeleted() فقط زمانی فعال می‌شود که داده‌ها در Realtime Database حذف شود.

پایتون

  • on_value_written() وقتی داده‌ها در Realtime Database ایجاد، به‌روزرسانی، یا حذف می‌شوند، راه‌اندازی می‌شود.
  • on_value_created() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database ایجاد شده باشد.
  • on_value_updated() فقط زمانی راه‌اندازی می‌شود که داده‌ها در Realtime Database به‌روزرسانی شود.
  • on_value_deleted() فقط زمانی فعال می‌شود که داده‌ها در Realtime Database حذف شود.

وارد کردن پیمانه‌های لازم

در منبع تابع، باید ماژول‌های SDK را که می‌خواهید استفاده کنید وارد کنید. برای این نمونه، لازم است که واحدهای HTTP و Realtime Database به‌همراه واحد Firebase Admin SDK برای نوشتن در Realtime Database وارد شوند.

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/https");
const {onValueCreated} = require("firebase-functions/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();

پایتون

# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

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

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

برای مثال:

Node.js

// All Realtime Database instances at path "/user/{uid}"
// There must be at least one Realtime Database present.
const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

// Instance named "my-app-db-2", at path "/user/{uid}".
// The "my-app-db-2" instance must exist in this region.
const OnWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
  },
  (event) => {
    // …
  }
);

// Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
// There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
  },
  (event) => {
    // …
  }
);

پایتون

# All Realtime Database instances at path "/user/{uid}"
# There must be at least one Realtime Database present.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

این پارامترها تابع شما را هدایت می‌کنند تا نوشتن‌ها را در مسیر خاصی در نمونه Realtime Database مدیریت کند.

مشخصات مسیر با همه نوشتن‌هایی که با مسیر در تماس هستند مطابقت دارد، ازجمله نوشتن‌هایی که در هر جایی زیر آن اتفاق می‌افتد. اگر مسیر تابع را /foo/bar تنظیم کنید، با رویدادهای هر دو مکان زیر مطابقت دارد:

 /foo/bar
 /foo/bar/baz/really/deep/path

در هر دو حالت، Firebase تفسیر می‌کند که رویداد در /foo/bar رخ می‌دهد، و داده‌های رویداد شامل داده‌های قدیمی و جدید در /foo/bar است. اگر داده‌های رویداد ممکن است زیاد باشد، به‌جای استفاده از یک تابع در نزدیکی ریشه پایگاه داده، از چند تابع در مسیرهای عمیق‌تر استفاده کنید. برای بهترین عملکرد، فقط داده‌ها را در عمیق‌ترین سطح ممکن درخواست کنید.

کارت جوکر و گرفتن عکس

می‌توانید از {key}،‏ {key=*}،‏ {key=prefix*}،‏ {key=*suffix} برای ضبط کردن استفاده کنید. ‫*، prefix*، *suffix برای جایگزین تک‌بخشی. توجه: ** نشان‌دهنده جای‌بان چندبخشی است که Realtime Database از آن پشتیبانی نمی‌کند. آشنایی با الگوهای مسیر را ببینید.

استفاده از نویسه عام در مسیر. می‌توانید مؤلفه مسیر را به‌عنوان نویسه عام مشخص کنید:

  • بااستفاده از ستاره، *. برای مثال، foo/* با هر فرزند یک سطح از سلسله‌مراتب گره زیر foo/ مطابقت دارد.
  • استفاده از بخشی که دقیقاً حاوی ستاره، *، است. برای مثال، foo/app*-us با هر بخش فرزند زیر foo/ با پیشوند app و پسوند -us مطابقت دارد.

مسیرهای دارای نویسه عام می‌توانند با چندین رویداد مطابقت داشته باشند، مثلاً یک نوشتن. درج

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

دو بار با مسیر "/foo/*" مطابقت دارد: یک بار با "hello": "world" و بار دیگر با "firebase": "functions".

درحال ضبط مسیر. می‌توانید مطابقت‌های مسیر را در متغیرهای نام‌گذاری‌شده ضبط کنید تا در کد تابع شما استفاده شوند (مثلاً /user/{uid}، /user/{uid=*-us}).

مقادیر متغیرهای ضبط در شیء database.DatabaseEvent.params تابع شما دردسترس است.

جایگزین کردن نمونه. همچنین می‌توانید بااستفاده از نویسه عام، مؤلفه نمونه را مشخص کنید. نویسه عام نمونه می‌تواند پیشوند، پسوند، یا هر دو را داشته باشد (مثلاً my-app-*-prod).

مرجع ضبط و کارت جایگزین

با Cloud Functions (نسل دوم) و Realtime Database، هنگام مشخص کردن ref و instance می‌توان از الگو استفاده کرد. هر میانای راه‌انداز گزینه‌های زیر را برای تعیین دامنه تابع خواهد داشت:

مشخص کردن ref مشخص کردن instance رفتار
تک‌آهنگ (/foo/bar) مشخص نکردن گرداننده را به همه نمونه‌ها در منطقه تابع محدود می‌کند.
تک‌آهنگ (/foo/bar) تک‌آهنگ (‘my-new-db') ‫Scopes handler to the specific instance in the function region.
تک‌آهنگ (/foo/bar) الگو (‘inst-prefix*') گرداننده را به همه نمونه‌هایی که با الگو در منطقه تابع مطابقت دارند محدود می‌کند.
الگو (/foo/{bar}) مشخص نکردن گرداننده را به همه نمونه‌ها در منطقه تابع محدود می‌کند.
الگو (/foo/{bar}) تک‌آهنگ (‘my-new-db') ‫Scopes handler to the specific instance in the function region.
الگو (/foo/{bar}) الگو (‘inst-prefix*') گرداننده را به همه نمونه‌هایی که با الگو در منطقه تابع مطابقت دارند محدود می‌کند.

مدیریت داده‌های رویداد

وقتی رویداد Realtime Database راه‌اندازی می‌شود، شیء Event را به تابع گرداننده شما ارسال می‌کند. این شیء دارای ویژگی data است که برای رویدادهای ایجاد و حذف، حاوی عکس آنی از داده‌های ایجادشده یا حذف‌شده است.

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

Node.js

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the
      // Realtime Database returns a Promise.
      return event.data.ref.parent.child("uppercase").set(uppercase);
    },
);

پایتون

@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

خواندن مقدار قبلی

برای رویدادهای write یا update، ویژگی data یک شیء Change است که حاوی دو عکس آنی است که وضعیت داده‌ها را قبل و بعداز رویداد راه‌انداز نشان می‌دهد. شیء Change دارای ویژگی before است که به شما امکان می‌دهد آنچه را که قبل‌از رویداد در Realtime Database ذخیره شده است بررسی کنید و ویژگی after که نشان‌دهنده وضعیت داده‌ها پس‌از وقوع رویداد است.

برای مثال، از ویژگی before می‌توان استفاده کرد تا مطمئن شد تابع فقط زمانی نوشتار را به حروف بزرگ تبدیل می‌کند که برای اولین‌بار ایجاد شده باشد:

Node.js

  exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => {
        // Only edit data when it is first created.
        if (event.data.before.exists()) {
          return null;
        }
        // Exit when the data is deleted.
        if (!event.data.after.exists()) {
          return null;
        }
        // Grab the current value of what was written to the Realtime Database.
        const original = event.data.after.val();
        console.log('Uppercasing', event.params.pushId, original);
        const uppercase = original.toUpperCase();
        // You must return a Promise when performing asynchronous tasks inside a Functions such as
        // writing to the Firebase Realtime Database.
        // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
        return event.data.after.ref.parent.child('uppercase').set(uppercase);
      });

پایتون

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

دسترسی به بافت اصالت‌سنجی

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

  • authtype: نوع اصلی که رویداد را راه‌اندازی کرده است. مقادیر ممکن عبارت‌اند از:
    • app_user: کاربر نهایی برنامه توسعه‌دهنده.
    • admin: حساب سرویس.
    • ‫unauthenticated: کاربر تأییدنشده.
    • unknown: پیش‌فرض وقتی اطلاعات اصالت‌سنجی دردسترس نیست.
  • authid: شناسه یکتای اصلی.
    • اگر authtype app_user باشد، این UID کاربر است.
    • اگر authtype admin باشد، این حساب سرویس یا ایمیل کاربر IAM است.

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

Node.js

// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onValueWritten} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();

exports.dbtrigger = onValueWritten("/messages/{pushId}/original", async (event) => {
  // 1. Check whether authtype is admin. If it is, skip this operation.
  if (event.authType === "admin") {
    logger.log("Modification by admin detected. Skipping uppercase conversion.");
    return null;
  }

  // 2. Retrieve the userID of the sender (assumed sibling node 'senderId')
  const snapshot = await event.data.after.ref.parent.child("senderId").get();
  const senderId = snapshot.val();

  // 3. Check if userID of sender of message = event.authid
  if (senderId !== event.authId) {
    logger.error(`Unauthorized write: senderId (${senderId}) does not match authId (${event.authId})`);
    return null;
  }

  // Grab the value that was written to the Realtime Database.
  const original = event.data.after.val();
  logger.log("Uppercasing", event.params.pushId, original);
  const uppercase = original.toUpperCase();

  // Return the promise to set the "uppercase" sibling node.
  return event.data.after.ref.parent.child("uppercase").set(uppercase);
});

پایتون

from firebase_functions import db_fn
from firebase_admin import initialize_app, db

initialize_app()

@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[db_fn.Change]) -> None:
    # 1. Check whether authtype is admin. If it is, skip this operation.
    if event.auth_type == "admin":
        print("Admin user detected. Skipping.")
        return

    # 2. Retrieve the userID of the sender (assumed sibling node: 'senderId')
    parent_ref = db.reference(event.reference).parent
    sender_id = parent_ref.child("senderId").get()

    # 3. Check if userID of sender = event.auth_id
    if sender_id != event.auth_id:
        print(f"Unauthorized: sender_id {sender_id} != auth_id {event.auth_id}")
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value and uppercase it
    original = event.data.after
    if not isinstance(original, str):
        return

    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent_ref.child("uppercase").set(upper)