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

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

برای مثال‌هایی از موارد استفاده، با Cloud Functions چه کارهایی می‌توانم انجام دهم؟ را ببینید. یا مخزن GitHub نمونه‌های توابع.

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

کیت توسعه نرم‌افزار 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 ...
    });

راه‌اندازی تابع برای همه تغییرات سند

اگر نوع رویدادی که اجرا می‌شود برایتان مهم نیست، می‌توانید بااستفاده از تابع onWrite() با نویسه عام، به همه تغییرات در سند Cloud Firestore گوش دهید. این تابع نمونه 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 (نسل اول) پیش‌نیاز یک پایگاه داده «(پیش‌فرض)» موجود در حالت بومی 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 موردنظر را حذف کنید
    • می‌توانید خود ثبت گزارش را بااستفاده از موارد استثنا خاموش کنید اما توجه داشته باشید که رویدادهای متخلف همچنان تحویل داده نخواهند شد.