ทริกเกอร์ Realtime Database


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

ในวงจรการใช้งานทั่วไป Firebase Realtime Database ฟังก์ชันจะทำสิ่งต่อไปนี้

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

คุณทริกเกอร์ฟังก์ชันเพื่อตอบสนองต่อการเขียน สร้าง อัปเดต หรือลบโหนดฐานข้อมูลใน Firebase Realtime Database ได้ หากต้องการ ควบคุมเวลาที่ฟังก์ชันทริกเกอร์ ให้ระบุตัวแฮนเดิลเหตุการณ์อย่างใดอย่างหนึ่ง และ ระบุเส้นทาง Realtime Database ที่จะใช้รับฟังเหตุการณ์

การตั้งค่าตำแหน่งฟังก์ชัน

ระยะห่างระหว่างตำแหน่งของอินสแตนซ์ Realtime Database กับตำแหน่งของฟังก์ชันอาจทำให้เกิดเวลาในการตอบสนองของเครือข่ายอย่างมาก นอกจากนี้ ภูมิภาคที่ไม่ตรงกันอาจทำให้การติดตั้งใช้งานล้มเหลว หากต้องการหลีกเลี่ยงสถานการณ์เหล่านี้ ให้ระบุตำแหน่งฟังก์ชันเพื่อให้ตรงกับตำแหน่งอินสแตนซ์ฐานข้อมูล

การจัดการเหตุการณ์ Realtime Database

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

ตัวแฮนเดิลต่อไปนี้พร้อมใช้งานสำหรับการตอบสนองต่อเหตุการณ์ Realtime Database

Node.jsPython
  • onValueWritten() ทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime Database
  • onValueCreated() จะทริกเกอร์เมื่อสร้างข้อมูลใน Realtime Database เท่านั้น
  • onValueUpdated() จะทริกเกอร์เมื่อมีการอัปเดตข้อมูลใน Realtime Database เท่านั้น
  • onValueDeleted() จะทริกเกอร์เมื่อมีการลบข้อมูลใน Realtime Database เท่านั้น
  • on_value_written() ทริกเกอร์เมื่อมีการสร้าง อัปเดต หรือลบข้อมูลใน Realtime Database
  • on_value_created() จะทริกเกอร์เมื่อสร้างข้อมูลใน Realtime Database เท่านั้น
  • on_value_updated() จะทริกเกอร์เมื่อมีการอัปเดตข้อมูลใน Realtime Database เท่านั้น
  • on_value_deleted() จะทริกเกอร์เมื่อมีการลบข้อมูลใน Realtime Database เท่านั้น

นำเข้าโมดูลที่จำเป็น

ในแหล่งที่มาของฟังก์ชัน คุณต้องนําเข้าโมดูล SDK ที่ต้องการใช้ สำหรับ ตัวอย่างนี้ คุณต้องนำเข้าโมดูล HTTP และ Realtime Database พร้อมกับโมดูล Firebase Admin SDK เพื่อเขียนไปยัง Realtime Database

Node.jsPython
// The Cloud Functions for Firebase SDK to setup triggers and logging.
const {onRequest} = require("firebase-functions/v2/https");
const {onValueCreated} = require("firebase-functions/v2/database");
const {logger} = require("firebase-functions");

// The Firebase Admin SDK to access the Firebase Realtime Database.
const admin = require("firebase-admin");
admin.initializeApp();
# The Cloud Functions for Firebase SDK to create Cloud Functions and set up triggers.
from firebase_functions import db_fn, https_fn

# The Firebase Admin SDK to access the Firebase Realtime Database.
from firebase_admin import initialize_app, db

app = initialize_app()

ระบุอินสแตนซ์และเส้นทาง

หากต้องการควบคุมเวลาและตำแหน่งที่ควรเรียกใช้ฟังก์ชัน ให้กำหนดค่าฟังก์ชันด้วยเส้นทางและอินสแตนซ์ Realtime Database (ไม่บังคับ) หากคุณไม่ได้ ระบุอินสแตนซ์ ฟังก์ชันจะรับฟังRealtime Databaseอินสแตนซ์ทั้งหมด ในภูมิภาคของฟังก์ชัน นอกจากนี้ คุณยังระบุรูปแบบอินสแตนซ์ Realtime Database เพื่อทําการติดตั้งใช้งานกับชุดย่อยของอินสแตนซ์ที่เลือกในภูมิภาคเดียวกันได้ด้วย

เช่น

Node.jsPython
// All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
// There must be at least one Realtime Database present in us-central1.
const onWrittenFunctionDefault = onValueWritten("/user/{uid}", (event) => {
  // …
});

// Instance named "my-app-db-2", at path "/user/{uid}".
// The "my-app-db-2" instance must exist in this region.
const OnWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid}",
    instance: "my-app-db-2"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);

// Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
// There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
const onWrittenFunctionInstance = onValueWritten(
  {
    ref: "/user/{uid=*@gmail.com}",
    instance: "my-app-db-*"
    // This example assumes us-central1, but to set location:
    // region: "europe-west1"
  },
  (event) => {
    // …
  }
);
# All Realtime Database instances in default function region us-central1 at path "/user/{uid}"
# There must be at least one Realtime Database present in us-central1.
@db_fn.on_value_written(r"/user/{uid}")
def onwrittenfunctiondefault(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance named "my-app-db-2", at path "/user/{uid}".
# The "my-app-db-2" instance must exist in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid}",
    instance="my-app-db-2",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

# Instance with "my-app-db-" prefix, at path "/user/{uid}", where uid ends with @gmail.com.
# There must be at least one Realtime Database with "my-app-db-*" prefix in this region.
@db_fn.on_value_written(
    reference=r"/user/{uid=*@gmail.com}",
    instance="my-app-db-*",
    # This example assumes us-central1, but to set location:
    # region="europe-west1",
)
def on_written_function_instance(event: db_fn.Event[db_fn.Change]):
    # ...
    pass

พารามิเตอร์เหล่านี้จะสั่งให้ฟังก์ชันจัดการการเขียนที่เส้นทางหนึ่งๆ ภายในอินสแตนซ์ Realtime Database

การระบุเส้นทางจะตรงกับการเขียนทั้งหมดที่แตะเส้นทาง รวมถึงการเขียนที่เกิดขึ้นที่ใดก็ตามด้านล่าง หากคุณตั้งค่าเส้นทาง สำหรับฟังก์ชันเป็น /foo/bar ระบบจะจับคู่เหตุการณ์ในทั้ง 2 ตำแหน่งต่อไปนี้

 /foo/bar
 /foo/bar/baz/really/deep/path

ไม่ว่าจะในกรณีใดก็ตาม Firebase จะตีความว่าเหตุการณ์เกิดขึ้นที่ /foo/bar และข้อมูลเหตุการณ์จะมีข้อมูลเก่าและใหม่ที่ /foo/bar หากข้อมูลเหตุการณ์ อาจมีขนาดใหญ่ ให้ลองใช้ฟังก์ชันหลายรายการในเส้นทางที่ลึกลงไปแทน ฟังก์ชันเดียวที่อยู่ใกล้รูทของฐานข้อมูล ขอข้อมูลที่ระดับลึกที่สุดเท่าที่จะเป็นไปได้เท่านั้นเพื่อประสิทธิภาพที่ดีที่สุด

การใช้ไวลด์การ์ดและการจับภาพ

คุณใช้ {key}, {key=*}, {key=prefix*}, {key=*suffix} เพื่อจับภาพได้ *, prefix*, *suffix สำหรับการใช้ไวลด์การ์ดแบบส่วนเดียว หมายเหตุ: ** แสดงถึงการใช้ไวลด์การ์ดแบบหลายส่วน ซึ่ง Realtime Database ไม่รองรับ ดูทำความเข้าใจรูปแบบเส้นทาง

การใช้ไวลด์การ์ดในเส้นทาง คุณระบุคอมโพเนนต์เส้นทางเป็นไวลด์การ์ดได้โดยทำดังนี้

  • การใช้ดอกจัน * เช่น foo/* จะตรงกับลูกๆ ที่อยู่ต่ำกว่า foo/ ในลำดับชั้นของโหนด 1 ระดับ
  • การใช้กลุ่มที่มีเครื่องหมายดอกจัน * เช่น foo/app*-us จะตรงกับกลุ่มย่อยใดก็ได้ด้านล่าง foo/ ที่มีคำนำหน้า app และคำต่อท้าย -us

เส้นทางที่มีไวลด์การ์ดจะจับคู่เหตุการณ์หลายรายการได้ เช่น จากการเขียนครั้งเดียว แทรก

{
  "foo": {
    "hello": "world",
    "firebase": "functions"
  }
}

จับคู่เส้นทาง "/foo/*" 2 ครั้ง โดยครั้งแรกใช้ "hello": "world" และอีกครั้งใช้ "firebase": "functions"

การบันทึกเส้นทาง คุณสามารถบันทึกการจับคู่เส้นทางลงในตัวแปรที่มีชื่อเพื่อใช้ ในโค้ดฟังก์ชัน (เช่น /user/{uid}, /user/{uid=*-us})

ค่าของตัวแปรการจับภาพจะอยู่ในออบเจ็กต์ database.DatabaseEvent.params ของฟังก์ชัน

การใช้สัญลักษณ์แทนของอินสแตนซ์ คุณยังระบุคอมโพเนนต์อินสแตนซ์ ได้โดยใช้ไวลด์การ์ด ไวลด์การ์ดอินสแตนซ์อาจมีคำนำหน้า คำต่อท้าย หรือทั้ง 2 อย่าง (เช่น my-app-*-prod)

การอ้างอิงไวลด์การ์ดและการจับภาพ

เมื่อใช้ Cloud Functions (รุ่นที่ 2) และ Realtime Database คุณจะใช้รูปแบบได้เมื่อระบุ ref และ instance อินเทอร์เฟซทริกเกอร์แต่ละรายการจะมีตัวเลือกต่อไปนี้ สำหรับการกำหนดขอบเขตฟังก์ชัน

การระบุ ref การระบุ instance พฤติกรรม
เดี่ยว (/foo/bar) ไม่ได้ระบุ กำหนดขอบเขตของตัวแฮนเดิลเป็นอินสแตนซ์ทั้งหมดในภูมิภาคของฟังก์ชัน
เดี่ยว (/foo/bar) เดี่ยว (‘my-new-db') ตัวแฮนเดิลขอบเขตไปยังอินสแตนซ์ที่เฉพาะเจาะจงในภูมิภาคของฟังก์ชัน
เดี่ยว (/foo/bar) รูปแบบ (‘inst-prefix*') ตัวแฮนเดิลขอบเขตไปยังอินสแตนซ์ทั้งหมดที่ตรงกับรูปแบบในภูมิภาคของฟังก์ชัน
รูปแบบ (/foo/{bar}) ไม่ได้ระบุ กำหนดขอบเขตของตัวแฮนเดิลเป็นอินสแตนซ์ทั้งหมดในภูมิภาคของฟังก์ชัน
รูปแบบ (/foo/{bar}) เดี่ยว (‘my-new-db') ตัวแฮนเดิลขอบเขตไปยังอินสแตนซ์ที่เฉพาะเจาะจงในภูมิภาคของฟังก์ชัน
รูปแบบ (/foo/{bar}) รูปแบบ (‘inst-prefix*') ตัวแฮนเดิลขอบเขตไปยังอินสแตนซ์ทั้งหมดที่ตรงกับรูปแบบในภูมิภาคของฟังก์ชัน

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

เมื่อRealtime Databaseทริกเกอร์เหตุการณ์ ระบบจะส่งออบเจ็กต์ Event ไปยังฟังก์ชันแฮนเดิล ออบเจ็กต์นี้มีพร็อพเพอร์ตี้ data ซึ่งสําหรับเหตุการณ์การสร้างและการลบ จะมีสแนปชอตของข้อมูลที่สร้างหรือลบ

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

Node.jsPython
// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
// for all databases in 'us-central1'
exports.makeuppercase = onValueCreated(
    "/messages/{pushId}/original",
    (event) => {
    // Grab the current value of what was written to the Realtime Database.
      const original = event.data.val();
      logger.log("Uppercasing", event.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing
      // asynchronous tasks inside a function, such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the
      // Realtime Database returns a Promise.
      return event.data.ref.parent.child("uppercase").set(uppercase);
    },
);
@db_fn.on_value_created(reference="/messages/{pushId}/original")
def makeuppercase(event: db_fn.Event[Any]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Grab the value that was written to the Realtime Database.
    original = event.data
    if not isinstance(original, str):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)

การอ่านค่าก่อนหน้า

สําหรับเหตุการณ์ write หรือ update พร็อพเพอร์ตี้ data คือออบเจ็กต์ Change ที่มีสแนปชอต 2 รายการ ซึ่งแสดงสถานะข้อมูลก่อนและหลังเหตุการณ์ที่ทริกเกอร์ ออบเจ็กต์ Change มีพร็อพเพอร์ตี้ before ที่ช่วยให้คุณตรวจสอบสิ่งที่บันทึกไว้ใน Realtime Database ก่อนเกิดเหตุการณ์ และพร็อพเพอร์ตี้ after ที่แสดงสถานะของข้อมูลหลังจากเกิดเหตุการณ์

เช่น คุณใช้พร็อพเพอร์ตี้ before เพื่อให้แน่ใจว่าฟังก์ชันจะเปลี่ยนข้อความเป็นตัวพิมพ์ใหญ่เฉพาะเมื่อสร้างข้อความเป็นครั้งแรกได้

Node.jsPython
  exports makeUppercase = onValueWritten("/messages/{pushId}/original", (event) => {
        // Only edit data when it is first created.
        if (event.data.before.exists()) {
          return null;
        }
        // Exit when the data is deleted.
        if (!event.data.after.exists()) {
          return null;
        }
        // Grab the current value of what was written to the Realtime Database.
        const original = event.data.after.val();
        console.log('Uppercasing', event.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 event.data.after.ref.parent.child('uppercase').set(uppercase);
      });
@db_fn.on_value_written(reference="/messages/{pushId}/original")
def makeuppercase2(event: db_fn.Event[db_fn.Change]) -> None:
    """Listens for new messages added to /messages/{pushId}/original and
    creates an uppercase version of the message to /messages/{pushId}/uppercase
    """

    # Only edit data when it is first created.
    if event.data.before is not None:
        return

    # Exit when the data is deleted.
    if event.data.after is None:
        return

    # Grab the value that was written to the Realtime Database.
    original = event.data.after
    if not hasattr(original, "upper"):
        print(f"Not a string: {event.reference}")
        return

    # Use the Admin SDK to set an "uppercase" sibling.
    print(f"Uppercasing {event.params['pushId']}: {original}")
    upper = original.upper()
    parent = db.reference(event.reference).parent
    if parent is None:
        print("Message can't be root node.")
        return
    parent.child("uppercase").set(upper)