Help us understand the problem. What is going on with this article?

ローカル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で作ってます♪

参考にしたサイト

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away