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など