ทริกเกอร์ฐานข้อมูล

Cloud Functions ช่วยให้คุณจัดการเหตุการณ์ในฐานข้อมูลเรียลไทม์ของ Firebase โดยไม่จำเป็นต้องอัปเดตโค้ดไคลเอ็นต์ Cloud Functions ช่วยให้คุณเรียกใช้การดำเนินการ Realtime Database ด้วยสิทธิ์การดูแลระบบเต็มรูปแบบ และทำให้แน่ใจว่าการเปลี่ยนแปลงแต่ละรายการใน Realtime Database จะได้รับการประมวลผลทีละรายการ คุณเปลี่ยนแปลงฐานข้อมูลเรียลไทม์ของ Firebase ได้ผ่าน DataSnapshot หรือผ่าน Admin SDK

ในวงจรชีวิตทั่วไป ฟังก์ชันฐานข้อมูลเรียลไทม์ของ Firebase จะทำสิ่งต่อไปนี้:

  1. รอการเปลี่ยนแปลงตำแหน่งฐานข้อมูลเรียลไทม์เฉพาะ
  2. ทริกเกอร์เมื่อเหตุการณ์เกิดขึ้นและดำเนินงาน (ดู ฉันจะทำอะไรกับ Cloud Functions ได้บ้าง สำหรับตัวอย่างกรณีการใช้งาน)
  3. รับออบเจ็กต์ข้อมูลที่มีสแน็ปช็อตของข้อมูลที่จัดเก็บไว้ในเอกสารที่ระบุ

เรียกใช้ฟังก์ชันฐานข้อมูลเรียลไทม์

สร้างฟังก์ชันใหม่สำหรับเหตุการณ์ Realtime Database ด้วย functions.database ในการควบคุมเวลาที่ฟังก์ชันทริกเกอร์ ให้ระบุตัวจัดการเหตุการณ์ตัวใดตัวหนึ่ง และระบุพาธ Realtime Database ที่จะรับฟังเหตุการณ์

ตั้งค่าตัวจัดการเหตุการณ์

ฟังก์ชันช่วยให้คุณจัดการเหตุการณ์ Realtime Database ได้ที่ระดับความจำเพาะสองระดับ คุณสามารถฟังเฉพาะเหตุการณ์ที่สร้าง อัปเดต หรือการลบเท่านั้น หรือคุณสามารถฟังการเปลี่ยนแปลงใดๆ ของเส้นทางก็ได้ Cloud Functions รองรับตัวจัดการเหตุการณ์เหล่านี้สำหรับ Realtime Database:

  • onWrite() ซึ่งจะทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime Database
  • onCreate() ซึ่งจะทริกเกอร์เมื่อมีการสร้างข้อมูลใหม่ในฐานข้อมูลเรียลไทม์
  • onUpdate() ซึ่งจะทริกเกอร์เมื่อมีการอัปเดตข้อมูลใน Realtime Database
  • 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"

จัดการข้อมูลเหตุการณ์

เมื่อจัดการเหตุการณ์ Realtime Database ออบเจ็กต์ข้อมูลที่ส่งคืนคือ DataSnapshot สำหรับเหตุการณ์ onWrite หรือ onUpdate พารามิเตอร์แรกคืออ็อบเจ็กต์ Change ที่มีสแน็ปช็อตสองอันที่แสดงสถานะข้อมูลก่อนและหลังเหตุการณ์ที่ทริกเกอร์ สำหรับเหตุการณ์ onCreate และ onDelete ออบเจ็กต์ข้อมูลที่ส่งคืนคือสแน็ปช็อตของข้อมูลที่สร้างหรือลบ

ในตัวอย่างนี้ ฟังก์ชันดึงสแน็ปช็อตสำหรับพาธที่ระบุเป็น snap แปลงสตริงที่ตำแหน่งนั้นเป็นตัวพิมพ์ใหญ่ และเขียนสตริงที่แก้ไขนั้นไปยังฐานข้อมูล:

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