בדף הזה מוסבר איך להשתמש בפונקציה של Cloud Functions שניתן להפעיל כדי למחוק נתונים. אחרי שפורסים את הפונקציה הזו, אפשר לקרוא לה ישירות מהאפליקציה לנייד או מהאתר כדי למחוק מסמכים ואוספים באופן רקורסיבי. לדוגמה, אתם יכולים להשתמש בפתרון הזה כדי לתת למשתמשים נבחרים את היכולת למחוק אוספים שלמים.
במאמר מחיקת נתונים מפורטות דרכים נוספות למחיקת אוספים.
פתרון: מחיקת נתונים באמצעות פונקציה של Cloud Functions שניתן להפעיל
יכול להיות שיהיה קשה למחוק אוספים שלמים מאפליקציה לנייד עם משאבים מוגבלים, מהסיבות הבאות:
- אין פעולה שמוחקת אוסף באופן אטומי.
- מחיקת מסמך לא מוחקת את המסמכים באוספי המשנה שלו.
- אם למסמכים יש אוספי משנה דינמיים, יכול להיות שיהיה קשה לדעת אילו נתונים צריך למחוק בנתיב נתון.
- כדי למחוק אוסף של יותר מ-500 מסמכים, צריך לבצע כמה פעולות כתיבה באצווה או מאות פעולות מחיקה בודדות.
- באפליקציות רבות, לא מתאים לתת למשתמשי קצה הרשאת משתמש למחוק אוספים שלמים.
למרבה המזל, אתם יכולים לכתוב פונקציה של Cloud Functions שניתן להפעיל כדי להריץ מחיקות בטוחות ויעילות של אוספים שלמים או של עצי אוספים. הפונקציה של Cloud Functions שבהמשך מטמיעה פונקציה שניתן להפעיל, כלומר אפשר להפעיל אותה ישירות מהאפליקציה לנייד או מהאתר, כמו פונקציה מקומית.
כדי לפרוס את הפונקציה ולנסות הדגמה, עיינו בקוד לדוגמה.
Cloud Function
הפונקציה של Cloud Functions שבהמשך מוחקת אוסף ואת כל הצאצאים שלו.
במקום להטמיע לוגיקה משלכם למחיקה רקורסיבית בפונקציה של Cloud Functions, אתם יכולים להשתמש בפקודה firestore:delete בממשק שורת הפקודה (CLI) של Firebase. אתם יכולים לייבא כל פונקציה של Firebase CLI לאפליקציית Node.js באמצעות החבילה firebase-tools.
Firebase CLI משתמש ב-API בארכיטקטורת REST של Cloud Firestore כדי למצוא את כל המסמכים בנתיב שצוין ולמחוק אותם בנפרד. ההטמעה הזו לא דורשת ידע על היררכיית הנתונים הספציפית של האפליקציה, והיא אפילו תמצא ותמחק מסמכים "יתומים" שכבר אין להם הורה.
Node.js
/** * Initiate a recursive delete of documents at a given path. * * The calling user must be authenticated and have the custom "admin" attribute * set to true on the auth token. * * This delete is NOT an atomic operation and it's possible * that it may fail after only deleting some documents. * * @param {string} data.path the document or collection path to delete. */ exports.recursiveDelete = functions .runWith({ timeoutSeconds: 540, memory: '2GB' }) .https.onCall(async (data, context) => { // Only allow admin users to execute this function. if (!(context.auth && context.auth.token && context.auth.token.admin)) { throw new functions.https.HttpsError( 'permission-denied', 'Must be an administrative user to initiate delete.' ); } const path = data.path; console.log( `User ${context.auth.uid} has requested to delete path ${path}` ); // Run a recursive delete on the given document or collection path. // The 'token' must be set in the functions config, and can be generated // at the command line by running 'firebase login:ci'. await firebase_tools.firestore .delete(path, { project: process.env.GCLOUD_PROJECT, recursive: true, force: true, token: functions.config().fb.token }); return { path: path }; });
הפעלת לקוח
כדי להפעיל את הפונקציה, צריך לקבל הפניה לפונקציה מ-Firebase SDK ולהעביר את הפרמטרים הנדרשים:
אינטרנט
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */ function deleteAtPath(path) { var deleteFn = firebase.functions().httpsCallable('recursiveDelete'); deleteFn({ path: path }) .then(function(result) { logMessage('Delete success: ' + JSON.stringify(result)); }) .catch(function(err) { logMessage('Delete failed, see console,'); console.warn(err); }); }
Swift
// Snippet not yet written
Objective-C
// Snippet not yet written
Kotlin
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */ fun deleteAtPath(path: String) { val deleteFn = Firebase.functions.getHttpsCallable("recursiveDelete") deleteFn.call(hashMapOf("path" to path)) .addOnSuccessListener { // Delete Success // ... } .addOnFailureListener { // Delete Failed // ... } }
Java
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */ public void deleteAtPath(String path) { Map<String, Object> data = new HashMap<>(); data.put("path", path); HttpsCallableReference deleteFn = FirebaseFunctions.getInstance().getHttpsCallable("recursiveDelete"); deleteFn.call(data) .addOnSuccessListener(new OnSuccessListener<HttpsCallableResult>() { @Override public void onSuccess(HttpsCallableResult httpsCallableResult) { // Delete Success // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Delete failed // ... } }); }
כשמשתמשים ב-SDK של הלקוח לפונקציות שניתנות לקריאה ב-Cloud Functions, מצב האימות של המשתמש והפרמטר path מועברים בצורה חלקה לפונקציה המרוחקת.
כשהפונקציה מסתיימת, הלקוח מקבל קריאה חוזרת (callback) עם התוצאה או חריגה. במאמר הזה מוסבר איך קוראים לפונקציה של Cloud Functions מ-Android, מ-Apple או מפלטפורמה אחרת.
מגבלות
הפתרון שמוצג למעלה מדגים מחיקה של אוספים מפונקציה שניתן להפעיל, אבל חשוב לשים לב למגבלות הבאות:
- עקביות – הקוד שלמעלה מוחק מסמכים אחד אחרי השני. אם תשלחו שאילתה בזמן שפעולת המחיקה מתבצעת, יכול להיות שהתוצאות ישקפו מצב של השלמה חלקית, שבו רק חלק מהמסמכים המטורגטים נמחקו. בנוסף, אין ערובה לכך שפעולות המחיקה יצליחו או ייכשלו באופן אחיד, לכן צריך להיות מוכנים לטפל במקרים של מחיקה חלקית.
- זמן קצוב לתפוגה – הפונקציה שלמעלה מוגדרת לפעול למשך 540 שניות לכל היותר לפני שהזמן הקצוב לתפוגה יסתיים. קוד המחיקה יכול למחוק 4,000 מסמכים בשנייה במקרה הטוב. אם אתם צריכים למחוק יותר מ-2,000,000 מסמכים, כדאי להריץ את הפעולה בשרת שלכם כדי שלא יחול זמן קצוב לתפוגה. במאמר מחיקת אוספים מוסבר איך למחוק אוסף מהשרת שלכם.
- אם מוחקים מספר גדול של מסמכים, יכול להיות שצופה הנתונים במסוף Google Cloud ייטען לאט או שתופיע שגיאת זמן קצוב לתפוגה.