با Cloud Functions، میتوانید رویدادها را در Cloud Firestore بدون نیاز به بهروزرسانی کد کارخواه مدیریت کنید. میتوانید تغییرات Cloud Firestore را ازطریق میانای عکس آنی سند یا ازطریق سرپرست SDK انجام دهید.
در یک چرخه حیات معمولی، تابع Cloud Firestore کارهای زیر را انجام میدهد:
- منتظر تغییرات در سند خاصی میماند.
- وقتی رویدادی رخ میدهد، راهانداز فعال میشود و تکالیفش را انجام میدهد.
- یک شیء داده دریافت میکند که حاوی عکس آنی از دادههای ذخیرهشده در سند مشخصشده است. برای رویدادهای نوشتن یا بهروزرسانی، شیء داده حاوی دو عکس آنی است که وضعیت داده را قبل و بعداز رویداد راهانداز نشان میدهد.
فاصله بین مکان نمونه Firestore و مکان تابع میتواند تأخیر شبکه قابلتوجهی ایجاد کند. برای بهینهسازی عملکرد، درصورت لزوم، مکان تابع را مشخص کنید.
راهاندازهای تابع Cloud Firestore
Cloud Functions for Firebase SDK راهاندازهای رویداد Cloud Firestore زیر را صادر میکند تا به شما امکان دهد کنترلکنندههایی را ایجاد کنید که به رویدادهای خاص Cloud Firestore مرتبط هستند:
Node.js
| نوع رویداد | رهانداز |
|---|---|
onDocumentCreated |
وقتی برای اولینبار در سند نوشته میشود، راهاندازی میشود. |
onDocumentUpdated |
وقتی راهاندازی میشود که سند ازقبل وجود داشته باشد و هر مقداری تغییر کرده باشد. |
onDocumentDeleted |
وقتی سندی حذف میشود، راهاندازی میشود. |
onDocumentWritten |
وقتی onDocumentCreated، onDocumentUpdated، یا onDocumentDeleted راهاندازی شود، راهاندازی میشود. |
onDocumentCreatedWithAuthContext |
onDocumentCreated با اطلاعات اصالتسنجی اضافی |
onDocumentWrittenWithAuthContext |
onDocumentWritten با اطلاعات اصالتسنجی اضافی |
onDocumentDeletedWithAuthContext |
onDocumentDeleted با اطلاعات اصالتسنجی اضافی |
onDocumentUpdatedWithAuthContext |
onDocumentUpdated با اطلاعات اصالتسنجی اضافی |
پایتون
| نوع رویداد | رهانداز |
|---|---|
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) => {
/* ... */
});
پایتون
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
});
پایتون
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"}
});
پایتون
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"}
});
پایتون
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 استفاده کنید.
پایتون
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 استفاده کنید.
پایتون
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 استفاده کنید.
پایتون
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 استفاده کنید.
پایتون
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();
});
پایتون
@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');
پایتون
# 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});
});
پایتون
@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
پایتون
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});
});
پایتون
@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
});
});
پایتون
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 یا حالت Datastore پشتیبانی نمیکند. لطفاً از Cloud Functions (نسل دوم) برای پیکربندی رویدادها در چنین مواردی استفاده کنید.
- راهاندازی پروژه متقاطع با راهانداز Cloud Functions و Cloud Firestore یک محدودیت است. برای راهاندازی Cloud Firestore، راهانداز Cloud Functions باید در همان پروژه باشد.
- سفارش تضمین نمیشود. تغییرات سریع میتواند باعث فراخوانی تابع به ترتیبی غیرمنتظره شود.
- رویدادها حداقل یکبار ارسال میشوند، اما یک رویداد ممکن است منجر به چندین فراخوانی تابع شود. از وابستگی به مکانیکهای دقیقاً یکبار اجتناب کنید و توابع همانی بنویسید.
- Cloud Firestore در حالت Datastore به Cloud Functions (نسل دوم) نیاز دارد. Cloud Functions (نسل اول) از حالت Datastore پشتیبانی نمیکند.
- راهانداز با یک پایگاه داده مرتبط است. نمیتوانید راهاندازی ایجاد کنید که با چند پایگاه داده مطابقت داشته باشد.
- حذف پایگاه داده بهطور خودکار راهاندازهای آن پایگاه داده را حذف نمیکند. راهانداز ارسال رویدادها را متوقف میکند اما تا زمانی که راهانداز را حذف نکنید همچنان وجود خواهد داشت.
- اگر رویداد مطابقتدادهشده از حداکثر اندازه درخواست فراتر رود، ممکن است رویداد به Cloud Functions (نسل اول) تحویل داده نشود.
- رویدادهایی که بهدلیل اندازه درخواست ارسال نشدهاند در گزارشهای پلاتفرم ثبت میشوند و در مصرف گزارش برای پروژه لحاظ میشوند.
- میتوانید این گزارشها را در «کاوشگر گزارشها» با پیام «رویداد بهدلیل فراتر رفتن اندازه از حد مجاز برای نسل اول نمیتواند به تابع Cloud ارسال شود...» با شدت
errorپیدا کنید. میتوانید نام تابع را در فیلدfunctionNameپیدا کنید. اگر فیلدreceiveTimestampهنوز در یک ساعت آینده است، میتوانید با خواندن سند موردنظر با عکس آنی قبل و بعداز مُهر زمان، محتوای واقعی رویداد را استنباط کنید. - برای جلوگیری از چنین ریتمی، میتوانید:
- انتقال دادن و ارتقا دادن به Cloud Functions (نسل دوم)
- کوچک کردن سند
- Cloud Functions موردنظر را حذف کنید
- میتوانید خود ثبت گزارش را بااستفاده از موارد استثنا خاموش کنید اما توجه داشته باشید که رویدادهای متخلف همچنان تحویل داده نخواهند شد.