ローカルPCでfirebase-adminを使って一括削除/一括更新する

最近、Nuxt.js+Firebaseで作った積読を解消するWebアプリ「積読ハウマッチ」をリリースしました!!

そのときに、Firestoreのスキーマを一括変更したり、
不要なデータを一括削除したいと思ったときの備忘録。

ローカルPCでfirebase-adminを使うときの初期設定とかはこちらを参照
- ローカルPCからfirebase-adminを使ってFirestoreを操作する(管理ツール) - くらげになりたい。

注意点

Firestoreでは、1度に実行できる更新処理の数に制限がある。

一括書き込みでは最大 500 件のオペレーションを実行できます

トランザクションと一括書き込み  |  Firebase

そのため、以下にも記載されている通り、
100件ごとなど、小さなバッチに分けて実行する必要がある。

メモリ不足エラーを避けるため、小さなバッチに分けてドキュメントを削除することをおすすめします。コレクション全体またはサブコレクションが削除されるまで、このプロセスを繰り返します。

Cloud Firestore からデータを削除する  |  Firebase

小さなバッチに分けて実行するソース

全体としてはこんな感じ。

登場する3つ

  1. main() ... メイン関数。実行したときに呼ばれる関数
  2. deleteAllUsers() ... ユーザを一括削除する関数。mainから呼ばれる
  3. executeBatch() ... 指定したバッチサイズで再帰的に実行する関数。deleteAllUsers()から呼ばれる

なお、コレクションusersのなかにあるドキュメントには、
作成日時のタイムスタンプcreateAtのドキュメントフィールドがある想定です。

const admin = require("firebase-admin");

// 配置したサービスアカウントの秘密鍵を取得
const serviceAccount = require("./key/XXXXX.json");

// firebase-adminを初期化
admin.initializeApp({ credential: admin.credential.cert(serviceAccount) });

// firestoreのインスタンスを取得
const db = admin.firestore();


/**
 * 一括実行処理の共通関数
 * @param {FirebaseFirestore.Firestore} db firestoreのインスタンス
 * @param {Number} limit 1回に実行するサイズ
 * @param {Function} queryFunc ドキュメントを検索するクエリを作成する関数。firestore.Queryを返す
 * @param {Function} executeFunc 削除や更新などを実行する関数。
 * @param {Object|undefined} last 検索でヒットした最後の要素。バッチサイズで繰り返す際に利用s
 */
async function executeBatch(db, limit, queryFunc, executeFunc, last) {

  // queryFuncを使って対象のドキュメントを取得
  const query = queryFunc(db, last);
  const items = await query.limit(limit).get();

  // ドキュメントが1つも見つからなければ、終了
  if (items.size === 0) return;

  // executeFuncを使ってbatchに削除/更新処理を追加&コミット
  const batch = db.batch();
  for (let i = 0; i < items.size; i++) {
    executeFunc(db, batch, items.docs[i]);
  }
  await batch.commit();

  // リストの最後の要素を取得して、再帰実行
  const lastItem = items.docs[items.size - 1].data();
  return await executeBatch(db, limit, queryFunc, executeFunc, lastItem);
}


/**
 * すべてのユーザを削除
 */
async function deleteAllUsers(db, limit) {
 // 検索部分の関数: 作成日時で昇順ソートして取得
 const queryFunc = (db, last) => {
    let query = db.collection("users").orderBy("createAt", "asc");
    if (!!last) query = query.startAfter(last.createAt);
    return query;
  };

  // 実行部分の関数: itemで該当のドキュメントを受け取るので削除
  const executeFunc = (db, batch, item) => {
    const docRef = db.collection("users").doc(item.id);
    batch.delete(docRef);
  };

  return await executeBatch(db, limit, queryFunc, executeFunc, undefined);
}

// ****************************
// * MAIN
// ****************************
async function main() {
  console.log(`***** START MAIN *****`);
  // バッチサイズを100件で実行
  const limit = 100;
  await deleteAllUsers(db, limit)
  console.log(`***** END   MAIN *****`);
}

main().then();

他の処理でも共通的に使えるようにexecuteBatch()では、
queryFuncexecuteFuncの2つの関数を引数で受け取れるようにしている。

なので他の処理などを追加するときは、deleteAllUsers()みたいなのを増やしていけばOK!!

これで一括削除やスキーマの更新もだいぶ楽に...(´ω`)

おわりに

Webサービスは運用しはじめると色々大変なので、管理ツールなども大事...
最近リリースしたこちらのアプリでも使ってます♪

■積んでる本の総額がわかる読書管理サービス
積読ハウマッチ

総額がわかると少しは積ん読してる本を読もうと思ったり♪
積読総額ランキングや人気の本などもあるので、よかったらあそんでもらえれば!!

積読ハウマッチは、Nuxt.js+Firebase+ZEIT Nowで作ってます♪

参考にしたサイト

Originally published at qiita.com

きらぷか@i18n補助ツール『トランスノート』開発者

フリーエンジニア/今はNuxt.js/いつかFlutter 受託&アプリ/Webサービス/ゲームを #個人開発 CS修士→SIer/R&D→フリー #paiza はAランクで満足/AtCoderしたい 仕事依頼やご相談はDMまで Kotlin/Python/Swift/Unity/Java/Haskell/DDD

Crieitは個人で開発中です。 興味がある方は是非記事の投稿をお願いします! どんな軽い内容でも嬉しいです。
なぜCrieitを作ろうと思ったか

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください!

ボードとは?

関連記事

コメント