tag:crieit.net,2005:https://crieit.net/tags/firebase-admin/feed 「firebase-admin」の記事 - Crieit Crieitでタグ「firebase-admin」に投稿された最近の記事 2019-08-16T12:23:37+09:00 https://crieit.net/tags/firebase-admin/feed tag:crieit.net,2005:PublicArticle/15326 2019-08-16T12:23:37+09:00 2019-08-16T12:23:37+09:00 https://crieit.net/posts/PC-firebase-admin ローカルPCでfirebase-adminを使って一括削除/一括更新する <p>最近、Nuxt.js+Firebaseで作った積読を解消するWebアプリ「<a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a>」をリリースしました!!</p> <p>そのときに、Firestoreのスキーマを一括変更したり、<br /> 不要なデータを一括削除したいと思ったときの備忘録。</p> <p>ローカルPCでfirebase-adminを使うときの初期設定とかはこちらを参照<br /> - <a target="_blank" rel="nofollow noopener" href="https://www.memory-lovers.blog/entry/2019/07/25/073000">ローカルPCからfirebase-adminを使ってFirestoreを操作する(管理ツール) - くらげになりたい。</a></p> <h4 id="注意点"><a href="#%E6%B3%A8%E6%84%8F%E7%82%B9">注意点</a></h4> <p>Firestoreでは、1度に実行できる更新処理の数に制限がある。</p> <blockquote> <p>一括書き込みでは最大 500 件のオペレーションを実行できます</p> </blockquote> <p><a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/firestore/manage-data/transactions">トランザクションと一括書き込み  |  Firebase</a></p> <p>そのため、以下にも記載されている通り、<br /> 100件ごとなど、小さなバッチに分けて実行する必要がある。</p> <blockquote> <p>メモリ不足エラーを避けるため、小さなバッチに分けてドキュメントを削除することをおすすめします。コレクション全体またはサブコレクションが削除されるまで、このプロセスを繰り返します。</p> </blockquote> <p><a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/firestore/manage-data/delete-data#collections">Cloud Firestore からデータを削除する  |  Firebase</a></p> <h3 id="小さなバッチに分けて実行するソース"><a href="#%E5%B0%8F%E3%81%95%E3%81%AA%E3%83%90%E3%83%83%E3%83%81%E3%81%AB%E5%88%86%E3%81%91%E3%81%A6%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%82%BD%E3%83%BC%E3%82%B9">小さなバッチに分けて実行するソース</a></h3> <p>全体としてはこんな感じ。</p> <p>登場する3つ</p> <ol> <li><code>main()</code> ... メイン関数。実行したときに呼ばれる関数</li> <li><code>deleteAllUsers()</code> ... ユーザを一括削除する関数。mainから呼ばれる</li> <li><code>executeBatch()</code> ... 指定したバッチサイズで再帰的に実行する関数。deleteAllUsers()から呼ばれる</li> </ol> <p>なお、コレクション<code>users</code>のなかにあるドキュメントには、<br /> 作成日時のタイムスタンプ<code>createAt</code>のドキュメントフィールドがある想定です。</p> <pre><code class="javascript">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(); </code></pre> <p>他の処理でも共通的に使えるように<code>executeBatch()</code>では、<br /> <code>queryFunc</code>と<code>executeFunc</code>の2つの関数を引数で受け取れるようにしている。</p> <p>なので他の処理などを追加するときは、<code>deleteAllUsers()</code>みたいなのを増やしていけばOK!!</p> <p>これで一括削除やスキーマの更新もだいぶ楽に...(<em>´ω`</em>)</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p>Webサービスは運用しはじめると色々大変なので、管理ツールなども大事...<br /> 最近リリースしたこちらのアプリでも使ってます♪</p> <p>■積んでる本の総額がわかる読書管理サービス<br /> <a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a><br /> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/2c9872d0-101b-88aa-af2a-5bb907d09e28.png" width="20%"/></p> <p>総額がわかると少しは積ん読してる本を読もうと思ったり♪<br /> 積読総額ランキングや人気の本などもあるので、よかったらあそんでもらえれば!!</p> <p>積読ハウマッチは、Nuxt.js+Firebase+ZEIT Nowで作ってます♪</p> <h1 id="参考にしたサイト"><a href="#%E5%8F%82%E8%80%83%E3%81%AB%E3%81%97%E3%81%9F%E3%82%B5%E3%82%A4%E3%83%88">参考にしたサイト</a></h1> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/firestore/manage-data/delete-data#collections">Cloud Firestore からデータを削除する  |  Firebase</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://note.mu/shogoyamada/n/n04d80a8d284f">Firestoreにテストデータを流す|shogo yamada|note</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://gist.github.com/mono0926/78c3d3e078323d566b46929c7540b15b">Firestoreの特定のコレクションのドキュメントを全削除するスクリプト(TypeScript版)</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://medium.com/google-cloud-jp/firestore-backup-67327a74cd54">Cloud Firestoreのバックアップ・リストア - google-cloud-jp - Medium</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://www.memory-lovers.blog/entry/2019/07/25/073000">ローカルPCからfirebase-adminを使ってFirestoreを操作する(管理ツール) - くらげになりたい。</a></li> </ul> きらぷか@積読ハウマッチ/SSSAPIなど