🤖
Claude CodeでFirestoreのデータをCSVエクスポートするツールを作ってみた
はじめに
FirestoreのデータをCSVで出力したいケースは実務でよくあります。
しかし、意外にもシンプルなツールが見つかりませんでした。BigQueryにエクスポートする方法はありますが、ちょっとしたデータ確認には大げさです。また、既存のライブラリも複雑で使いづらいものが多く、シンプルにコピペで動くツールが欲しいと思っていました。
そこで、Claude Codeを使って、コピペで簡単に動作できる Firestore to CSVエクスポートツールを作成しました。
完成したツールの機能
主な機能
-
コレクション一覧表示
- プロジェクト内のすべてのコレクションを表示
- 各コレクションのドキュメント数も表示
-
CSV出力
- 指定したコレクションをCSVファイルに出力
- すべてのフィールドを自動検出
- ネストしたデータはJSON形式で出力
セットアップと使い方
1. ファイルの準備
新しいディレクトリを作成し、以下の2つのファイルを作成します。
# ディレクトリ作成
mkdir firestore-csv-export
cd firestore-csv-export
2. main.goファイルを作成
以下のコードをmain.goとして保存します。
package main
import (
"context"
"encoding/csv"
"encoding/json"
"fmt"
"os"
"sort"
"strconv"
"time"
"cloud.google.com/go/firestore"
firebase "firebase.google.com/go/v4"
"google.golang.org/api/iterator"
)
func main() {
// コマンドライン引数のチェック
if len(os.Args) < 2 {
printUsage()
os.Exit(1)
}
// モード判定
if os.Args[1] == "-l" || os.Args[1] == "--list" {
// コレクション一覧表示モード
if len(os.Args) != 3 {
printUsage()
os.Exit(1)
}
projectID := os.Args[2]
if err := listCollections(projectID); err != nil {
fmt.Fprintf(os.Stderr, "エラー: %v\n", err)
os.Exit(1)
}
} else {
// CSV出力モード
if len(os.Args) != 3 {
printUsage()
os.Exit(1)
}
projectID := os.Args[1]
collectionName := os.Args[2]
if err := exportToCSV(projectID, collectionName); err != nil {
fmt.Fprintf(os.Stderr, "エラー: %v\n", err)
os.Exit(1)
}
}
}
func printUsage() {
fmt.Fprintf(os.Stderr, "使用方法:\n")
fmt.Fprintf(os.Stderr, " %s -l <GCPプロジェクトID> # コレクション一覧表示\n", os.Args[0])
fmt.Fprintf(os.Stderr, " %s <GCPプロジェクトID> <コレクション名> # CSV出力\n", os.Args[0])
}
func initializeFirestore(ctx context.Context, projectID string) (*firestore.Client, error) {
conf := &firebase.Config{ProjectID: projectID}
app, err := firebase.NewApp(ctx, conf)
if err != nil {
return nil, fmt.Errorf("Firebase初期化エラー: %w", err)
}
client, err := app.Firestore(ctx)
if err != nil {
return nil, fmt.Errorf("Firestore接続エラー: %w", err)
}
return client, nil
}
func listCollections(projectID string) error {
ctx := context.Background()
client, err := initializeFirestore(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collections := client.Collections(ctx)
collectionRefs, err := collections.GetAll()
if err != nil {
return fmt.Errorf("コレクション取得エラー: %w", err)
}
if len(collectionRefs) == 0 {
fmt.Println("コレクションが存在しません")
return nil
}
fmt.Println("📋 利用可能なコレクション一覧:")
fmt.Println("----------------------------------------")
for i, col := range collectionRefs {
// ドキュメント数を取得
count := 0
iter := col.Documents(ctx)
for {
_, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
fmt.Printf("%d. %s (件数取得エラー)\n", i+1, col.ID)
continue
}
count++
if count >= 1000 {
// 1000件以上は概算表示
fmt.Printf("%d. %s (1000件以上)\n", i+1, col.ID)
break
}
}
if count < 1000 {
fmt.Printf("%d. %s (%d件)\n", i+1, col.ID, count)
}
}
fmt.Println("----------------------------------------")
fmt.Printf("合計: %d個のコレクション\n", len(collectionRefs))
return nil
}
func exportToCSV(projectID, collectionName string) error {
ctx := context.Background()
client, err := initializeFirestore(ctx, projectID)
if err != nil {
return err
}
defer client.Close()
collection := client.Collection(collectionName)
iter := collection.Documents(ctx)
defer iter.Stop()
// 最初のドキュメントでフィールド名を収集
fieldSet := make(map[string]bool)
var docs []*firestore.DocumentSnapshot
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return fmt.Errorf("ドキュメント取得エラー: %w", err)
}
docs = append(docs, doc)
data := doc.Data()
for fieldName := range data {
fieldSet[fieldName] = true
}
// 最初の100件でフィールド構造を把握
if len(docs) >= 100 {
break
}
}
if len(docs) == 0 {
fmt.Printf("⚠️ コレクション '%s' にドキュメントが存在しません\n", collectionName)
return nil
}
// フィールド名をソート
var fieldNames []string
for fieldName := range fieldSet {
fieldNames = append(fieldNames, fieldName)
}
sort.Strings(fieldNames)
// CSV作成
filename := fmt.Sprintf("%s_%s.csv", collectionName, time.Now().Format("20060102_150405"))
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("ファイル作成エラー: %w", err)
}
defer file.Close()
writer := csv.NewWriter(file)
defer writer.Flush()
// ヘッダー出力
headers := append([]string{"document_id"}, fieldNames...)
if err := writer.Write(headers); err != nil {
return fmt.Errorf("ヘッダー書き込みエラー: %w", err)
}
// データ出力(収集済みドキュメント)
count := 0
for _, doc := range docs {
record := make([]string, len(fieldNames)+1)
record[0] = doc.Ref.ID
data := doc.Data()
for i, fieldName := range fieldNames {
record[i+1] = convertToString(data[fieldName])
}
if err := writer.Write(record); err != nil {
return fmt.Errorf("データ書き込みエラー: %w", err)
}
count++
}
// 残りのドキュメントを処理
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
return fmt.Errorf("ドキュメント取得エラー: %w", err)
}
record := make([]string, len(fieldNames)+1)
record[0] = doc.Ref.ID
data := doc.Data()
for i, fieldName := range fieldNames {
record[i+1] = convertToString(data[fieldName])
}
if err := writer.Write(record); err != nil {
return fmt.Errorf("データ書き込みエラー: %w", err)
}
count++
}
fmt.Printf("✅ %s コレクションのCSV出力が完了しました\n", collectionName)
fmt.Printf("📄 出力ファイル: %s (%d件)\n", filename, count)
return nil
}
func convertToString(value interface{}) string {
if value == nil {
return ""
}
switch v := value.(type) {
case string:
return v
case int, int32, int64:
return fmt.Sprintf("%d", v)
case float32, float64:
return fmt.Sprintf("%f", v)
case bool:
return strconv.FormatBool(v)
case time.Time:
return v.Format(time.RFC3339)
case []interface{}, map[string]interface{}:
jsonBytes, _ := json.Marshal(v)
return string(jsonBytes)
case *firestore.DocumentRef:
return v.Path
default:
return fmt.Sprintf("%v", v)
}
}
3. go.modファイルを作成
以下の内容でgo.modファイルを作成します。
module firestore-csv-export
go 1.23
require (
cloud.google.com/go/firestore v1.14.0
firebase.google.com/go/v4 v4.14.1
google.golang.org/api v0.152.0
)
4. 依存関係のインストール
# 依存関係をダウンロードしてgo.sumファイルを生成
go mod tidy
このコマンドを実行すると、必要なパッケージがダウンロードされ、go.sumファイルが自動生成されます。
5. Google Cloud認証の設定
方法1: サービスアカウントキーを使用
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json"
方法2: gcloud CLIでログイン
gcloud auth application-default login
必要な権限
- Cloud Datastore User または
- Firebase Admin
6. プログラムの実行
コレクション一覧を表示
go run main.go -l <プロジェクトID>
# 例
go run main.go -l my-project-id
出力例:
📋 利用可能なコレクション一覧:
----------------------------------------
1. users (150件)
2. orders (89件)
3. products (45件)
----------------------------------------
合計: 3個のコレクション
CSV出力
go run main.go <プロジェクトID> <コレクション名>
# 例
go run main.go my-project-id users
出力例:
✅ users コレクションのCSV出力が完了しました
📄 出力ファイル: users_20240915_143022.csv (150件)
出力仕様
ファイル名形式
<コレクション名>_<日時>.csv
例:users_20240915_143022.csv
データ型の変換ルール
| Firestoreデータ型 | CSV出力形式 | 例 |
|---|---|---|
| string | そのまま | "Hello World" |
| number | 数値 | 123.45 |
| boolean | true/false | true |
| timestamp | RFC3339形式 | 2024-09-15T14:30:22Z |
| array | JSON配列 | ["item1","item2"] |
| map | JSONオブジェクト | {"key":"value"} |
| reference | パス文字列 | projects/my-project/databases/(default)/documents/users/abc123 |
| null | 空文字 | (空) |
Discussion