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


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

في أي دورة حياة عادية، تقوم دالة Firebase Realtime Database بما يلي:

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

تشغيل دالة Realtime Database

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

ضبط معالِج الحدث

تتيح لك الدوال التعامل مع أحداث Realtime Database على مستويَين من الخصوصية. التي يمكنك الاستماع إليها تحديدًا للإنشاء والتحديث أو حذف الأحداث، أو يمكنك الاستماع إلى أي تغيير من أي نوع في المسار. يتوافق "Cloud Functions" مع معالِجات الأحداث التالية لـ "Realtime Database":

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

تحديد العنصر والمسار

للتحكّم في وقت بدء الدالة ومكانها، يمكنك استدعاء ref(path) لتحديد مسار، ويمكنك اختياريًا تحديد مثيل Realtime Database باستخدام instance('INSTANCE_NAME'). في حال عدم تحديد مثيل، يتم نشر الدالة على مثيل Realtime Database التلقائي لمشروع Firebase. على سبيل المثال:

  • مثيل Realtime Database التلقائي: functions.database.ref('/foo/bar')
  • النسخة التي تحمل الاسم "my-app-db-2": functions.database.instance('my-app-db-2').ref('/foo/bar')

توجه هذه الطرق دالتك للتعامل مع عمليات الكتابة في مسار معين داخل مثيل Realtime Database. تتطابق مواصفات المسار مع كل عمليات الكتابة التي تؤثر في مسار، بما في ذلك عمليات الكتابة التي تحدث في أي مكان تحته. إذا ضبطت المسار لوظيفتك على /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 Database، يكون عنصر البيانات الذي يتم عرضه هو 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/v1');
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 Database قبل فعالية. تعرض السمة before علامة DataSnapshot حيث تكون جميع (على سبيل المثال، val() و exists()) يشير إلى القيمة السابقة. يمكنك قراءة القيمة الجديدة مرة أخرى باستخدام السمة الأصلية DataSnapshot أو قراءة السمة after . هذه السمة في أيّ Change هي DataSnapshot أخرى تمثّل حالة البيانات بعد وقوع الحدث.

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

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);
    });