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

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

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

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

مشغِّلات الدوال 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

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:

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