रीयलटाइम डेटाबेस के ट्रिगर


Cloud Functions की मदद से, Firebase रीयल टाइम डेटाबेस में इवेंट मैनेज किए जा सकते हैं. इसके लिए क्लाइंट कोड को अपडेट करने की ज़रूरत नहीं होती. Cloud Functions, आपको पूरे एडमिन के खास अधिकारों के साथ रीयल टाइम डेटाबेस ऑपरेशन चलाने देता है. साथ ही, यह पक्का करता है कि रीयलटाइम डेटाबेस में होने वाला हर बदलाव अलग-अलग प्रोसेस हो. DataSnapshot या एडमिन SDK की मदद से, Firebase रीयल टाइम डेटाबेस में बदलाव किए जा सकते हैं.

किसी सामान्य लाइफ़साइकल में, Firebase रीयल टाइम डेटाबेस फ़ंक्शन ये काम करता है:

  1. किसी खास रीयल टाइम डेटाबेस की जगह में बदलाव का इंतज़ार करता है.
  2. यह तब ट्रिगर होता है, जब कोई इवेंट होता है और अपने टास्क पूरे करते हैं (इस्तेमाल के उदाहरणों के लिए, Cloud Functions की मदद से क्या किया जा सकता है? सेक्शन देखें).
  3. इससे ऐसा डेटा ऑब्जेक्ट मिलता है जिसमें बताए गए दस्तावेज़ में सेव किए गए डेटा का स्नैपशॉट होता है.

रीयलटाइम डेटाबेस फ़ंक्शन को ट्रिगर करना

रीयल टाइम डेटाबेस इवेंट के लिए, functions.database की मदद से नए फ़ंक्शन बनाएं. यह कंट्रोल करने के लिए कि फ़ंक्शन कब ट्रिगर हो, किसी एक इवेंट हैंडलर के बारे में बताएं और रीयल टाइम डेटाबेस के पाथ की जानकारी दें, जहां वह इवेंट सुन सकेगा.

इवेंट हैंडलर सेट करना

फ़ंक्शन की मदद से, रीयल टाइम डेटाबेस इवेंट को दो लेवल पर मैनेज किया जा सकता है. आपके पास खास तौर पर, सिर्फ़ बनाने, अपडेट करने या मिटाने के इवेंट की जानकारी सुनने का विकल्प होता है. इसके अलावा, पाथ में होने वाले किसी भी बदलाव को सुना जा सकता है. Cloud Functions, रीयलटाइम डेटाबेस के लिए इन इवेंट हैंडलर के साथ काम करता है:

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