توسيع نطاق Cloud Firestore باستخدام وظائف Cloud (الجيل الثاني)

باستخدام Cloud Functions، يمكنك نشر رمز لمعالجة الأحداث التي يتم تشغيلها بسبب التغييرات في قاعدة بيانات Cloud Firestore. يتيح لك ذلك إضافة وظائف من جهة الخادم إلى تطبيقك بدون تشغيل الخوادم الخاصة بك.

Cloud Functions (الجيل الثاني)

تمنحك خدمة Cloud Functions for Firebase (الجيل الثاني)، المستندة إلى Cloud Run و Eventarc، بنية تحتية أكثر فعالية وتحكّمًا متقدّمًا في الأداء وقابلية التوسّع، بالإضافة إلى تحكّم أكبر في وقت تشغيل الدوال. لمزيد من المعلومات عن الجيل الثاني، يُرجى الاطّلاع على وظائف Firebase السحابية (الجيل الثاني).

مشغّلات دوال Cloud Firestore

تصدِّر حزمة تطوير البرامج (SDK) الخاصة بخدمة Cloud Functions for Firebase مشغّلات أحداث Cloud Firestore التالية لتتيح لك إنشاء معالِجات مرتبطة بأحداث Cloud Firestore معيّنة:

Node.js

نوع الحدث مشغّل
onDocumentCreated يتم تشغيله عند كتابة مستند للمرة الأولى.
onDocumentUpdated يتم تشغيله عندما يكون المستند موجودًا وتتغيّر أي قيمة فيه.
onDocumentDeleted يتم تشغيله عند حذف مستند.
onDocumentWritten يتم تشغيله عند تشغيل onDocumentCreated أو onDocumentUpdated أو onDocumentDeleted.
onDocumentCreatedWithAuthContext onDocumentCreated مع معلومات مصادقة إضافية
onDocumentWrittenWithAuthContext onDocumentWritten مع معلومات مصادقة إضافية
onDocumentDeletedWithAuthContext onDocumentDeleted مع معلومات مصادقة إضافية
onDocumentUpdatedWithAuthContext onDocumentUpdated مع معلومات مصادقة إضافية

Python

نوع الحدث مشغّل
on_document_created يتم تشغيله عند كتابة مستند للمرة الأولى.
on_document_updated يتم تشغيله عندما يكون المستند موجودًا وتتغيّر أي قيمة فيه.
on_document_deleted يتم تشغيله عند حذف مستند.
on_document_written يتم تشغيله عند تشغيل on_document_created أو on_document_updated أو on_document_deleted.
on_document_created_with_auth_context on_document_created مع معلومات مصادقة إضافية
on_document_updated_with_auth_context on_document_updated مع معلومات مصادقة إضافية
on_document_deleted_with_auth_context on_document_deleted مع معلومات مصادقة إضافية
on_document_written_with_auth_context on_document_written مع معلومات مصادقة إضافية

لا يتم تشغيل أحداث Cloud Firestore إلا عند إجراء تغييرات على المستند. لا يؤدي تعديل مستند Cloud Firestore بدون تغيير البيانات (عملية كتابة بدون تغيير) إلى إنشاء حدث تعديل أو كتابة. لا يمكن إضافة أحداث إلى حقول معيّنة.

إذا لم يكن لديك مشروع مفعَّل لخدمة Cloud Functions for Firebase بعد، يُرجى قراءة مقالة البدء في استخدام Cloud Functions for Firebase (الجيل الثاني) لضبط مشروعك على Cloud Functions for Firebase وإعداده.

كتابة الدوال التي يتم تشغيلها بواسطة Cloud Firestore

تحديد مشغّل دالة

لتحديد مشغّل Cloud Firestore، حدِّد مسار مستند ونوع حدث:

Node.js

const {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("my-collection/{docId}", (event) => {
   /* ... */ 
});

Python

from firebase_functions.firestore_fn import (
  on_document_created,
  on_document_deleted,
  on_document_updated,
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:

يمكن أن تشير مسارات المستندات إلى مستند معيّن أو إلى نمط حرف بدل.

تحديد مستند واحد

إذا أردت تشغيل حدث عند إجراء أي تغيير على مستند معيّن، يمكنك استخدام الدالة التالية.

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/marie", (event) => {
  // Your code here
});

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/marie")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

تحديد مجموعة من المستندات باستخدام أحرف البدل

إذا أردت إرفاق مشغّل بمجموعة من المستندات، مثل أي مستند في مجموعة معيّنة، استخدِم {wildcard} بدلاً من رقم تعريف المستند:

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.myfunction = onDocumentWritten("users/{userId}", (event) => {
  // If we set `/users/marie` to {name: "Marie"} then
  // event.params.userId == "marie"
  // ... and ...
  // event.data.after.data() == {name: "Marie"}
});

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # If we set `/users/marie` to {name: "Marie"} then
  event.params["userId"] == "marie"  # True
  # ... and ...
  event.data.after.to_dict() == {"name": "Marie"}  # True

في هذا المثال، عند تغيير أي حقل في أي مستند في users، يتطابق ذلك مع حرف بدل يُسمى userId.

يتم استخراج تطابقات أحرف البدل من مسار المستند وتخزينها في event.params. يجب أن يشير المشغّل دائمًا إلى مستند، حتى إذا كنت تستخدم حرف بدل.

مشغّلات الأحداث

تشغيل دالة عند إنشاء مستند جديد

يمكنك تشغيل دالة في أي وقت يتم فيه إنشاء مستند جديد في مجموعة. يتم تشغيل دالة المثال هذه في كل مرة تتم فيها إضافة ملف شخصي جديد لمستخدم:

Node.js

const {
  onDocumentCreated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.createuser = onDocumentCreated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snapshot = event.data;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // access a particular field as you would any JS property
    const name = data.name;

    // perform more operations ...
});

للحصول على معلومات مصادقة إضافية، استخدِم onDocumentCreatedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_created,
  Event,
  DocumentSnapshot,
)

@on_document_created(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

تشغيل دالة عند تعديل مستند

يمكنك أيضًا تشغيل دالة عند تعديل مستند. يتم تشغيل دالة المثال هذه إذا غيّر مستخدم ملفه الشخصي:

Node.js

const {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.updateuser = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const newValue = event.data.after.data();

    // access a particular field as you would any JS property
    const name = newValue.name;

    // perform more operations ...
});

للحصول على معلومات مصادقة إضافية، استخدِم onDocumentUpdatedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_updated,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get a dictionary representing the document
  # e.g. {'name': 'Marie', 'age': 66}
  new_value = event.data.after.to_dict()

  # Access a particular field as you would any dictionary
  name = new_value["name"]

  # Perform more operations ...

تشغيل دالة عند حذف مستند

يمكنك أيضًا تشغيل دالة عند حذف مستند. يتم تشغيل دالة المثال هذه عندما يحذف مستخدم ملفه الشخصي:

Node.js

const {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.deleteuser = onDocumentDeleted("users/{userId}", (event) => {
    // Get an object representing the document
    // e.g. {'name': 'Marie', 'age': 66}
    const snap =  event.data;
    const data =  snap.data();

    // perform more operations ...
});

للحصول على معلومات مصادقة إضافية، استخدِم onDocumentDeletedWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_deleted,
  Event,
  DocumentSnapshot,
)

@on_document_deleted(document="users/{userId}")
def myfunction(event: Event[DocumentSnapshot|None]) -> None:
  # Perform more operations ...

تشغيل دالة لجميع التغييرات التي يتم إجراؤها على مستند

إذا لم يكن نوع الحدث الذي يتم تشغيله مهمًا بالنسبة إليك، يمكنك الاستماع إلى جميع التغييرات في مستند Cloud Firestoreباستخدام مشغّل حدث "تمت كتابة المستند" يتم تشغيل دالة المثال هذه إذا تم إنشاء مستخدم أو تعديله أو حذفه:

Node.js

const {
  onDocumentWritten,
  Change,
  FirestoreEvent
} = require('firebase-functions/v2/firestore');

exports.modifyuser = onDocumentWritten("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const document =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();

    // perform more operations ...
});

للحصول على معلومات مصادقة إضافية، استخدِم onDocumentWrittenWithAuthContext.

Python

from firebase_functions.firestore_fn import (
  on_document_written,
  Event,
  Change,
  DocumentSnapshot,
)

@on_document_written(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  # Get an object with the current document values.
  # If the document does not exist, it was deleted.
  document = (event.data.after.to_dict()
              if event.data.after is not None else None)

  # Get an object with the previous document values.
  # If the document does not exist, it was newly created.
  previous_values = (event.data.before.to_dict()
                     if event.data.before is not None else None)

  # Perform more operations ...

قراءة البيانات وكتابتها

عند تشغيل دالة، فإنّها توفّر لقطة من البيانات المرتبطة بالحدث. يمكنك استخدام هذه اللقطة للقراءة من المستند الذي شغّل الحدث أو الكتابة فيه، أو استخدام حزمة Firebase Admin SDK للوصول إلى أجزاء أخرى من قاعدة البيانات.

بيانات الأحداث

قراءة البيانات

عند تشغيل دالة، قد تريد الحصول على بيانات من مستند تم تعديله، أو الحصول على البيانات قبل التعديل. يمكنك الحصول على البيانات السابقة باستخدام event.data.before، التي تحتوي على لقطة المستند قبل التعديل. وبالمثل، تحتوي event.data.after على حالة لقطة المستند بعد التعديل.

Node.js

exports.updateuser2 = onDocumentUpdated("users/{userId}", (event) => {
    // Get an object with the current document values.
    // If the document does not exist, it was deleted
    const newValues =  event.data.after.data();

    // Get an object with the previous document values
    const previousValues =  event.data.before.data();
});

Python

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get an object with the current document values.
  new_value = event.data.after.to_dict()

  # Get an object with the previous document values.
  prev_value = event.data.before.to_dict()

يمكنك الوصول إلى الخصائص كما تفعل في أي عنصر آخر. بدلاً من ذلك، يمكنك استخدام الدالة get للوصول إلى حقول معيّنة:

Node.js

// Fetch data using standard accessors
const age = event.data.after.data().age;
const name = event.data.after.data()['name'];

// Fetch data using built in accessor
const experience = event.data.after.data.get('experience');

Python

# Get the value of a single document field.
age = event.data.after.get("age")

# Convert the document to a dictionary.
age = event.data.after.to_dict()["age"]

كتابة البيانات

يرتبط كل استدعاء دالة بمستند معيّن في قاعدة بياناتك Cloud Firestore. يمكنك الوصول إلى هذا المستند في اللقطة التي يتم عرضها على الدالة.

يتضمّن مرجع المستند طرقًا مثل update() وset() وremove() حتى تتمكّن من تعديل المستند الذي شغّل الدالة.

Node.js

const {onDocumentUpdated} = require('firebase-functions/v2/firestore');

exports.countnamechanges = onDocumentUpdated('users/{userId}', (event) => {
  // Retrieve the current and previous value
  const data = event.data.after.data();
  const previousData = event.data.before.data();

  // We'll only update if the name has changed.
  // This is crucial to prevent infinite loops.
  if (data.name == previousData.name) {
    return null;
  }

  // Retrieve the current count of name changes
  let count = data.name_change_count;
  if (!count) {
    count = 0;
  }

  // Then return a promise of a set operation to update the count
  return event.data.after.ref.set({
    name_change_count: count + 1
  }, {merge: true});

});

Python

@on_document_updated(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:
  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # We'll only update if the name has changed.
  # This is crucial to prevent infinite loops.
  if new_value.get("name") == prev_value.get("name"):
      return

  # Retrieve the current count of name changes
  count = new_value.to_dict().get("name_change_count", 0)

  # Update the count
  new_value.reference.update({"name_change_count": count + 1})

الوصول إلى معلومات مصادقة المستخدم

إذا كنت تستخدم أحد أنواع الأحداث التالية، يمكنك الوصول إلى معلومات مصادقة المستخدم حول الجهة الرئيسية التي شغّلت الحدث. تُضاف هذه المعلومات إلى المعلومات التي يتم عرضها في الحدث الأساسي.

Node.js

  • onDocumentCreatedWithAuthContext
  • onDocumentWrittenWithAuthContext
  • onDocumentDeletedWithAuthContext
  • onDocumentUpdatedWithAuthContext

Python

  • on_document_created_with_auth_context
  • on_document_updated_with_auth_context
  • on_document_deleted_with_auth_context
  • on_document_written_with_auth_context

للحصول على معلومات عن البيانات المتاحة في سياق المصادقة، يُرجى الاطّلاع على سياق المصادقة. يوضّح المثال التالي كيفية استرداد معلومات المصادقة:

Node.js

const {onDocumentWrittenWithAuthContext} = require('firebase-functions/v2/firestore');

exports.syncUser = onDocumentWrittenWithAuthContext("users/{userId}", (event) => {
    const snapshot = event.data.after;
    if (!snapshot) {
        console.log("No data associated with the event");
        return;
    }
    const data = snapshot.data();

    // retrieve auth context from event
    const { authType, authId } = event;

    let verified = false;
    if (authType === "system") {
      // system-generated users are automatically verified
      verified = true;
    } else if (authType === "unknown" || authType === "unauthenticated") {
      // admin users from a specific domain are verified
      if (authId.endsWith("@example.com")) {
        verified = true;
      }
    }

    return data.after.ref.set({
        created_by: authId,
        verified,
    }, {merge: true}); 
}); 

Python

@on_document_updated_with_auth_context(document="users/{userId}")
def myfunction(event: Event[Change[DocumentSnapshot]]) -> None:

  # Get the current and previous document values.
  new_value = event.data.after
  prev_value = event.data.before

  # Get the auth context from the event
  user_auth_type = event.auth_type
  user_auth_id = event.auth_id

البيانات خارج حدث المشغّل

Cloud Functions يتم تنفيذ في بيئة موثوق بها. يتم منحها إذن الوصول بصفتها حساب خدمة في مشروعك، ويمكنك إجراء عمليات القراءة والكتابة باستخدام حزمة Firebase Admin SDK:

Node.js

const { initializeApp } = require('firebase-admin/app');
const { getFirestore, Timestamp, FieldValue } = require('firebase-admin/firestore');

initializeApp();
const db = getFirestore();

exports.writetofirestore = onDocumentWritten("some/doc", (event) => {
    db.doc('some/otherdoc').set({ ... });
  });

  exports.writetofirestore = onDocumentWritten('users/{userId}', (event) => {
    db.doc('some/otherdoc').set({
      // Update otherdoc
    });
  });

Python

from firebase_admin import firestore, initialize_app
import google.cloud.firestore

initialize_app()

@on_document_written(document="some/doc")
def myfunction(event: Event[Change[DocumentSnapshot | None]]) -> None:
  firestore_client: google.cloud.firestore.Client = firestore.client()
  firestore_client.document("another/doc").set({
      # ...
  })

القيود

  • لا نضمن الترتيب. يمكن أن تؤدي التغييرات السريعة إلى تشغيل استدعاءات الدوال بترتيب غير متوقّع.
  • يتم تسليم الأحداث مرة واحدة على الأقل، ولكن قد يؤدي حدث واحد إلى استدعاءات متعددة للدوال. تجنَّب الاعتماد على آليات "مرة واحدة بالضبط"، واكتب دوالاً غير متغيرة.
  • يرتبط المشغّل بقاعدة بيانات واحدة. لا يمكنك إنشاء مشغّل يتطابق مع قواعد بيانات متعددة.
  • لا يؤدي حذف قاعدة بيانات إلى حذف أي مشغّلات لهذه قاعدة البيانات تلقائيًا. يتوقف المشغّل عن تسليم الأحداث ولكنه يظل موجودًا إلى أن تحذفه .
  • لا يتم تضمين المستندات التي يزيد حجمها عن 10 ميغابايت في حمولة الحدث. لاسترداد المستند الذي تم وضع إصدار له، استخدِم ا وقت قراءة الاسترداد في نقطة زمنية.