مشغلات Realtime Database


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

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

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

تشغيل وظيفة Realtime Database

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

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

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

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

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

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

  • مثيل قاعدة بيانات Realtime الافتراضي: 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');
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);
    });