Firestoreの運用が捗るデータ移行ツールfsrpl

これは [Firebase #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/firebase_2)の16日目の記事です. 昨日は、@H_Crane さんでした.

### 注意:2020/10追記

コマンドの破壊的変更がされたようです。
最新版でのコマンドは本記事ではなく、下記を参照してください


https://zenn.dev/mtskhs/articles/fsrpl-replication-tool-for-testing

## これは何

Firebaseの運用にとても便利な [fsrpl](https://github.com/matsu0228/fsrpl) (Firestore Replication Tool) と、その使い方について紹介します.

## 目的

Firebaseを運用していて、下記のニーズはよくあると思います。

- データ更新前に更新対象の特定のドキュメントだけをバックアップしておいて、万が一操作を間違えたらそのドキュメントだけを復元したい
- 特定のドキュメントを、別のCollection配下にコピーしたい
- 別のProjectのFirebaseから、他のFirebaseへ、特定のドキュメントのデータをコピーしたい

公式に[CLIツール](https://firebase.google.com/docs/cli?hl=ja)も提供されており一括バックアップ・一括復元などはできますが、特定のデータだけをバックアップし手軽に復元することはできないので、運用しにくいと感じていました。本記事では、上記ニーズを満たすCLIツール`fsrpl`について紹介します。

Firebaseには、 `Cloud Firestore` (以降Firestoreと呼ぶ) と `RealtimeDatabase` の２種類のデータベースがありますが、現時点で `fsrpl`が対応しているのは `Firestore` のみのようです。


## 事前設定

### インストール


- Macの場合は、homebrewで簡単にインストールできました


```
# homebrewを使ったインストール
# https://github.com/matsu0228/fsrpl#homebrew

brew tap matsu0228/homebrew-fsrpl
brew install fsrpl
fsrpl  --help
```


- Go経由: fsrplは、Goで実装されているようで、Goがセットアップ済の環境であれば簡単にインストールできるようです

```
go get github.com/matsu0228/fsrpl
```


- バイナリからインストールする場合は、[ここから](https://github.com/matsu0228/fsrpl/releases)ダウンロードできるようです (LinuxもWindowsも対応していそうです)


### Firebaseの認証鍵の用意

- Firebaseの認証鍵ファイルを[公式ドキュメント](https://firebase.google.com/docs/admin/setup?authuser=0) のとおり取得して、任意のディレクトリに格納してください

## 使い方

### 必須オプション

処理対象のFirebaseの秘密鍵と、対象のパス(Collection + documentId)

- Firebaseの秘密鍵の指定方法は、２種類あります
  - `--secret=` オプションで指定する.環境変数よりもこちらが優先されます.
  - 環境変数 `FIRESTORE_SECRET` として指定する
- 対象のパス指定
  - 引数として指定します。例: `users/tarou` のように、collectionId + documentId で指定してください(必ず偶数となります。)


### 利用例: firestoreのデータをローカル保存

- ローカルにバックアップするときには、`-f`オプションでディレクトリを指定します
- 1documentに対して1ファイルを生成し、documentIdがファイル名として出力されます

```
$ fsrpl --secret=(your secret JSON)  "users/taro"  -f ./backups

$ ls ./backups
 taro.json
```

- 特定のコレクション配下を指定する場合は、documemtIdの指定の代わりにワイルドカード(`*`)を指定すると、すべてのドキュメントを保存できます

```
$ fsrpl --secret=(your secret JSON)  "users/*"  -f ./backups

$ ls ./backups
 taro.json  jiro.json ......
```

### 利用例: ローカルにあるJSONファイルからfirestoreへ復元

- ローカルにバックアップ済のJSONファイルを、firestoreにインポートする場合は、 `-i` オプションを利用します.

```
$ fsrpl --secret=(your secret JSON)  "users/taro"  -i ./backups/taro.json

# DocumentIdを指定してインポート
$ fsrpl --secret=(your secret JSON)  "groups/A/users/usera"  -i ./backups/taro.json
```


- また、`*`で一括インポートできます。このとき、ファイル名をdocumentIdとしてインポートするので、注意してください。既存のdocumentがあれば上書き更新します

```
$ fsrpl --secret=(your secret JSON)  "users/*"  -i ./backups/*.json
```


### 利用例: 特定のドキュメントを、別のCollection配下にコピー

Firebaseでは、破壊的なスキーマ変更をする場合は、既存データはそのままにして、コレクション名を変更した先に新しいスキーマでデータをいれていくといった運用をすると思います。そのときなどに、本利用例が役立ちます。

- 下記のようにして、`--destPath` を指定することで、別のCollectionへコピーできます.

```
$ fsrpl --secret=(your secret JSON)  "users/taro" --destPath="groups/A/users/taro"  
```

- `*`で、一括コピーもできます.このとき、DocumentIdは変えずにコピーします.

```
$ fsrpl --secret=(your secret JSON)  "users/*" --destPath="usersData/*"  
```

- コピーしたら元のドキュメントを削除したい場合は、 `--delete` オプションで実現できます.

```
# users/* のドキュメントはすべて削除されます.サブコレクションは削除されません

$ fsrpl --secret=(your secret JSON)  "users/*" --destPath="usersData/*" --delete
```


### 利用例: あるProjectのFirestoreから他のProjectのFirestoreへデータをコピー




Firestoreのprojectを変更をしたい 、開発用Firestoreから個人開発用のFirestoreにデータをコピーしたい、といったときに利用できます.[^1]


- 別のfirestoreへのコピーを宣言するオプション `--destfs` と、そのfirestoreの認証鍵を指定するオプション `--dest-secret` で実現できます.

```
# １件だけコピー
$ fsrpl --secret=(your secret JSON)  "users/taro" --destPath="users/*"
  --destfs --dest-secret=(your another secret JSON)  --destPath="users/taro"


# `*` で一括コピー
$ fsrpl --secret=(your secret JSON)  "users/*" --destPath="users/*"
  --destfs --dest-secret=(your another secret JSON)  --destPath="users/*"
```

[^1]: 話題のアドベントカレンダーでも [GCP Projectを消しちゃった話](https://839.hateblo.jp/entry/2019/12/07/000000) として言及がありましたが、Firebaseを消すとGCPのプロジェクトも全て消えるので注意してください.


## 足りないところ

基本的にはとても便利に利用しているツールなのですが、もう少し改善するといいと思う点を書いてみます.


### 利用できるデータ型に制限がある

`fsrpl` は現時点でβ版のツールのようです。 [制限](https://github.com/matsu0228/fsrpl/blob/master/README_ja.md#%e5%88%b6%e9%99%90 )の項目に注意してご利用されることをお勧めます.
具体的には、「参照」や「地理座標」が入っているドキュメントは、このツールではコピーできないのでご注意ください.

### 再帰的にコピーができない

再帰的にコピーができると、もっと運用が楽になると思います.
具体的には、Firestoreではサブコレクションが利用でき下記のようにデータが保存するといったことがあります.下記の例では `stockNoteList`がサブコレクション・  `{noteId} / noteTitle` がその配下のドキュメントとなります.


```
# ユーザーごとにストックした記事データが保存されている

/users/ taro / stockNoteList / {noteId} / noteTitle
                             / {noteId} / noteTitle

        jiro / stockNoteList / {noteId} / noteTitle
                             / {noteId} / noteTitle
```

fsrplでは、「ドキュメントの一括コピー」はできますが、サブコレクションごとコピーすることはできません
.

```
fsrpl --secret=(your secret JSON)  "users/*" --destPath="usersData/*" 

# 全ユーザーのドキュメントデータはコピーできるが、サブコレクション`stockNoteList`配下はコピーできない
/userData / taro / {documentData}
            jiro / {documentData}
```

自分はshellで頑張って利用していますが、CLIツールの機能として提供してもらいたいです

```bash
fsrpl --secret=(your secret JSON)  "users/*" --destPath="usersData/*" 

userIds=("taro" "jiro")
for userId in ${userIds[@]} ; do
  echo "copy stockNote of ${userId}"
  fsrpl --secret=(your secret JSON)  "users/${userId}/stockNoteList/*" --destPath="usersData/${userId}/stockNoteList/*" 
done

```


### オプションが多くて分かりづらい

実現したいことごとにオプションを指定する必要があるのでしょうがない点もありますが、オプションが多いので毎回しらべてから利用する手間を感じます.

たとえば、TUIクライアントとしての機能があれば、 `fsrpl` を実行すると、「対象のコレクションを指定してください」のようなウィンドウを表示させて、グラフィカルに操作することもできるかなと思います.

参考:@gorilla0513 さんの [docui](https://qiita.com/gorilla0513/items/814f369bb25b1ea3ae00) を知り、TUIクライアントとしてこんなに直感的に操作できるツールがあることに驚きました



## 最後に

みなさんは、Firebase運用にあたって、どのような工夫をされていますか？？
なにかノウハウがあれば、コメントいただきたいです！！

明日は、 @masashi-sutou さんです。お楽しみに！