使用 Callable Cloud Function 刪除數據

該頁面描述瞭如何使用可調用的雲函數來刪除數據。部署此功能後,您可以直接從您的移動應用程序或網站調用它以遞歸刪除文檔和集合。例如,您可以使用此解決方案讓選定用戶能夠刪除整個集合。

有關刪除集合的其他方法,請參閱刪除數據

解決方案:使用可調用的 Cloud Function 刪除數據

從資源有限的移動應用程序中刪除整個集合可能難以實施,原因如下:

  • 沒有原子地刪除集合的操作。
  • 刪除文檔不會刪除其子集合中的文檔。
  • 如果您的文檔具有動態子集合,則可能很難知道要刪除給定路徑的哪些數據。
  • 刪除超過 500 個文檔的集合需要多次批量寫入操作或數百次單次刪除。
  • 在許多應用程序中,授予最終用戶刪除整個集合的權限是不合適的。

幸運的是,您可以編寫一個可調用的雲函數來運行對整個集合或集合樹的安全且高性能的刪除。下面的雲函數實現了一個可調用函數,這意味著它可以像本地函數一樣直接從您的移動應用程序或網站調用。

要部署該功能並嘗試演示,請參閱示例代碼

雲功能

下面的 Cloud Function 刪除一個集合及其所有後代。

您可以利用 Firebase 命令行界面 (CLI) 中的firestore:delete命令,而不是為您的 Cloud Function 實現自己的遞歸刪除邏輯。您可以使用firebase-tools包將 Firebase CLI 的任何功能導入您的 Node.js 應用程序。

Firebase CLI 使用 Cloud Firestore REST API 來查找指定路徑下的所有文檔並單獨刪除它們。此實現不需要了解您的應用程序的特定數據層次結構,甚至會查找和刪除不再具有父級的“孤立”文檔。

節點.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,
        yes: 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);
        });
}
迅速
注意:此產品不適用於 watchOS 和 App Clip 目標。
    // Snippet not yet written
    
Objective-C
注意:此產品不適用於 watchOS 和 App Clip 目標。
    // Snippet not yet written
    

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
                    // ...
                }
            });
}

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
                // ...
            }
}

通過使用可調用雲函數的客戶端SDK,將用戶的認證狀態和path參數無縫傳遞給遠程函數。當函數完成時,客戶端將收到帶有結果或異常的回調。要了解如何從 Android、Apple 或其他平台調用雲函數,請閱讀文檔

限制

上面顯示的解決方案演示了從可調用函數中刪除集合,但您應該注意以下限制:

  • 一致性- 上面的代碼一次刪除一個文檔。如果您在進行刪除操作時進行查詢,您的結果可能會反映部分完成狀態,其中僅刪除了一些目標文檔。也不能保證刪除操作會統一成功或失敗,因此請準備好處理部分刪除的情況。
  • 超時- 上述功能配置為在超時前最多運行 540 秒。刪除代碼在最好的情況下每秒可以刪除 4000 個文檔。如果您需要刪除超過 2,000,000 個文檔,則應考慮在自己的服務器上運行該操作,以免超時。有關如何從您自己的服務器中刪除集合的示例,請參閱刪除集合