باستخدام Cloud Functions، يمكنك معالجة الأحداث في Cloud Firestore بدون الحاجة إلى تعديل رمز العميل. يمكنك إجراء تغييرات على Cloud Firestore من خلال واجهة لقطة المستند أو من خلال حزمة تطوير البرامج (SDK) للمشرف.
في دورة حياة نموذجية، تنفّذ دالة Cloud Firestore ما يلي:
- تنتظر إجراء تغييرات على مستند معيّن.
- يتم تشغيلها عند وقوع حدث وتنفّذ مهامها.
- تتلقّى عنصر بيانات يحتوي على لقطة للبيانات المخزّنة في المستند المحدّد. بالنسبة إلى أحداث الكتابة أو التعديل، يحتوي عنصر البيانات على لقطتَين تمثّلان حالة البيانات قبل الحدث الذي أدّى إلى تشغيل الدالة وبعده.
يمكن أن تؤدي المسافة بين موقع مثيل 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
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.
إذا كان أحد المستندات في users يحتوي على مجموعات فرعية وتم تغيير حقل في أحد مستندات هذه المجموعات الفرعية، لن يتم تشغيل حرف البدل userId.
يتم استخراج تطابقات أحرف البدل من مسار المستند وتخزينها في event.params.
يمكنك تحديد أي عدد تريده من أحرف البدل لاستبدال أرقام تعريف مجموعات أو مستندات صريحة، على سبيل المثال:
Node.js
const {
onDocumentWritten,
Change,
FirestoreEvent
} = require('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
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
onDocumentCreatedWithAuthContextonDocumentWrittenWithAuthContextonDocumentDeletedWithAuthContextonDocumentUpdatedWithAuthContext
Python
on_document_created_with_auth_contexton_document_updated_with_auth_contexton_document_deleted_with_auth_contexton_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({
# ...
})
القيود
يُرجى ملاحظة القيود التالية لعوامل تشغيل Cloud Firestore في "وظائف السحابة الإلكترونية":
- Cloud Functions تتطلّب (الجيل الأول) قاعدة بيانات حالية باسم "(default)" في وضع Firestore الأصلي. لا تتوافق مع قواعد بيانات Cloud Firestore المُسمّاة أو وضع Datastore. يُرجى استخدام Cloud Functions (الجيل الثاني) لضبط الأحداث في مثل هذه الحالات.
- يُعدّ الإعداد على مستوى مشاريع متعددة باستخدام Cloud Functions وعامل تشغيل Cloud Firestore أحد القيود. لإعداد عامل تشغيل Cloud Firestore، يجب أن تكون Cloud Functions في المشروع نفسه.
- لا نضمن ترتيب الأحداث. يمكن أن تؤدي التغييرات السريعة إلى تشغيل استدعاءات الدوال بترتيب غير متوقّع.
- يتم تسليم الأحداث مرة واحدة على الأقل، ولكن قد يؤدي حدث واحد إلى استدعاءات متعددة للدوال. ننصحك بتجنُّب الاعتماد على آليات "مرة واحدة بالضبط" وكتابة دوال متطابقة.
- Cloud Firestore في وضع Datastore يتطلّب Cloud Functions (الجيل الثاني). Cloud Functions (الجيل الأول) لا يتوافق مع وضع Datastore.
- يرتبط عامل التشغيل بقاعدة بيانات واحدة. لا يمكنك إنشاء عامل تشغيل يطابق قواعد بيانات متعددة.
- لا يؤدي حذف قاعدة بيانات إلى حذف أي عوامل تشغيل لهذه قاعدة البيانات تلقائيًا. يتوقف عامل التشغيل عن تسليم الأحداث ولكنه يظل موجودًا إلى أن تحذف عامل التشغيل.
- إذا تجاوز حدث مطابق الحد الأقصى لحجم الطلب، قد لا يتم تسليم الحدث إلى Cloud Functions (الجيل الأول).
- يتم تسجيل الأحداث التي لم يتم تسليمها بسبب حجم الطلب في سجلّات النظام الأساسي ويتم احتسابها ضمن استخدام السجلّات للمشروع.
- يمكنك العثور على هذه السجلّات في "مستكشف السجلّات" مع الرسالة "لا يمكن تسليم الحدث إلى
وظيفة السحابة الإلكترونية بسبب تجاوز الحجم الحدّ الأقصى للجيل الأول..." من مستوى الخطورة
error. يمكنك العثور على اسم الدالة ضمن الحقلfunctionName. إذا كان الحقل thereceiveTimestampلا يزال ضمن ساعة من الآن، يمكنك استنتاج محتوى الحدث الفعلي من خلال قراءة المستند المعنيّ باستخدام لقطة قبل الطابع الزمني وبعده. - لتجنُّب معدّل خطوات الجري هذا، يمكنك إجراء ما يلي:
- نقل البيانات والترقية إلى Cloud Functions (الجيل الثاني)
- تقليل حجم المستند
- حذف Cloud Functions المعنيّة
- يمكنك إيقاف التسجيل نفسه باستخدام الاستثناءات ولكن يُرجى العِلم أنّه سيظل يتعذّر تسليم الأحداث المخالفة.