コンソールへ移動

コレクションとサブコレクションを削除する

Cloud Firestore でのデータの削除は、特にリソースが制限されるモバイルアプリからは、以下の理由により、正しく行えない場合があります。

  • コレクションをアトミックに削除するオペレーションが存在しない。
  • ドキュメントを削除しても、そのサブコレクション内のドキュメントは削除されない。
  • ドキュメントに動的なサブコレクションが存在する場合は、指定されたパスでどのデータを削除すればよいかを認識するのが困難になる可能性がある。
  • 500 を超えるドキュメントのコレクションを削除するには、複数回の書き込みバッチ オペレーションまたは数百回の単純削除が必要である。
  • 多くのアプリでは、エンドユーザーにコレクション全体を削除する権限を付与するのは適切ではない。

ただし、コレクション全体やコレクション ツリー全体を安全かつ効率的に削除する Cloud Function の記述は可能です。

続行する前に、Cloud Firestore のデータモデルに関する記事をお読みください。

解決策: 呼び出し可能な Cloud Function を使用してデータを削除する

このガイドでは、呼び出し可能な Cloud Function を使用してデータを削除する方法について説明します。この関数をデプロイすると、モバイルアプリやウェブサイトから直接呼び出して、ドキュメントやコレクションを再帰的に削除できます。

関数をデプロイしてデモを試すには、サンプルコードをご覧ください。

Cloud Function

以下の Cloud Function は、コレクションとその子孫すべてを削除します。

使用する Cloud Function 用に独自の再帰的な削除ロジックを実装する代わりに、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((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'.
    return firebase_tools.firestore
      .delete(path, {
        project: process.env.GCLOUD_PROJECT,
        recursive: true,
        yes: true,
        token: functions.config().fb.token
      })
      .then(() => {
        return {
          path: path
        };
      });
  });

上記の Cloud Function は、呼び出し可能関数として実装されています。つまり、ローカル関数の場合と同様に、モバイルアプリまたはウェブサイトから直接呼び出すことができます。

クライアントからの呼び出し

関数を呼び出すには、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);
        });
}

呼び出し可能な Cloud Function 向けのクライアント SDK を使用すると、ユーザーの認証状態と path パラメータがリモート関数にシームレスに渡されます。関数が完了すると、クライアントは結果または例外が含まれるコールバックを受け取ります。Android、iOS、または別のプラットフォームから Cloud Function を呼び出す方法については、こちらのドキュメントをご覧ください。

制限事項

上記の解決策では、呼び出し可能関数からコレクションを削除していますが、次の制限事項がありますのでご注意ください。

  • 整合性 - 上記のコードは、一度に複数のドキュメントを削除します。削除オペレーションの進行中にクエリを行うと、クエリ結果では、部分的に完了した状態、つまり対象のドキュメントの一部のみが削除された状態が反映される場合があります。削除オペレーションがいつも同じように成功または失敗するという保証もないため、部分的な削除が発生したケースを処理する準備が必要です。
  • タイムアウト - 上記の関数は、タイムアウトまでの最大実行時間が 540 秒に構成されています。削除コードは、最適な場合に毎秒 4,000 のドキュメントを削除できます。2,000,000 を超えるドキュメントを削除する必要がある場合は、オペレーションがタイムアウトしないように、独自のサーバーでオペレーションを実行することを検討してください。