📖

FirestoreにJSONファイルのデータをダミーデータとしてインサートしたい

2023/11/26に公開

私は普段、FlutterとFirebaseを使って個人開発を行っています。アプリでリストを表示する際、Firestoreにテストデータが必要になることがよくあります。しかし、Firestoreのコンソールで手動でデータを入力するのは手間です。そこで、テストデータを簡単に作成できるスクリプトを用意しました。

このスクリプトは、ダミーデータをすぐに作成したいときに役立ちます。記事のタイトル通り、JSONでダミーデータを作成するのに特化しています。

Firestoreに保存するデータのコレクションやドキュメントの構造が決まっている場合、JSONファイルで準備したデータをFirestoreに直接挿入することができて便利です。

スクリプトはGithubに公開していますので、自由に利用できます。

コードの説明は不要で、すぐにスクリプトを利用したい方は、以下のGithubのリンクからソースをクローンし、事前準備を行うことですぐに使用できるようになります。

https://github.com/king-kazu39/SampleFirestoreDummyData

以下のコマンドが用意されています。
それらはダミーデータを作成するコマンドと、指定したコレクションを削除するコマンドのみです。

command description
npm run insert 'collectionName' 事前にJSONファイルを作成する必要がある。指定したコレクション名のJSONファイルをインサートする。
npm run delete 'collectionName' 指定した名前のコレクションを削除する

フォルダ構成について

フォルダ構成は以下のようにしています。

.
├── README.md
├── config
│	└── firebaseConfig.js
├── json
│           └── example
│	        └── users.json
├── keys
│            └── .gitignore
├── package-lock.json
├── package.json
└── scripts
	├── delete.js
	├── funstions.js
	└── insert.js

主要なフォルダとファイルについて説明します。

  • /config/firebaseConfig.js

以下はfirebaseConfig.jsファイルの中身です。

Firestoreのインスタンスを取得し、後で説明するscripts内のjsファイルで使用するために再利用しやすいように、モジュールとしてエクスポートしています。

firebaseConfig.js
// Firebase Admin SDKをインポート
const admin = require('firebase-admin');
// サービスアカウントキーをインポート(Firebase認証用)
const serviceAccount = require('../keys/serviceAccountKey.json');

// Firebase Admin SDKを初期化
admin.initializeApp({
    credential: admin.credential.cert(serviceAccount)
});

// Firestoreインスタンスの取得
const db = admin.firestore();

module.exports = db;
  • jsonフォルダ

このフォルダには、ダミーデータとして登録したいコレクションをjsonファイルとして配置します。ファイル名はコレクション名になるので、jsonファイルを作成する際にはコレクション名.jsonというファイル名にする必要があります。jsonファイルの内容は、exampleを参考に作成します。

例: users.json

  • scriptsフォルダ

このフォルダには以下の3つのファイルを用意しています。後ほど詳しく説明します。

ファイル名 説明
insert.js ダミーデータ作成の時に実行するファイル
delete.js ダミーデータを削除する時に実行するファイル
functions.js insert.jsdelete.jsで行う処理をまとめたファイル
  • package.json

package.jsonnpm initコマンドを実行すると作成されます。

このファイルにはメタデータ(プロジェクトの名前、バージョン、著者、説明など)、依存関係(プロジェクトが必要とするnpmパッケージのリスト)、スクリプトなどが記述されています。

scriptsの値にはコマンドが定義されています。コマンド名を変更したり、新しいコマンドを追加したい場合は、この値を修正してください。

package.json
{
  "name": "firestore-dummy-data",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "insert": "node ./scripts/insert.js",
    "delete": "node ./scripts/delete.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "firebase-admin": "^11.11.0"
  }
}

ダミーデータ作成コマンドで利用するコードについて

npm run insert 'collectionName'のコマンドを使用して、ダミーデータを作成できます。

コマンドを実行するときに関連するファイルは以下の通りです。

  • insert.js

insert.jsファイルは、JSONファイルから読み取ったダミーデータをFirestoreに挿入する役割を果たします。以下にその詳細なコードの説明を記載します。

insert.js
const func = require('./funstions');

const collectionNameFromArg = process.argv[2];
const jsonDirPath = './json/';
const jsonExt = '.json';
const jsonFilePath = jsonDirPath + collectionNameFromArg + jsonExt;

func.insertData(jsonFilePath);

  • const func = require('./funstions');では、funstions.jsファイルをインポートしています。このファイルにはFirestoreへのデータ挿入やデータ削除といったメソッドが定義されています。
  • const collectionNameFromArg = process.argv[2];では、コマンドラインから受け取った第2引数(つまり、コレクション名)を変数collectionNameFromArgに代入しています。
  • 次の3行では、JSONファイルのパスを組み立てています。const jsonDirPath = './json/';でJSONファイルが保存されているディレクトリのパスを指定し、const jsonExt = '.json';でJSONファイルの拡張子を指定します。そして、const jsonFilePath = jsonDirPath + collectionNameFromArg + jsonExt;でこれらを組み合わせてフルパスを作成します。
  • 最後にfunc.insertData(jsonFilePath);funstions.jsinsertDataメソッドを呼び出し、作成したJSONファイルのパスを引数として渡します。これにより、指定されたJSONファイルからデータを読み取り、それをFirestoreに挿入する操作が実行されます。
  • functions.js

functions.jsファイル内に定義されたinsertDataメソッドは、JSONファイルから読み取ったダミーデータをFirestoreに挿入する機能を持っています。このメソッドは非同期(async)であり、指定したファイルパスを引数として受け取ります。

functions.js
const db = require('../config/firebaseConfig');
const path = require('path');
const fs = require('fs');
const admin = require('firebase-admin');
...

// Firestoreにダミーデータを挿入する関数
async function insertData(filePath) {
    const collectionName = path.basename(filePath, '.json'); // ファイル名を取得(拡張子なし)
    const collectionRef = db.collection(collectionName);
    const batch = db.batch();

    // JSONファイルの内容を読み込む
    const documents = JSON.parse(fs.readFileSync(filePath, 'utf8'));
    documents.forEach((docData, index) => {
        const docRef = collectionRef.doc(); // 新しいドキュメントIDを作成
        const timestamp = createTimestampWithOffset(index);
        batch.set(docRef, {
            ...docData,
            filePath,  // ドキュメントデータにファイルパスを含める
            createdAt: timestamp,
            updatedAt: timestamp
        });
    });

    await batch.commit();
    console.log('Data from ' + filePath + ' has been successfully inserted into ' + collectionName);
}

...

// insertDataメソッドはinsert.jsファイルで実行するために、エクスポートしています。
module.exports = {
        ...
    insertData
};

まず、コレクション名を取得するために、ファイル名から拡張子(.json)を除去します。次に、Firestoreからそのコレクションへの参照を取得し、バッチ書き込みを行うためのバッチを作成します。

JSONファイルの内容はfs.readFileSyncを使って読み取り、JSON.parseでパースします。この結果を使って、各ドキュメントに対して以下の操作を行います。

  1. 新しいドキュメントIDを作成します。
  2. 現在のインデックスからタイムスタンプを生成します。このタイムスタンプはcreatedAtおよびupdatedAtフィールドに使用されます。
  3. バッチに、新しいドキュメントの作成を追加します。このドキュメントは、元のドキュメントデータ、ファイルパス、そして先ほど生成したタイムスタンプから作成されます。

最後に、バッチをコミットします。これにより、Firestoreに全ての変更が書き込まれます。成功すると、コンソールにメッセージが表示されます。

このメソッドにより、JSONファイルに記述されたすべてのデータがFirestoreに挿入されます。

createTimestampWithOffsetメソッドはFirestoreに挿入するドキュメントデータにタイムスタンプを追加するためのものです。このメソッドは、Firestoreが利用するadmin.firestore.Timestampを使って作成されたタイムスタンプを生成します。

functions.js
// 指定されたオフセット量でタイムスタンプを生成するメソッド
function createTimestampWithOffset(index, timerNumer = 1) {
    return admin.firestore.Timestamp.fromDate(
        new Date(new Date().getTime() + index * timerNumer * 60000)
    );
}

ここで、indexはドキュメントのインデックスで、timerNumberは分単位での時間オフセット量です。timerNumberにデフォルト値として1が設定されています。これは、各ドキュメントが1分ごとにタイムスタンプをずらすことを意味します。

new Date().getTime()は現在の日付と時刻をエポック時間(1970年1月1日からの経過ミリ秒数)で返します。このエポック時間にindex * timerNumer * 60000を加えることで、指定された分数だけ時間をずらすことができます。ここで、60000は1分をミリ秒で表現したもの(60秒 × 1000ミリ秒/秒)です。

この新しいエポック時間から新しいDateオブジェクトを作成し、それをadmin.firestore.Timestamp.fromDateに渡してFirestore用のタイムスタンプを作成します。

このメソッドを使うことで、Firestoreに挿入する各ドキュメントに一意で連続したタイムスタンプを追加することができます。

ダミーデータ削除コマンドで利用するコードについて

npm run delete 'collectionName'のコマンドを使って、ダミーデータを削除できます。

コマンドを実行する際に関連するファイルは以下のとおりです。

  • delete.js

delete.jsでは、コマンドライン引数から指定されたコレクション名を取得し、そのコレクションの全ドキュメントを削除する処理を行います。具体的なコードは以下の通りです。

delete.js
const func = require('./funstions');

// コマンドライン引数からコレクション名を取得
const collectionNameFromArg = process.argv[2];

// 対象コレクションの全ドキュメント削除を実行
func.deleteCollectionData(collectionNameFromArg);

このコードでは、insert.jsと同様にまずrequire('./funstions')functions.jsをインポートします。その後、process.argv[2]でコマンドライン引数からコレクション名を取得します。最後に、取得したコレクション名を引数にしてdeleteCollectionData関数を実行します。この関数はfunctions.js内に定義されており、指定したコレクションの全ドキュメントを削除する機能を持っています。

  • functions.js

functions.jsファイルに定義されているdeleteCollectionDataメソッドは、Firestoreから特定のコレクション内のデータを削除する役割を果たしています。このメソッドは非同期(async)であり、削除を実行したいコレクション名を引数として受け取ります。

以下に、その詳細なコードの説明を示します。

functions.js
const db = require('../config/firebaseConfig');
const path = require('path');
const fs = require('fs');
const admin = require('firebase-admin');

// 指定したコレクション内のデータを全削除する関数
async function deleteCollectionData(collectionName) {
    try {
	// ①
        const collectionRef = db.collection(collectionName); // コレクションへの参照を取得
        const snapshot = await collectionRef.get(); // コレクション内の全ドキュメントを取得
	
	// ②
        if (snapshot.empty) {
            console.log('No documents found in ' + collectionName);
        } else {
	    // ③
            const batch = db.batch();
            // ④
	    snapshot.docs.forEach(doc => {
                batch.delete(doc.ref); // バッチにドキュメントの削除を追加
            });
	    // ⑤
            await batch.commit(); // バッチをコミットしてFirestoreに全ての変更を書き込む
            console.log('All documents in ' + collectionName + ' have been deleted.');
        }
      // エラーが発生した場合は、catchブロックが実行され、エラー情報がコンソールに出力されます。
    } catch (error) {
        console.error('Error documents in ' + collectionName + ': ', error);
    }
}

①まず、削除対象のコレクションへの参照を取得します。
次に、そのコレクション内の全ドキュメントを取得します。
if (snapshot.empty)を使ってコレクションが空(ドキュメントが1つもない)かどうかを判定します。空の場合、コンソールにメッセージを出力します。
③コレクションが空でない場合は、バッチ書き込みを行うためのバッチを作成します。
db.batch()を使って新しい書き込みバッチを作成します。)
④全てのドキュメントに対して、バッチにそのドキュメントの削除を追加します。この操作を行うためには、各ドキュメントの参照(doc.ref)が必要となります。
⑤最後に、バッチをコミットします。これにより、Firestoreに全ての変更が書き込まれ、コレクション内の全データが削除されます。成功すると、コンソールにメッセージが表示されます。

このメソッドにより、指定したコレクション内の全データを一括で削除することが可能となります。

まとめ

Firestoreにダミーデータを挿入するためのスクリプトについて説明しました。少しでも参考になったり、Githubで公開しているコードを利用してもらえたら嬉しいです。

参考リンク

https://qiita.com/zaburo/items/42bbf923696e19af5039
https://github.com/king-kazu39/SampleFirestoreDummyData

Discussion