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

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

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

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

مشغِّلات وظائف Cloud Firestore

تُصدِّر حزمة تطوير البرامج (SDK) للوظائف في السحابة الإلكترونية لبرنامج Firebase حزمة تطوير البرامج (SDK) التالية في 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 بعد، يُرجى قراءة بدء استخدام وظائف السحابة الإلكترونية لبرنامج Firebase (الجيل الثاني) لضبط إعداد "وظائف السحابة الإلكترونية لبرنامج Firebase" وإعداده.

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

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

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

Node.js

import {
  onDocumentWritten,
  onDocumentCreated,
  onDocumentUpdated,
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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.

إذا كان مستند في users يحتوي على مجموعات فرعية وحقل في إحدى هذه المجموعات للمجموعات الفرعية' المستندات، لا يتم تشغيل حرف البدل userId.

يتم استخراج تطابقات أحرف البدل من مسار المستند وتخزينها في event.params. يمكنك تحديد العدد الذي تريده من أحرف البدل لاستبدال المجموعة الفاضحة. أو معرِّفات المستندات، مثل:

Node.js

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "firebase-functions/v2/firestore";

exports.myfunction = onDocumentWritten("users/{userId}/{messageCollectionId}/{messageId}", (event) => {
    // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then
    // event.params.userId == "marie";
    // event.params.messageCollectionId == "incoming_messages";
    // event.params.messageId == "134";
    // ... and ...
    // event.data.after.data() == {body: "Hello"}
});

Python (معاينة)

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

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

يجب أن يشير المشغِّل دائمًا إلى مستند، حتى إذا كنت تستخدم حرف بدل. على سبيل المثال، users/{userId}/{messageCollectionId} غير صالح بسبب {messageCollectionId}. عبارة عن مجموعة. في المقابل، هي users/{userId}/{messageCollectionId}/{messageId} صالحة لأن {messageId} سيشير دائمًا إلى مستند.

عوامل تشغيل الأحداث

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

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

Node.js

import {
  onDocumentCreated,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentUpdated,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentDeleted,
  Change,
  FirestoreEvent
} from "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

import {
  onDocumentWritten,
  Change,
  FirestoreEvent
} from "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 ...

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

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

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

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

عند تشغيل دالة، قد ترغب في الحصول على بيانات من مستند البيانات، أو الحصول على البيانات قبل التحديث. يمكنك الحصول على البيانات السابقة باستخدام 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

import { onDocumentUpdated } from "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 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

import { onDocumentWrittenWithAuthContext } from "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 في بيئة موثوق بها. وهي كحساب خدمة في مشروعك، ويمكنك إجراء عمليات قراءة باستخدام SDK لمشرف Firebase:

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({
      # ...
  })

القيود

لاحِظ القيود التالية على مشغّلات Cloud Firestore لوظائف السحابة الإلكترونية:

  • تتطلب ميزة Cloud Functions (الجيل الأول) توفُّر "(تلقائي)" حاليًا. قاعدة البيانات في وضع Firestore الأصلي. لا تتيح استخدام قواعد البيانات المعروفة أو وضع "مخزن البيانات" في Cloud Firestore. يُرجى استخدام وظائف السحابة الإلكترونية (الجيل الثاني) لضبط الأحداث في مثل هذه الحالات
  • الطلب غير مضمون. يمكن أن تؤدي التغييرات السريعة إلى استدعاء الدوال في طلب غير متوقع.
  • يتم تسليم الأحداث مرة واحدة على الأقل، ولكن قد يؤدي حدث واحد إلى استدعاءات الدوال المتعددة. تجنب الاعتماد على مرة واحدة بالضبط، واكتب الدوال المتكررة.
  • Cloud Firestore في وضع "تخزين البيانات" يتطلب Cloud Functions (الجيل الثاني). لا ينطبق ذلك على وظائف السحابة الإلكترونية (الجيل الأول) تدعم وضع تخزين البيانات.
  • يرتبط المشغل بقاعدة بيانات واحدة. لا يمكنك إنشاء مشغل يطابق قواعد بيانات متعددة.
  • لا يؤدي حذف قاعدة بيانات إلى حذف أي مشغلات لقاعدة البيانات هذه تلقائيًا. تشير رسالة الأشكال البيانية توقف عرض الأحداث، ولكنه سيظل موجودًا إلى أن تحذف عامل التفعيل.
  • إذا تجاوز حدث مطابق الحد الأقصى لحجم الطلب، سيتم قد لا يتم تسليم الحدث إلى Cloud Functions (الجيل الأول).
    • يتم تسجيل الدخول إلى سجلّات النظام الأساسي للأحداث التي لم يتم تسليمها بسبب حجم الطلب. وحسابها ضمن استخدام السجل للمشروع.
    • يمكنك العثور على هذه السجلّات في "مستكشف السجلات" مع الرسالة "تعذّر تسليم الحدث إلى دالة السحابة الإلكترونية لأنّ حجمها يتجاوز الحد الأقصى المسموح به للجيل الأول..." من error وشدة الخطأ. يمكنك العثور على اسم الدالة ضمن الحقل functionName. في حال حذف لا يزال الحقل receiveTimestamp خلال ساعة من الآن، يمكنك استنتاج محتوى الحدث الفعلي من خلال قراءة المستند المعني لقطة قبل وبعد الطابع الزمني.
    • لتجنُّب هذا الوتيرة، يمكنك إجراء ما يلي:
      • نقل البيانات والترقية إلى Cloud Functions (الجيل الثاني)
      • تصغير حجم المستند
      • حذف دوال السحابة الإلكترونية المعنيّة
    • يمكنك إيقاف التسجيل نفسه باستخدام الاستبعادات ولكن يُرجى العلم أنّه لن يتم تسليم الأحداث المخالفة.