בדף הזה מוסבר איך להשתמש ב-Cloud Function שאפשר להפעיל כדי למחוק נתונים. אחרי שמפעילים את הפונקציה הזו, אפשר לקרוא לה ישירות מאפליקציית מובייל או מאתר כדי למחוק מסמכים ואוספים באופן רקורסיבי. לדוגמה, אפשר להשתמש בפתרון הזה כדי לתת למשתמשים נבחרים את האפשרות למחוק אוספים שלמים.
דרכים נוספות למחוק אוספים מפורטות במאמר מחיקת נתונים.
פתרון: מחיקת נתונים באמצעות Cloud Function שניתן להפעלה
יכול להיות שיהיה קשה להטמיע מחיקה של אוספים שלמים באפליקציה לנייד עם משאבים מוגבלים, מהסיבות הבאות:
- אין פעולה שמוחקת אוסף באופן אטומי.
- כשמוחקים מסמך, המסמכים באוספי המשנה שלו לא נמחקים.
- אם במסמכים שלכם יש אוספי משנה דינמיים, יכול להיות שיהיה לכם קשה לדעת אילו נתונים למחוק בנתיב מסוים.
- כדי למחוק אוסף של יותר מ-500 מסמכים, צריך לבצע כמה פעולות כתיבה באצווה או מאות מחיקות בודדות.
- בהרבה אפליקציות, לא מתאים לתת למשתמשי קצה הרשאה למחוק אוספים שלמים.
למזלכם, אתם יכולים לכתוב פונקציית Cloud שאפשר להפעיל כדי למחוק בבטחה וביעילות קולקציות שלמות או עץ של קולקציות. הפונקציה הבאה של Cloud Functions מיישמת פונקציה שאפשר להפעיל, כלומר אפשר להפעיל אותה ישירות מאפליקציית מובייל או מאתר, כמו שמפעילים פונקציה מקומית.
כדי לפרוס את הפונקציה ולנסות הדגמה, אפשר לעיין בקוד לדוגמה.
Cloud Function
הפונקציה של Cloud Functions שמופיעה בהמשך מוחקת אוסף ואת כל צאצאיו.
במקום להטמיע לוגיקה משלכם למחיקה רקורסיבית של Cloud Function, אתם יכולים להשתמש בפקודה firestore:delete
בממשק שורת הפקודה (CLI) של Firebase. אתם יכולים לייבא כל פונקציה של Firebase CLI לאפליקציית Node.js באמצעות חבילת firebase-tools
.
ה-Firebase CLI משתמש ב-Cloud Firestore REST API כדי למצוא את כל המסמכים בנתיב שצוין ולמחוק אותם בנפרד. ההטמעה הזו לא דורשת ידע בהיררכיית הנתונים הספציפית של האפליקציה, והיא אפילו תמצא ותמחק מסמכים 'יתומים' שכבר אין להם מסמך אב.
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 של הלקוח לפונקציות בענן שאפשר להפעיל, מצב האימות של המשתמש והפרמטר path
מועברים בצורה חלקה לפונקציה המרוחקת.
כשהפונקציה מסתיימת, הלקוח יקבל קריאה חוזרת עם התוצאה או עם חריגה. במסמכי התיעוד מוסבר איך להתקשר לפונקציית Cloud מ-Android, מ-Apple או מפלטפורמה אחרת.
מגבלות
הפתרון שמוצג למעלה מדגים איך למחוק אוספים מפונקציה שאפשר לקרוא לה, אבל חשוב לשים לב למגבלות הבאות:
- עקביות – הקוד שלמעלה מוחק מסמכים אחד בכל פעם. אם תריצו שאילתה בזמן פעולת מחיקה, יכול להיות שהתוצאות ישקפו מצב של השלמה חלקית, שבו רק חלק מהמסמכים שהוגדרו למחיקה נמחקו. בנוסף, אין ערובה לכך שפעולות המחיקה יצליחו או ייכשלו באופן אחיד, ולכן צריך להיות מוכנים לטפל במקרים של מחיקה חלקית.
- פסק זמן (timeout) – הפונקציה שלמעלה מוגדרת לפעול למשך 540 שניות לכל היותר לפני שיופעל פסק זמן. קוד המחיקה יכול למחוק 4,000 מסמכים בשנייה במקרה הטוב. אם אתם צריכים למחוק יותר מ-2,000,000 מסמכים, כדאי להריץ את הפעולה בשרת שלכם כדי שלא יתרחש פסק זמן. דוגמה למחיקת אוסף מהשרת שלכם מופיעה במאמר בנושא מחיקת אוספים.
- מחיקה של מספר גדול של מסמכים עלולה לגרום לטעינה איטית של כלי הצגת הנתונים במסוף Google Cloud או להחזרת שגיאת זמן קצוב לתפוגה.