本页面介绍如何使用 Callable Cloud Functions 函数删除数据。部署此函数后,您可以直接从移动应用或网站对其进行调用,以递归方式删除文档和集合。例如,您可以使用此解决方案为部分用户授予删除整个集合的权限。
如需了解删除集合的其他方法,请参阅删除数据。
解决方案:使用 Callable Cloud Functions 函数删除数据
从资源有限的移动应用中完整删除集合可能很难实现,原因如下:
- 没有以原子方式删除集合的操作。
- 删除文档不会删除其子集合中的文档。
- 如果文档有动态子集合,就很难确定要删除给定路径中的哪些数据。
- 如需删除包含超过 500 个文档的集合,需要执行多次批量写入操作,或数百次单独的删除操作。
- 在很多应用中,向最终用户授予删除整个集合的权限是不合适的。
幸运的是,您可以编写 Callable Cloud Functions 函数,安全高效地删除整个集合或集合树。以下 Cloud Functions 函数实现了一个 Callable 函数,这意味着您可以直接在移动应用或网站中调用该函数,就像调用本地函数一样。
如需部署函数并查看演示,请参阅示例代码。
Cloud Functions 函数
下面的 Cloud Functions 函数能够删除集合及其所有后代。
您无需自行实现 Cloud Functions 函数的递归式删除逻辑,可以直接利用 Firebase 命令行界面 (CLI) 的 firestore:delete
命令。您可以使用 firebase-tools
软件包将任何 Firebase CLI 函数导入自己的 Node.js 应用。
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 获取对函数的引用,并传递必需的参数:
Web
/** * 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+KTX
/** * 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
参数会无缝传递给远程函数。函数执行完以后,客户端将收到包含结果或异常的回调。如需了解如何从 Android、Apple 或其他平台调用 Cloud Functions 函数,请参阅相关文档。
限制
上述解决方案演示了如何通过 Callable 函数删除集合,不过您应该注意以下限制:
- 一致性 - 上述代码一次删除一个文档。如果您在删除操作进行时执行查询,最终可能导致部分完成的状态,即只有部分目标文档删除成功。 另外,删除操作无法保证全部删除或全部不删除,因此请做好准备应对仅部分文档删除成功的情况。
- 超时 - 根据配置,上述函数最多运行 540 秒,超出即会超时。用于执行删除操作的代码每秒钟最多可以删除 4000 个文档。如果您需要删除超过 200 万个文档,应该考虑在您自己的服务器上运行此操作,以免超时。如需通过示例了解如何从您自己的服务器中删除集合,请参阅删除集合。