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


باستخدام Cloud Functions، يمكنك التعامل مع الأحداث في Firebase Realtime Database بدون الحاجة إلى تعديل رمز العميل. يتيح لك Cloud Functions تشغيل عمليات Realtime Database باستخدام الإدارة الكاملة وتضمن معالجة كل تغيير في Realtime Database. كل على حدة. يمكنك إجراء Firebase Realtime Database تغييرات من خلال DataSnapshot أو من خلال 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 للتأكّد من أنّ الدالة فقط كتابة النص بأحرف كبيرة عند إنشائه لأول مرة:

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