Firestore 全てのドキュメントに対して、一気にフィールドを追加する方法(Cloud Functions)


全てのusersドキュメントに対してフィールドを追加したい!

私が運営しているアプリではFirestoreを使っている。
新機能の追加にあたって、usersに入っているドキュメント全てに対してisLockedというフィールドを追加したくなった。

イメージはこんなん

Firestoreのコンソール画面からフィールドを追加することはできるが、それだと1つ1つ全てのユーザーに対して手動で操作しないといけない・・・

ユーザーが1000人いたら、1000回Firestoreのコンソール画面で「フィールドを追加→フィールド名入力→タイプを選択→値を入力」を繰り返さないといけない!

そんなんしたら、手がイカれちまうわ!

FirestoreではphpMyadminのように、一気にデータ追加ができない

Web系の人なら多く人が触った事あるであろうphpMyadminくん
こいつと同じように、DBの管理画面(画像参照)から「カラム追加」みたいにできないものか?と色々と調べたが、どうやらできないようである・・・

ではどうするか?

Batch処理でFirestoreにデータを書き込む

Batch処理って何?という方は、まず下記の記事を読むことをお勧めする。
Firestore のバッチ処理とトランザクション処理

ざっくり説明すると
・トランザクション処理→「読み込み」も「書き込み」もどっちもOK
・バッチ処理→「書き込み」しかできないが、その分処理が早い

みたいな感じ(違うかったらごめんなさい・・)

前提

・Cloud Functionsの環境が既にある
もしまだの人はを参考に
Firebase で Cloud Functions を簡単にはじめよう

functionを作る

コードは完全に下記の記事を真似しました。ありがとうございました!
FirestoreでBatch処理

index.ts
import * as functions from 'firebase-functions';
import * as admin from 'firebase-admin';
admin.initializeApp();
const db = admin.firestore();

// // Start writing Firebase Functions
// // https://firebase.google.com/docs/functions/typescript

exports.setLocked = functions.https.onRequest(async (req, res) => {
  res.send('関数発動したよ!');

  let batch = db.batch();

  const snapshots = await db.collection('users').get();

  snapshots.docs.map((doc, index) => {
    if ((index + 1) % 500 === 0) {
      batch.commit();
      batch = db.batch();
    }

    batch.set(doc.ref, { isLocked: false }, { merge: true });
  });
  batch.commit();
});

注意点として、setメソッドのオプションで{ merge: true }をつけないと、全userドキュメントのフィールドがisLocked: falseだけになってしまうので、気をつけましょう

デプロイしてhttpリクエストのURLを開く

functionsetLockedを指定してデプロイ

firebase deploy --only functions:setLocked

発行されたURLにアクセスすると完了!

最後に

ちなみに、私が運営しているアプリではユーザー数が1000以上ありましたが、数分で反映されていました。