Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang
Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.
Trang này mô tả cách sử dụng một Cloud Functions có thể gọi để xoá dữ liệu. Sau khi triển khai hàm này, bạn có thể gọi trực tiếp hàm này từ ứng dụng di động hoặc trang web của mình để xoá đệ quy các tài liệu và bộ sưu tập. Ví dụ: bạn có thể sử dụng giải pháp này để cấp cho một số người dùng quyền xoá toàn bộ bộ sưu tập.
Để biết các cách xoá bộ sưu tập khác, hãy xem phần Xoá dữ liệu.
Giải pháp: Xoá dữ liệu bằng Cloud Functions có thể gọi
Việc xoá toàn bộ bộ sưu tập khỏi một ứng dụng di động có giới hạn tài nguyên có thể khó triển khai vì những lý do sau:
Không có thao tác nào xoá một bộ sưu tập theo cách thức nguyên tử.
Khi bạn xoá một tài liệu, các tài liệu trong bộ sưu tập con của tài liệu đó sẽ không bị xoá.
Nếu tài liệu của bạn có các tập hợp con động, thì bạn khó có thể biết cần xoá dữ liệu nào cho một đường dẫn nhất định.
Để xoá một tập hợp gồm hơn 500 tài liệu, bạn cần thực hiện nhiều thao tác ghi theo lô hoặc hàng trăm thao tác xoá riêng lẻ.
Trong nhiều ứng dụng, việc cấp cho người dùng cuối quyền xoá toàn bộ các bộ sưu tập là không phù hợp.
Rất may là bạn có thể viết một Cloud Function có thể gọi để chạy các thao tác xoá an toàn và hiệu quả đối với toàn bộ các bộ sưu tập hoặc cây bộ sưu tập. Hàm Cloud bên dưới triển khai một hàm có thể gọi, tức là bạn có thể gọi trực tiếp hàm này từ ứng dụng di động hoặc trang web của mình như đối với một hàm cục bộ.
Để triển khai hàm và dùng thử bản minh hoạ, hãy xem mã mẫu.
Cloud Function
Hàm Cloud bên dưới sẽ xoá một bộ sưu tập và tất cả các phần tử con của bộ sưu tập đó.
Thay vì triển khai logic xoá đệ quy của riêng bạn cho Cloud Functions, bạn có thể tận dụng lệnh firestore:delete trong Giao diện dòng lệnh (CLI) của Firebase. Bạn có thể nhập mọi hàm của Firebase CLI vào ứng dụng Node.js bằng cách sử dụng gói firebase-tools.
Firebase CLI sử dụng REST API Cloud Firestore để tìm tất cả các tài liệu theo đường dẫn đã chỉ định và xoá từng tài liệu.
Việc triển khai này không yêu cầu bạn phải biết hệ thống phân cấp dữ liệu cụ thể của ứng dụng và thậm chí sẽ tìm và xoá các tài liệu "không có chủ" không còn có tài liệu mẹ nữa.
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)){thrownewfunctions.https.HttpsError('permission-denied','Must be an administrative user to initiate delete.');}constpath=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'.awaitfirebase_tools.firestore.delete(path,{project:process.env.GCLOUD_PROJECT,recursive:true,force:true,token:functions.config().fb.token});return{path:path};});
Để gọi hàm, hãy lấy một tham chiếu đến hàm từ Firebase SDK và truyền các tham số bắt buộc:
Web
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */functiondeleteAtPath(path){vardeleteFn=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);});}
Lưu ý: Sản phẩm này không có trên các mục tiêu watchOS và App Clip.
// Snippet not yet written
Objective-C
Lưu ý: Sản phẩm này không có trên các mục tiêu watchOS và App Clip.
// Snippet not yet written
Kotlin
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */fundeleteAtPath(path:String){valdeleteFn=Firebase.functions.getHttpsCallable("recursiveDelete")deleteFn.call(hashMapOf("path"topath)).addOnSuccessListener{// Delete Success// ...}.addOnFailureListener{// Delete Failed// ...}}
/** * Call the 'recursiveDelete' callable function with a path to initiate * a server-side delete. */publicvoiddeleteAtPath(Stringpath){Map<String,Object>data=newHashMap<>();data.put("path",path);HttpsCallableReferencedeleteFn=FirebaseFunctions.getInstance().getHttpsCallable("recursiveDelete");deleteFn.call(data).addOnSuccessListener(newOnSuccessListener<HttpsCallableResult>(){@OverridepublicvoidonSuccess(HttpsCallableResulthttpsCallableResult){// Delete Success// ...}}).addOnFailureListener(newOnFailureListener(){@OverridepublicvoidonFailure(@NonNullExceptione){// Delete failed// ...}});}
Bằng cách sử dụng SDK ứng dụng cho các hàm đám mây có thể gọi, trạng thái xác thực của người dùng và tham số path sẽ được truyền liền mạch đến hàm từ xa.
Khi hàm hoàn tất, ứng dụng sẽ nhận được một lệnh gọi lại có kết quả hoặc một ngoại lệ. Để tìm hiểu cách gọi một hàm trên đám mây từ Android, Apple hoặc một nền tảng khác, hãy đọc tài liệu.
Các điểm hạn chế
Giải pháp nêu trên minh hoạ cách xoá các bộ sưu tập khỏi một hàm có thể gọi, nhưng bạn cần lưu ý những hạn chế sau:
Tính nhất quán – đoạn mã trên sẽ xoá từng tài liệu một. Nếu bạn truy vấn trong khi đang có một thao tác xoá diễn ra, thì kết quả có thể phản ánh trạng thái hoàn thành một phần, trong đó chỉ một số tài liệu mục tiêu bị xoá.
Ngoài ra, không có gì đảm bảo rằng các thao tác xoá sẽ thành công hoặc thất bại một cách đồng nhất, vì vậy, hãy chuẩn bị sẵn sàng để xử lý các trường hợp xoá một phần.
Thời gian chờ – hàm ở trên được định cấu hình để chạy tối đa 540 giây trước khi hết thời gian chờ. Trong trường hợp tốt nhất, mã xoá có thể xoá 4.000 tài liệu mỗi giây. Nếu cần xoá hơn 2.000.000 tài liệu, bạn nên cân nhắc chạy thao tác này trên máy chủ của riêng mình để thao tác không bị hết thời gian chờ. Để xem ví dụ về cách xoá một bộ sưu tập khỏi máy chủ của riêng bạn, hãy xem phần Xoá bộ sưu tập.
Việc xoá một số lượng lớn tài liệu có thể khiến trình xem dữ liệu trong Bảng điều khiển Google Cloud tải chậm hoặc trả về lỗi hết thời gian chờ.
[[["Dễ hiểu","easyToUnderstand","thumb-up"],["Giúp tôi giải quyết được vấn đề","solvedMyProblem","thumb-up"],["Khác","otherUp","thumb-up"]],[["Thiếu thông tin tôi cần","missingTheInformationINeed","thumb-down"],["Quá phức tạp/quá nhiều bước","tooComplicatedTooManySteps","thumb-down"],["Đã lỗi thời","outOfDate","thumb-down"],["Vấn đề về bản dịch","translationIssue","thumb-down"],["Vấn đề về mẫu/mã","samplesCodeIssue","thumb-down"],["Khác","otherDown","thumb-down"]],["Cập nhật lần gần đây nhất: 2025-09-05 UTC."],[],[],null,["\u003cbr /\u003e\n\nThe page describes how to use a callable Cloud Function\nto delete data. Once you deploy this function,\nyou can call it directly from your mobile app or website to\nrecursively delete documents and collections. For example, you can use this\nsolution to give select users the ability to delete entire collections.\n\nFor other ways to delete collections, see [Delete data](../manage-data/delete-data#collections).\n\nSolution: Delete data with a callable Cloud Function\n\nDeleting entire collections from a resource-limited mobile app can be difficult\nto implement for the following reasons:\n\n- There is no operation that atomically deletes a collection.\n- Deleting a document does not delete the documents in its subcollections.\n- If your documents have dynamic subcollections, it can be hard to know what data to delete for a given path.\n- Deleting a collection of more than 500 documents requires multiple batched write operations or hundreds of single deletes.\n- In many apps, it isn't appropriate to give end-users permission to delete entire collections.\n\nFortunately, you can write a [callable Cloud Function](https://firebase.google.com/docs/functions/callable)\nto run safe and performant deletes of entire collections or collection trees. The Cloud Function below implements a [callable function](https://firebase.google.com/docs/functions/callable)\nwhich means it can be called directly from your mobile app or website as you\nwould for a local function.\n\nTo deploy the function and try a demo, see the [sample code](https://github.com/firebase/snippets-node/tree/master/firestore/solution-deletes).\n\nCloud Function\n\nThe Cloud Function below deletes a collection and all of its descendants.\n\nInstead of implementing your own recursive delete logic for your Cloud Function,\nyou can take advantage of the `firestore:delete` command in the\nFirebase Command Line Interface (CLI). You can import any function of the\nFirebase CLI into your Node.js application using the `firebase-tools` package.\n| **Note:** Deleting data with the Firebase CLI incurs read and delete costs. For more information, see [Pricing](https://firebase.google.com/docs/firestore/pricing).\n\nThe Firebase CLI uses the Cloud Firestore REST API\nto find all documents under the specified path and delete them individually.\nThis implementation requires no knowledge of your app's specific data hierarchy\nand will even find and delete \"orphaned\" documents that no longer have a\nparent. \n\nNode.js \n\n```javascript\n/**\n * Initiate a recursive delete of documents at a given path.\n * \n * The calling user must be authenticated and have the custom \"admin\" attribute\n * set to true on the auth token.\n * \n * This delete is NOT an atomic operation and it's possible\n * that it may fail after only deleting some documents.\n * \n * @param {string} data.path the document or collection path to delete.\n */\nexports.recursiveDelete = functions\n .runWith({\n timeoutSeconds: 540,\n memory: '2GB'\n })\n .https.onCall(async (data, context) =\u003e {\n // Only allow admin users to execute this function.\n if (!(context.auth && context.auth.token && context.auth.token.admin)) {\n throw new functions.https.HttpsError(\n 'permission-denied',\n 'Must be an administrative user to initiate delete.'\n );\n }\n\n const path = data.path;\n console.log(\n `User ${context.auth.uid} has requested to delete path ${path}`\n );\n\n // Run a recursive delete on the given document or collection path.\n // The 'token' must be set in the functions config, and can be generated\n // at the command line by running 'firebase login:ci'.\n await firebase_tools.firestore\n .delete(path, {\n project: process.env.GCLOUD_PROJECT,\n recursive: true,\n force: true,\n token: functions.config().fb.token\n });\n\n return {\n path: path \n };\n });https://github.com/firebase/snippets-node/blob/f1869eeb97c2bbb713aff3deb5a67666da7bcb6b/firestore/solution-deletes/functions/index.js#L29-L73\n```\n\nClient Invocation\n\nTo call the function, get a reference to the function from the Firebase SDK\nand pass the required parameters: \n\nWeb \n\n```javascript\n/**\n * Call the 'recursiveDelete' callable function with a path to initiate\n * a server-side delete.\n */\nfunction deleteAtPath(path) {\n var deleteFn = firebase.functions().httpsCallable('recursiveDelete');\n deleteFn({ path: path })\n .then(function(result) {\n logMessage('Delete success: ' + JSON.stringify(result));\n })\n .catch(function(err) {\n logMessage('Delete failed, see console,');\n console.warn(err);\n });\n}https://github.com/firebase/snippets-node/blob/f1869eeb97c2bbb713aff3deb5a67666da7bcb6b/firestore/solution-deletes/public/index.js#L4-L18\n```\n\nSwift \n**Note:** This product is not available on watchOS and App Clip targets. \n\n```swift\n // Snippet not yet written\n \n```\n\nObjective-C \n**Note:** This product is not available on watchOS and App Clip targets. \n\n```objective-c\n // Snippet not yet written\n \n```\n\nKotlin \n\n```kotlin\n/**\n * Call the 'recursiveDelete' callable function with a path to initiate\n * a server-side delete.\n */\nfun deleteAtPath(path: String) {\n val deleteFn = Firebase.functions.getHttpsCallable(\"recursiveDelete\")\n deleteFn.call(hashMapOf(\"path\" to path))\n .addOnSuccessListener {\n // Delete Success\n // ...\n }\n .addOnFailureListener {\n // Delete Failed\n // ...\n }\n}https://github.com/firebase/snippets-android/blob/b694d4dbd411d31be39655f47691c3e9f3529b03/firestore/app/src/main/java/com/google/example/firestore/kotlin/SolutionDeletes.kt#L9-L24\n```\n\nJava \n\n```java\n/**\n * Call the 'recursiveDelete' callable function with a path to initiate\n * a server-side delete.\n */\npublic void deleteAtPath(String path) {\n Map\u003cString, Object\u003e data = new HashMap\u003c\u003e();\n data.put(\"path\", path);\n\n HttpsCallableReference deleteFn =\n FirebaseFunctions.getInstance().getHttpsCallable(\"recursiveDelete\");\n deleteFn.call(data)\n .addOnSuccessListener(new OnSuccessListener\u003cHttpsCallableResult\u003e() {\n @Override\n public void onSuccess(HttpsCallableResult httpsCallableResult) {\n // Delete Success\n // ...\n }\n })\n .addOnFailureListener(new OnFailureListener() {\n @Override\n public void onFailure(@NonNull Exception e) {\n // Delete failed\n // ...\n }\n });\n}https://github.com/firebase/snippets-android/blob/b694d4dbd411d31be39655f47691c3e9f3529b03/firestore/app/src/main/java/com/google/example/firestore/SolutionDeletes.java#L17-L42\n```\n\nBy using the client SDK for callable cloud functions, the users's authentication\nstate and the `path` parameter are seamlessly passed to the remote function.\nWhen the function completes, the client will receive a callback with the\nresult or an exception. To learn about how to call a cloud function from\nAndroid, Apple, or another platform, read [the documentation](https://firebase.google.com/docs/functions/callable#call_the_function).\n| **Warning:** Callable functions are not secure by default! Make sure that your callable functions check the user's authorization before performing any sensitive actions like writing or deleting documents.\n\nLimitations\n\nThe solution shown above demonstrates deleting collections from a callable\nfunction, but you should be aware of the following limitations:\n\n- **Consistency** - the code above deletes documents one at a time. If you query while there is an ongoing delete operation, your results may reflect a partially complete state where only some targeted documents are deleted. There is also no guarantee that the delete operations will succeed or fail uniformly, so be prepared to handle cases of partial deletion.\n- **Timeouts** - the function above is configured to run for a maximum of 540 seconds before timing out. The deletion code can delete 4000 documents per second in the best case. If you need to delete more than 2,000,000 documents, you should consider running the operation on your own server so that it does not time out. For an example of how to delete a collection from your own server, see [Delete collections](../manage-data/delete-data#collections).\n- Deleting a large number of documents might cause the data viewer in the Google Cloud console to load slowly or to return a timeout error."]]