באמצעות Cloud Functions אפשר לטפל באירועים ב-Cloud Firestore בלי לעדכן את קוד הלקוח. אפשר לבצע שינויים ב-Cloud Firestore דרך הממשק של קובץ snapshot של מסמך או דרך Admin SDK.
במחזור החיים הרגיל, פונקציית Cloud Firestore מבצעת את הפעולות הבאות:
- המתנה לשינויים במסמך מסוים.
- מופעל כשמתרחש אירוע ומבצע את המשימות שלו.
- מקבל אובייקט נתונים שמכיל קובץ snapshot של הנתונים שמאוחסנים במסמך שצוין. באירועי כתיבה או עדכון, אובייקט הנתונים מכיל שתי קובצי snapshot שמייצגים את מצב הנתונים לפני ואחרי האירוע שהפעיל אותם.
המרחק בין המיקום של מכונה של Firestore לבין המיקום של הפונקציה יכול לגרום לזמן אחזור ארוך ברשת. כדי לשפר את הביצועים, מומלץ לציין את מיקום הפונקציה במקרים הרלוונטיים.
טריגרים של פונקציות ב-Cloud Firestore
ה-SDK של Cloud Functions for Firebase מייצא אובייקט functions.firestore
שמאפשר ליצור מנהלים שמקושרים לאירועים ספציפיים ב-Cloud Firestore.
סוג האירוע | הפעלה |
---|---|
onCreate |
מופעל כשמסמך נכתב בפעם הראשונה. |
onUpdate |
מופעל כשמסמך כבר קיים וערך כלשהו השתנה בו. |
onDelete |
מופעל כשמסמך עם נתונים נמחק. |
onWrite |
מופעל כשהאירוע onCreate , onUpdate או onDelete מופעל. |
אם עדיין לא הפעלתם פרויקט ב-Cloud Functions for Firebase, תוכלו לקרוא את המאמר תחילת העבודה: כתיבת הפונקציות הראשונות ופריסתן כדי להגדיר את הפרויקט ב-Cloud Functions for Firebase.
כתיבת פונקציות שמופעל בהן טריגר של Cloud Firestore
הגדרת טריגר לפונקציה
כדי להגדיר טריגר ב-Cloud Firestore, מציינים נתיב למסמך וסוג אירוע:
Node.js
const functions = require('firebase-functions');
exports.myFunction = functions.firestore
.document('my-collection/{docId}')
.onWrite((change, context) => { /* ... */ });
נתיבי מסמכים יכולים להפנות למסמך ספציפי או לתבנית של תו כללי לחיפוש.
ציון מסמך יחיד
אם רוצים להפעיל אירוע בעקבות שינוי כלשהו במסמך ספציפי, אפשר להשתמש בפונקציה הבאה.
Node.js
// Listen for any change on document `marie` in collection `users` exports.myFunctionName = functions.firestore .document('users/marie').onWrite((change, context) => { // ... Your code here });
ציון קבוצה של מסמכים באמצעות תווים כלליים לחיפוש
אם רוצים לצרף טריגר לקבוצת מסמכים, למשל כל מסמך בקולקציה מסוימת, צריך להשתמש ב-{wildcard}
במקום במזהה המסמך:
Node.js
// Listen for changes in all documents in the 'users' collection exports.useWildcard = functions.firestore .document('users/{userId}') .onWrite((change, context) => { // If we set `/users/marie` to {name: "Marie"} then // context.params.userId == "marie" // ... and ... // change.after.data() == {name: "Marie"} });
בדוגמה הזו, כששדה כלשהו במסמך כלשהו ב-users
משתנה, הוא תואם למשתנה אסימון בשם userId
.
אם מסמך ב-users
כולל אוספי משנה, אבל משתנה שדה באחד מהמסמכים של אוספי המשנה האלה, התו הכללי לחיפוש userId
לא מופעל.
התאמות של תווים כלליים לחיפוש מופקות מנתיב המסמך ונשמרות ב-context.params
.
אפשר להגדיר כמה תווים כלליים לחיפוש שרוצים כדי להחליף מזהי אוספים או מסמכים מפורשים, לדוגמה:
Node.js
// Listen for changes in all documents in the 'users' collection and all subcollections exports.useMultipleWildcards = functions.firestore .document('users/{userId}/{messageCollectionId}/{messageId}') .onWrite((change, context) => { // If we set `/users/marie/incoming_messages/134` to {body: "Hello"} then // context.params.userId == "marie"; // context.params.messageCollectionId == "incoming_messages"; // context.params.messageId == "134"; // ... and ... // change.after.data() == {body: "Hello"} });
טריגרים של אירועים
הפעלת פונקציה כשיוצרים מסמך חדש
אפשר להשתמש ב-handler של onCreate()
עם תו כללי לחיפוש כדי להפעיל פונקציה בכל פעם שמסמך חדש נוצר באוסף.
הפונקציה לדוגמה הזו מפעילה את הפונקציה createUser
בכל פעם שמתווסף פרופיל משתמש חדש:
Node.js
exports.createUser = functions.firestore .document('users/{userId}') .onCreate((snap, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = snap.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
הפעלת פונקציה כשמסמך מתעדכן
אפשר גם להפעיל פונקציה שתופעל כשמסמך מתעדכן, באמצעות הפונקציה onUpdate()
עם תו כללי לחיפוש. הפונקציה לדוגמה הזו קורא לפונקציה updateUser
אם משתמש משנה את הפרופיל שלו:
Node.js
exports.updateUser = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the document // e.g. {'name': 'Marie', 'age': 66} const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); // access a particular field as you would any JS property const name = newValue.name; // perform desired operations ... });
הפעלת פונקציה כשמסמך נמחק
אפשר גם להפעיל פונקציה כשמסמך נמחק באמצעות הפונקציה onDelete()
עם תו כללי לחיפוש. הפונקציה לדוגמה קורא ל-deleteUser
כשמשתמש מחק את פרופיל המשתמש שלו:
Node.js
exports.deleteUser = functions.firestore .document('users/{userID}') .onDelete((snap, context) => { // Get an object representing the document prior to deletion // e.g. {'name': 'Marie', 'age': 66} const deletedValue = snap.data(); // perform desired operations ... });
הפעלת פונקציה לכל השינויים במסמך
אם אתם לא מתעניינים בסוג האירוע שמופעל, אתם יכולים להאזין לכל השינויים במסמך ב-Cloud Firestore באמצעות הפונקציה onWrite()
עם תו Wildcard. הפונקציה לדוגמה הזו מפעילה את modifyUser
אם משתמש נוצר, מתעדכן או נמחק:
Node.js
exports.modifyUser = functions.firestore .document('users/{userID}') .onWrite((change, context) => { // Get an object with the current document value. // If the document does not exist, it has been deleted. const document = change.after.exists ? change.after.data() : null; // Get an object with the previous document value (for update or delete) const oldDocument = change.before.data(); // perform desired operations ... });
קריאה וכתיבה של נתונים
כשפונקציה מופעלת, היא מספקת תמונת מצב של הנתונים שקשורים לאירוע. אפשר להשתמש בתמונת המצב הזו כדי לקרוא מהמסמך שהפעיל את האירוע או לכתוב בו, או להשתמש ב-Firebase Admin SDK כדי לגשת לחלקים אחרים של מסד הנתונים.
נתוני אירוע
קריאת נתונים
כשפונקציה מופעלת, יכול להיות שתרצו לקבל נתונים ממסמך שעודכן, או לקבל את הנתונים לפני העדכון. אפשר לקבל את הנתונים הקודמים באמצעות change.before.data()
, שמכיל את קובץ snapshot של המסמך לפני העדכון.
באופן דומה, המשתנה change.after.data()
מכיל את המצב של תמונת המצב של המסמך אחרי העדכון.
Node.js
exports.updateUser2 = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Get an object representing the current document const newValue = change.after.data(); // ...or the previous value before this update const previousValue = change.before.data(); });
אפשר לגשת למאפיינים כמו בכל אובייקט אחר. לחלופין, אפשר להשתמש בפונקציה get
כדי לגשת לשדות ספציפיים:
Node.js
// Fetch data using standard accessors const age = snap.data().age; const name = snap.data()['name']; // Fetch data using built in accessor const experience = snap.get('experience');
כתיבת נתונים
כל קריאה לפונקציה משויכת למסמך ספציפי במסד הנתונים של Cloud Firestore. תוכלו לגשת למסמך הזה בתור DocumentReference
במאפיין ref
של קובץ ה-snapshot שמוחזר לפונקציה.
הערך DocumentReference
מגיע מ-Cloud Firestore Node.js SDK, וכולל שיטות כמו update()
, set()
ו-remove()
, שמאפשרות לשנות בקלות את המסמך שהפעיל את הפונקציה.
Node.js
// Listen for updates to any `user` document. exports.countNameChanges = functions.firestore .document('users/{userId}') .onUpdate((change, context) => { // Retrieve the current and previous value const data = change.after.data(); const previousData = change.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 change.after.ref.set({ name_change_count: count + 1 }, {merge: true}); });
נתונים מחוץ לאירוע ההפעלה
Cloud Functions מופעל בסביבה מהימנה, כלומר הוא מורשה כחשבון שירות בפרויקט שלכם. אפשר לבצע פעולות קריאה וכתיבה באמצעות SDK של Firebase לאדמינים:
Node.js
const admin = require('firebase-admin');
admin.initializeApp();
const db = admin.firestore();
exports.writeToFirestore = functions.firestore
.document('some/doc')
.onWrite((change, context) => {
db.doc('some/otherdoc').set({ ... });
});
מגבלות
חשוב לשים לב למגבלות הבאות לגבי טריגרים של Cloud Firestore ל-Cloud Functions:
- Cloud Functions (דור ראשון) נדרשת למסד נתונים קיים מסוג '(ברירת מחדל)' במצב מקורי של Firestore. אין תמיכה במסדי נתונים בעלי שם Cloud Firestore או במצב Datastore. צריך להשתמש ב-Cloud Functions (דור שני) כדי להגדיר אירועים במקרים כאלה.
- לא מובטח שההזמנה תבוצע. שינויים מהירים יכולים להפעיל קריאות לפונקציות בסדר לא צפוי.
- אירועים מועברים לפחות פעם אחת, אבל אירוע אחד עלול לגרום להפעלות של כמה פונקציות. צריך להימנע משימוש במכונה פעם אחת בדיוק, ולכתוב פונקציות אידמפוטנטיות.
- כדי להשתמש ב-Cloud Firestore במצב Datastore נדרש Cloud Functions (דור שני). ב-Cloud Functions (דור ראשון) אין תמיכה במצב Datastore.
- טריגר משויך למסד נתונים אחד. לא ניתן ליצור טריגר שתואמת למספר מסדי נתונים.
- מחיקת מסד נתונים לא מובילה למחיקה אוטומטית של טריגרים של אותו מסד נתונים. הטריגר יפסיק לשלוח אירועים, אבל הוא ימשיך להתקיים עד שתמחקו אותו.
- אם אירוע תואם חורג מגודל הבקשה המקסימלי, יכול להיות שהאירוע לא יישלח אל Cloud Functions (דור ראשון).
- אירועים שלא נשלחו בגלל גודל הבקשה מתועדים ביומני הפלטפורמה ומספרם נספר בשימוש היומן של הפרויקט.
- היומנים האלה מופיעים ב-Logs Explorer עם ההודעה "Event cannot deliver to
Cloud function due to size exceeding the limit for 1st gen..." ברמת חומרה
error
. שם הפונקציה מופיע בשדהfunctionName
. אם השדהreceiveTimestamp
עדיין נמצא בטווח של שעה מעכשיו, תוכלו להסיק מה תוכן האירוע עצמו על ידי קריאת המסמך הרלוונטי עם צילום מסך לפני ואחרי חותמת הזמן. - כדי להימנע מקצב כזה, אתם יכולים:
- העברה ושדרוג ל-Cloud Functions (דור שני)
- הקטנת המסמך
- מוחקים את ה-Cloud Functions הרלוונטי
- אפשר להשבית את הרישום ביומן באמצעות החרגות, אבל חשוב לזכור שהאירועים הבעייתיים עדיין לא יישלחו.