באמצעות Cloud Functions, אתם יכולים לפרוס קוד Node.js כדי לטפל באירועים שמופעלים על ידי שינויים במסד הנתונים של Cloud Firestore. כך תוכלו להוסיף בקלות פונקציונליות בצד השרת לאפליקציה, בלי להפעיל שרתים משלכם.
דוגמאות לתרחישים לדוגמה מופיעות במאמר מה אפשר לעשות עם Cloud Functions? או במאגר Functions Samples ב-GitHub.
טריגרים של פונקציות 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() עם תו כללי לחיפוש. הפונקציה לדוגמה הזו קוראת ל-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 ... });
קריאה וכתיבה של נתונים
כשפונקציה מופעלת, היא מספקת תמונת מצב של הנתונים שקשורים לאירוע. אתם יכולים להשתמש בתמונת המצב הזו כדי לקרוא מהמסמך שהפעיל את האירוע או לכתוב אליו, או להשתמש ב-SDK של Firebase לאדמינים כדי לגשת לחלקים אחרים במסד הנתונים.
נתוני אירוע
נתוני הקריאה
כשפונקציה מופעלת, יכול להיות שתרצו לקבל נתונים ממסמך שעודכן, או לקבל את הנתונים לפני העדכון. כדי לקבל את הנתונים הקודמים, אפשר להשתמש ב-change.before.data(), שמכיל את תמונת המצב של המסמך לפני העדכון.
באופן דומה, 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 (דור ראשון), צריך מסד נתונים קיים מסוג (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 (דור ראשון).
- אירועים שלא נמסרו בגלל גודל הבקשה מתועדים ביומני הפלטפורמה ונכללים בשימוש ביומן של הפרויקט.
- היומנים האלה מופיעים ב-Logs Explorer עם ההודעה "Event cannot deliver to Cloud function due to size exceeding the limit for דור ראשון..." ברמת החומרה
error. שם הפונקציה מופיע בשדהfunctionName. אם השדהreceiveTimestampעדיין נמצא בטווח של שעה מעכשיו, אפשר להסיק מהו תוכן האירוע בפועל על ידי קריאת המסמך הרלוונטי עם תמונת מצב לפני חותמת הזמן ואחריה. - כדי למנוע מצב כזה, אתם יכולים:
- מעבר ושדרוג ל-Cloud Functions (דור שני)
- הקטנת המסמך
- מוחקים את ה-Cloud Functions הרלוונטי.
- אפשר להשבית את הרישום עצמו באמצעות החרגות, אבל שימו לב שהאירועים הבעייתיים עדיין לא יועברו.