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


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

في دورة الحياة النموذجية، تنفّذ دالة قاعدة بيانات Firebase في الوقت الفعلي ما يلي:

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

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

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

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

تتيح لك الدوال التعامل مع أحداث قاعدة البيانات في الوقت الفعلي على مستويَين من الدقة، حيث يمكنك الاستماع تحديدًا إلى أحداث الإنشاء أو التحديث أو الحذف فقط، أو الاستماع إلى أي تغيير من أي نوع في المسار. تدعم دوال السحابة معالِجات الأحداث التالية لقاعدة البيانات في الوقت الفعلي:

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

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

للتحكّم في وقت ومكان تشغيل الدالة، استدعِ ref(path) لتحديد مسار، ويمكنك اختياريًا تحديد مثيل قاعدة بيانات "الوقت الفعلي" باستخدام instance('INSTANCE_NAME'). إذا لم تحدد مثيلاً، يتم نشر الدالة إلى مثيل قاعدة البيانات التلقائي في الوقت الفعلي لمشروع Firebase على سبيل المثال:

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

توجه هذه الطرق وظيفتك للتعامل مع عمليات الكتابة في مسار معين داخل مثيل قاعدة البيانات في الوقت الفعلي. تتطابق مواصفات المسار مع جميع النصوص التي تلمس المسار، بما في ذلك الكتابة التي تحدث في أي مكان أسفله. إذا ضبطت المسار لدالتك على /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".

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

عند معالجة حدث قاعدة بيانات في الوقت الفعلي، يكون عنصر البيانات الذي يتم عرضه هو 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 التي تتيح لك فحص البيانات التي تم حفظها في "قاعدة بيانات الوقت الفعلي" قبل الحدث. تعرض السمة 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);
    });