توسيع نطاق Cloud Firestore باستخدام Cloud Functions (الجيل الأول)

باستخدام Cloud Functions، يمكنك نشر رمز Node.js لمعالجة الأحداث التي يتم تفعيلها بسبب التغييرات في قاعدة بيانات Cloud Firestore. يتيح لك ذلك إضافة وظائف من جهة الخادم بسهولة إلى تطبيقك بدون تشغيل الخوادم الخاصة بك.

للاطّلاع على أمثلة لحالات الاستخدام، يُرجى مراجعة مقالة ما هي الإجراءات التي يمكنني اتّخاذها باستخدام Cloud Functions؟ أو مستودع نماذج الدوال على GitHub.

مشغّلات الدوال Cloud Firestore

تصدِّر حزمة تطوير البرامج (SDK) لـ 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 ...
    });

تفعيل دالة لجميع التغييرات التي يتم إجراؤها على مستند

إذا لم يكن نوع الحدث الذي يتم تفعيله مهمًا بالنسبة إليك، يمكنك الاستماع إلى جميع التغييرات في مستند Cloud Firestore باستخدام الدالة onWrite() مع حرف بدل . تستدعي دالة المثال هذه 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 (الجيل الأول) تتطلّب قاعدة بيانات حالية باسم "(default)" في وضع 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 Function بسبب تجاوز الحجم الحدّ الأقصى للجيل الأول..." بمستوى خطورة error. يمكنك العثور على اسم الدالة ضمن الحقل functionName. إذا كان الحقل the receiveTimestamp لا يزال ضمن ساعة من الآن، يمكنك استنتاج محتوى الحدث الفعلي من خلال قراءة المستند المعنيّ باستخدام لقطة قبل الطابع الزمني وبعده.
    • لتجنُّب معدّل خطوات الجري هذا، يمكنك إجراء ما يلي:
      • نقل البيانات والترقية إلى Cloud Functions (الجيل الثاني)
      • تقليل حجم المستند
      • حذف Cloud Functions المعنيّة
    • يمكنك إيقاف التسجيل نفسه باستخدام الاستثناءات ولكن يُرجى العِلم أنّه لن يتم تسليم الأحداث المخالفة.