قم بتوسيع قاعدة البيانات في الوقت الفعلي باستخدام الوظائف السحابية


باستخدام Cloud Functions، يمكنك التعامل مع الأحداث في Firebase Realtime Database دون الحاجة إلى تحديث رمز العميل. تتيح لك Cloud Functions تشغيل عمليات قاعدة بيانات Realtime مع امتيازات إدارية كاملة، وتضمن معالجة كل تغيير في قاعدة بيانات Realtime بشكل فردي. يمكنك إجراء تغييرات على قاعدة بيانات Firebase Realtime عبر DataSnapshot أو عبر Admin SDK .

في دورة الحياة النموذجية، تقوم وظيفة Firebase Realtime Database بما يلي:

  1. ينتظر التغييرات على موقع قاعدة بيانات Realtime معين.
  2. يتم تشغيله عند وقوع حدث وتنفيذ مهامه (راجع ما الذي يمكنني فعله باستخدام وظائف السحابة؟ للحصول على أمثلة لحالات الاستخدام).
  3. يتلقى كائن بيانات يحتوي على لقطة من البيانات المخزنة في المستند المحدد.

تشغيل وظيفة قاعدة البيانات في الوقت الحقيقي

قم بإنشاء وظائف جديدة لأحداث Realtime Database باستخدام functions.database . للتحكم في وقت تشغيل الوظيفة، حدد أحد معالجات الأحداث، وحدد مسار قاعدة بيانات Realtime حيث سيتم الاستماع إلى الأحداث.

قم بتعيين معالج الحدث

تتيح لك الوظائف التعامل مع أحداث قاعدة البيانات في الوقت الفعلي على مستويين من الخصوصية؛ يمكنك الاستماع خصيصًا لأحداث الإنشاء أو التحديث أو الحذف فقط، أو يمكنك الاستماع لأي تغيير من أي نوع على المسار. تدعم Cloud Functions معالجات الأحداث التالية لقاعدة بيانات Realtime:

  • onWrite() ، والذي يتم تشغيله عند إنشاء البيانات أو تحديثها أو حذفها في قاعدة بيانات Realtime.
  • onCreate() ، والذي يتم تشغيله عند إنشاء بيانات جديدة في قاعدة بيانات Realtime.
  • onUpdate() ، الذي يتم تشغيله عند تحديث البيانات في قاعدة بيانات الوقت الفعلي.
  • onDelete() ، الذي يتم تشغيله عند حذف البيانات من قاعدة بيانات Realtime.

حدد المثيل والمسار

للتحكم في متى وأين يجب تشغيل وظيفتك، اتصل بـ ref(path) لتحديد مسار، وحدد بشكل اختياري مثيل قاعدة بيانات Realtime مع instance('INSTANCE_NAME') . إذا لم تحدد مثيلاً، فسيتم نشر الوظيفة إلى مثيل قاعدة بيانات Realtime الافتراضي لمشروع Firebase على سبيل المثال:

  • مثيل قاعدة بيانات الوقت الفعلي الافتراضي: functions.database.ref('/foo/bar')
  • المثيل المسمى "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

تقوم هذه الطرق بتوجيه وظيفتك للتعامل مع عمليات الكتابة في مسار معين داخل مثيل قاعدة بيانات Realtime. تتطابق مواصفات المسار مع جميع عمليات الكتابة التي تمس المسار، بما في ذلك عمليات الكتابة التي تحدث في أي مكان أسفله. إذا قمت بتعيين المسار لوظيفتك كـ /foo/bar ، فإنه يطابق الأحداث في كلا الموقعين:

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

في كلتا الحالتين، يفسر Firebase أن الحدث يقع في /foo/bar ، وأن بيانات الحدث تتضمن البيانات القديمة والجديدة في /foo/bar . إذا كانت بيانات الحدث كبيرة، ففكر في استخدام وظائف متعددة في مسارات أعمق بدلاً من وظيفة واحدة بالقرب من جذر قاعدة البيانات. للحصول على أفضل أداء، اطلب البيانات الموجودة على أعمق مستوى ممكن فقط.

يمكنك تحديد مكون مسار كحرف بدل من خلال إحاطته بأقواس متعرجة؛ ref('foo/{bar}') يطابق أي فرع من /foo . تتوفر قيم مكونات مسار أحرف البدل هذه داخل كائن EventContext.params الخاص بوظيفتك. في هذا المثال، القيمة متاحة كـ context.params.bar .

يمكن أن تتطابق المسارات التي تحتوي على أحرف البدل مع أحداث متعددة من عملية كتابة واحدة. إدراج من

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

يطابق المسار "/foo/{bar}" مرتين: مرة مع "hello": "world" ومرة ​​أخرى مع "firebase": "functions" .

التعامل مع بيانات الحدث

عند التعامل مع حدث قاعدة بيانات Realtime، يكون كائن البيانات الذي يتم إرجاعه هو DataSnapshot . بالنسبة لأحداث onWrite أو onUpdate ، فإن المعلمة الأولى هي كائن Change الذي يحتوي على لقطتين تمثلان حالة البيانات قبل وبعد حدث التشغيل. بالنسبة لأحداث onCreate و onDelete ، يكون كائن البيانات الذي يتم إرجاعه عبارة عن لقطة من البيانات التي تم إنشاؤها أو حذفها.

في هذا المثال، تسترد الدالة اللقطة للمسار المحدد، وتحول السلسلة في ذلك الموقع إلى أحرف كبيرة، وتكتب تلك السلسلة المعدلة إلى قاعدة البيانات:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.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 snapshot.ref.parent.child('uppercase').set(uppercase);
    });

الوصول إلى معلومات مصادقة المستخدم

من EventContext.auth و EventContext.authType ، يمكنك الوصول إلى معلومات المستخدم، بما في ذلك الأذونات، للمستخدم الذي قام بتشغيل الوظيفة. يمكن أن يكون هذا مفيدًا لفرض قواعد الأمان، مما يسمح لوظيفتك بإكمال عمليات مختلفة بناءً على مستوى أذونات المستخدم:

const functions = require('firebase-functions');
const admin = require('firebase-admin');

exports.simpleDbFunction = functions.database.ref('/path')
    .onCreate((snap, context) => {
      if (context.authType === 'ADMIN') {
        // do something
      } else if (context.authType === 'USER') {
        console.log(snap.val(), 'written by', context.auth.uid);
      }
    });

يمكنك أيضًا الاستفادة من معلومات مصادقة المستخدم من أجل "انتحال شخصية" مستخدم وتنفيذ عمليات الكتابة نيابةً عن المستخدم. تأكد من حذف مثيل التطبيق كما هو موضح أدناه لمنع مشكلات التزامن:

exports.impersonateMakeUpperCase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snap, context) => {
      const appOptions = JSON.parse(process.env.FIREBASE_CONFIG);
      appOptions.databaseAuthVariableOverride = context.auth;
      const app = admin.initializeApp(appOptions, 'app');
      const uppercase = snap.val().toUpperCase();
      const ref = snap.ref.parent.child('uppercase');

      const deleteApp = () => app.delete().catch(() => null);

      return app.database().ref(ref).set(uppercase).then(res => {
        // Deleting the app is necessary for preventing concurrency leaks
        return deleteApp().then(() => res);
      }).catch(err => {
        return deleteApp().then(() => Promise.reject(err));
      });
    });

قراءة القيمة السابقة

يحتوي كائن Change على خاصية before تتيح لك فحص ما تم حفظه في قاعدة بيانات Realtime قبل الحدث. تقوم الخاصية before بإرجاع DataSnapshot حيث تشير كافة الأساليب (على سبيل المثال، val() و exists() ) إلى القيمة السابقة. يمكنك قراءة القيمة الجديدة مرة أخرى إما باستخدام DataSnapshot الأصلي أو قراءة الخاصية after . هذه الخاصية عند أي Change هي DataSnapshot أخرى تمثل حالة البيانات بعد وقوع الحدث.

على سبيل المثال، يمكن استخدام الخاصية before للتأكد من أن الدالة تكتب النص بأحرف كبيرة فقط عند إنشائها لأول مرة:

exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onWrite((change, context) => {
      // Only edit data when it is first created.
      if (change.before.exists()) {
        return null;
      }
      // Exit when the data is deleted.
      if (!change.after.exists()) {
        return null;
      }
      // Grab the current value of what was written to the Realtime Database.
      const original = change.after.val();
      console.log('Uppercasing', context.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 change.after.ref.parent.child('uppercase').set(uppercase);
    });