tag:crieit.net,2005:https://crieit.net/tags/firebase-tools/feed 「firebase-tools」の記事 - Crieit Crieitでタグ「firebase-tools」に投稿された最近の記事 2019-09-26T20:05:46+09:00 https://crieit.net/tags/firebase-tools/feed tag:crieit.net,2005:PublicArticle/15426 2019-09-26T19:11:16+09:00 2019-09-26T20:05:46+09:00 https://crieit.net/posts/firebase-admin 複数のプロジェクトのfirebase-adminを起動して、開発用に本番データの一部を移行する <p>開発している<a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">Webサービス</a>で、本番データの一部を開発用に使いたいと思ったときの備忘録。<br /> firebase-adminは複数のプロジェクトで使えるらしい。</p> <p>開発用だけじゃなく、UIDを変更しながらのデータ移行などにも使えそう。</p> <h2 id="ローカル環境でfirebase-adminを使うための準備"><a href="#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E7%92%B0%E5%A2%83%E3%81%A7firebase-admin%E3%82%92%E4%BD%BF%E3%81%86%E3%81%9F%E3%82%81%E3%81%AE%E6%BA%96%E5%82%99">ローカル環境でfirebase-adminを使うための準備</a></h2> <p>準備の部分は、以前まとめたこちらを参照ください〜<br /> - <a target="_blank" rel="nofollow noopener" href="https://www.memory-lovers.blog/entry/2019/07/25/073000">ローカルPCからfirebase-adminを使ってFirestoreを操作する(管理ツール) - くらげになりたい。</a></p> <h3 id="ソースはこんな感じ。"><a href="#%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AF%E3%81%93%E3%82%93%E3%81%AA%E6%84%9F%E3%81%98%E3%80%82">ソースはこんな感じ。</a></h3> <pre><code class="javascript">const admin = require("firebase-admin"); // *** 移行元のプロジェクトの設定 const srcSA = require("./key/XXXXX.json"); // サービスアカウントの秘密鍵を取得 // firebase-adminの初期化 admin.initializeApp({ credential: admin.credential.cert(srcSA) }); const srcDB = admin.firestore(); // firestoreのインスタンスを取得 // ** 移行先のプロジェクトの設定 const destSA = require("./key/XXXXX.json"); // サービスアカウントの秘密鍵を取得 // ※同じadminを使って、別のAppを作成しないといけない※ // ※2つ目を初期化する場合は、"dest"など名前をつけないといけない※ const destAdmin = admin.initializeApp( { credential: admin.credential.cert(destSA) }, "dest"); const destDB = destAdmin.firestore(); // firestoreのインスタンスを取得 // **************************** // * MAIN // **************************** async function main() { console.log(`***** START MAIN`); // 移行元からデータを取得 const snaps = await srcDB.collection("data").get() const srcData = snaps.docs; for (let v of data) { try { // 移行先からデータを保存 const destDataRef = destDB.collection("data").doc(v.id); await destDataRef.set(v.data()); } catch (error) { console.errror(`Error: ${error}`, error); } } console.log(`***** END MAIN`); process.exit(0); } main().then(); </code></pre> <p>雛形はこんな感じ。</p> <p>以前書いた以下の記事だと丸々移行するので、UIDが変わるとめんどくさいことになる。。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kira_puka/items/ef80b07347cfdd37f116">Firestoreのデータをgcloudを使ってバックアップ&別のプロジェクトへインポートしてみる - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/kira_puka/items/98283e837f30209f23c8">Firestoreのデータを分析するための3つの方法とその注意点 - Qiita</a></li> </ul> <p>この方法であれば、</p> <ul> <li>whereで一部のだけに絞り込んだり、</li> <li><code>set()</code>するときに、UIDを差し替えたり</li> </ul> <p>できるので、開発用に使うにはよいかんじ。</p> <h5 id="注意1: 2つ目を初期化する場合は別の名前に"><a href="#%E6%B3%A8%E6%84%8F1%3A+2%E3%81%A4%E7%9B%AE%E3%82%92%E5%88%9D%E6%9C%9F%E5%8C%96%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AF%E5%88%A5%E3%81%AE%E5%90%8D%E5%89%8D%E3%81%AB">注意1: 2つ目を初期化する場合は別の名前に</a></h5> <p>そのまんまコピペで2つのfirebase-adminを作るとエラーに。。<br /> 2つ目を初期化する場合は、"dest"など名前をつけないといけないいけないらしい。。</p> <p>公式ドキュメントの「<a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/admin/setup#initialize_multiple_apps">複数のアプリを初期化する</a>」に書いてあった。</p> <p>これで、2つのfirebase-adminが起動できるので、データ移行などもできそう(<em>´ω`</em>)</p> <h5 id="注意2: メモリを大量に使いそうな場合はオプションを指定"><a href="#%E6%B3%A8%E6%84%8F2%3A+%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%92%E5%A4%A7%E9%87%8F%E3%81%AB%E4%BD%BF%E3%81%84%E3%81%9D%E3%81%86%E3%81%AA%E5%A0%B4%E5%90%88%E3%81%AF%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E6%8C%87%E5%AE%9A">注意2: メモリを大量に使いそうな場合はオプションを指定</a></h5> <p>データが多く、メモリを大量に使いそうな場合は、<br /> <code>--max_old_space_size</code>オプションを指定しないといけない。。</p> <p>途中で失敗すると(読み込み/書き込み件数的に)悲しいことになるので、<br /> 大きめのサイズを指定しておくと良さそう。</p> <pre><code class="console">node --max_old_space_size=8192 index.js </code></pre> <h5 id="注意3: 件数が多い場合は、limitを指定して再帰処理"><a href="#%E6%B3%A8%E6%84%8F3%3A+%E4%BB%B6%E6%95%B0%E3%81%8C%E5%A4%9A%E3%81%84%E5%A0%B4%E5%90%88%E3%81%AF%E3%80%81limit%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%A6%E5%86%8D%E5%B8%B0%E5%87%A6%E7%90%86">注意3: 件数が多い場合は、limitを指定して再帰処理</a></h5> <p>大量のデータを一度に取得しようとすると、</p> <blockquote> <p>Deadline Exceeded error</p> </blockquote> <p>が出て怒られるので、少しずつ実行するのがよいかんじ。</p> <pre><code class="javascript">const BATCH_SIZE = 300 async function moveData(lastItem) { // 移行元からデータを取得 const colRef = srcDB.collection("data"); let query = colRef.orderBy("createAt", "asc"); if (lastItem != null) query = query.startAfter(lastItem.createAt); const snap = await query.limit(BATCH_SIZE).get(); if (snap.docs.length === 0) return; // 0件なら終了する const srcData = snaps.docs; for (let v of data) { try { // 移行先からデータを保存 const destDataRef = destDB.collection("data").doc(v.id); await destDataRef.set(v.data()); } catch (error) { console.errror(`Error: ${error}`, error); } } // 最後の要素を渡して、そこから継続する await moveData(data[data.length - 1]); } // **************************** // * MAIN // **************************** async function main() { console.log(`***** START MAIN`); await moveData(null); console.log(`***** END MAIN`); process.exit(0); } </code></pre> <h5 id="注意4: 複数の場所に反映する場合は、batchやtransactionを使う"><a href="#%E6%B3%A8%E6%84%8F4%3A+%E8%A4%87%E6%95%B0%E3%81%AE%E5%A0%B4%E6%89%80%E3%81%AB%E5%8F%8D%E6%98%A0%E3%81%99%E3%82%8B%E5%A0%B4%E5%90%88%E3%81%AF%E3%80%81batch%E3%82%84transaction%E3%82%92%E4%BD%BF%E3%81%86">注意4: 複数の場所に反映する場合は、batchやtransactionを使う</a></h5> <p>途中でエラーになる場合もあるので、batchやtransactionを使うとよさそう。</p> <p>公式ドキュメントだと、このあたり<br /> - <a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/firestore/manage-data/transactions">トランザクションと一括書き込み  |  Firebase</a></p> <pre><code class="javascript"> for (let v of data) { try { const batch = destDB.batch(); // 移行先からデータを保存 const destDataRef = destDB.collection("data").doc(v.id); batch.set(destDataRef, v.data()); // 移行先からデータを保存: その2 const destData2Ref = destDB.collection("data2").doc(v.id); batch.set(destData2Ref, v.data()); // コミット await batch.commit(); } catch (error) { console.errror(`Error: ${error}`, error); } } </code></pre> <p>ループ外で使うほうのもいい感じ。</p> <p>ただ、一括で書き込めるドキュメント数は500までと制限があるので、<br /> 一度に書き込む範囲は適宜調整が必要。</p> <blockquote> <p>1 回のトランザクションまたは一括書き込みでは、<br /> 最大500のドキュメントに書き込みを行うことができます。<br /> <a target="_blank" rel="nofollow noopener" href="https://firebase.google.com/docs/firestore/manage-data/transactions?hl=ja#security_rules_limits">トランザクションと一括書き込み  |  Firebase</a></p> </blockquote> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>firebase-adminは複数のプロジェクトでも扱える。<br /> 部分的なデータ移行や一部を書き換えながらの移行もできる(<em>´ω`</em>)</p> <p>ただ、件数が多かったり、複数の書き込みがある場合は、いろいろ必要。。</p> <h2 id="こんなのつくってます!!"><a href="#%E3%81%93%E3%82%93%E3%81%AA%E3%81%AE%E3%81%A4%E3%81%8F%E3%81%A3%E3%81%A6%E3%81%BE%E3%81%99%21%21">こんなのつくってます!!</a></h2> <p>積読用の読書管理アプリ 『積読ハウマッチ』をリリースしました!<br /> <a target="_blank" rel="nofollow noopener" href="https://tsundoku.site">積読ハウマッチ</a>は、Nuxt.js+Firebaseで開発してます!</p> <p><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478782/572d4947-f40b-e4dc-1c9c-bc584cd2a66c.png" width="200"/></p> <p>もしよかったら、遊んでみてくださいヽ(=´▽`=)ノ</p> <p>要望・感想・アドバイスなどあれば、<br /> 公式アカウント(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/MemoryLoverz">@MemoryLoverz</a>)や開発者(<a target="_blank" rel="nofollow noopener" href="https://twitter.com/kira_puka">@kira_puka</a>)まで♪</p> きらぷか@積読ハウマッチ/SSSAPIなど