ExtensionsでFirestore→BigQueryのエクスポート手順

[Firebase #2 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/firebase_2)の10日目の記事です。
半分ぐらいCGPの記事ですが、~~GCPカレンダーが埋まってしまったので~~ Firestore利用者のお役に立てればと思いこちらで書かせていただきます。

# はじめに
現行プロジェクトでFirestoreからBigQueryにデータを同期して、分析等に使うことになりました。
調べたところBigQuery側でほぼ完結し、コードを書かずに済む方法があったので記事にしました。（ただしSQLは書いてます！）

# Firestore→BigQueryの自動連携設定
[Export Collections to BigQuery](https://firebase.google.com/products/extensions/firestore-bigquery-export/)を使います。
従来はFIrestoreの更新に合わせて手動でBigQueryにデータを入れるか、FirestoreのバックアップをCloudStorageにエクスポートしてBigQueryにインポートする手段が取られていました。

Extensionは2019年9月にリリースされたもので、Firestoreの更新のたびにBigQueryに更新情報を渡すようになり常に最新版を得ることができます。実際にはCloudFunctionsにFirestoreの更新で起動するトリガーが登録されています。

### 注意点

- BigQueryのロケーションを指定したい場合は、**先にデータセットを作る**必要があります
    - 指定しないとデフォルトでUSになってしまうためです
- この拡張機能を使用するには、Blazeプランを利用する必要があります
- Collection1つにつき、1つの拡張機能が必要です

### 登録方法

install to consoleからプロジェクトを選び、次に進むを選び続けます（下記画像参照）

- ロケーションはfirestoreと同じに
- CollectionPathはfirestoreのコレクション名
- Table IDはBigQuery側で新しく作るテーブル名（CollectionPathと同じで問題なし）

インストールを押して、しばらく待てば完了です。
あとはFirestore側で値が更新されると、BigQuery側でテーブルとビューを作成し、更新ログを登録してくれます。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3330353531302f32653863656636392d386134652d363861362d643632362d6638636434656632613435332e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305510/06c82297-27a3-ba61-8e0b-8e65ac6f508f.png)


# BigQueryのデータをjson→RDB仕様に変換したビューの作成

ここまででデータセットに2つの情報が出来上がります。

- xxx_raw_changelog（テーブル）
- xxx_raw_latest（ビュー）

このうちビュー側のクエリを実行すると、結果カラムは5つあり、その中の`data`カラムにjson形式でFirestoreの最新データが反映されてます。ビューをクエリ→`SELECT data FROM ...`に書き換えればそれが確認できます。
<img width="677" alt="スクリーンショット 2019-11-29 20.43.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305510/1c24daa1-8832-2fa6-1065-058ed73c1f66.png">
このままではdataカラムはjson形式なので、何かと使いにくいです。そこでjson形式→RDB形式に変換してビューで保存します。
実は当初テーブルを作る予定でしたが、ビューのほうが良いことに気づきました。もしテーブルを作るなら一度DELETEでテーブルを削除してからINSERT句で登録するクエリを作り、クエリのスケジュール登録する・・・くらいしか思いつきませんが。これだと最新版を反映するには手動でクエリを叩く必要もある一方、ビューにすれば常に最新版を反映することができます。

しかしどうやってjsonデータを加工するんだ？最悪SQLゴリゴリ書かないとなのか？と困ってたのですが、公式ドキュメントにありました。
[標準 SQL の JSON 関数
](https://cloud.google.com/bigquery/docs/reference/standard-sql/json_functions?hl=ja)
以下例をもとに作っていきます。
### 例

もとのjsonデータ

```json
[
  {
    "objectID": "001",
    "name": { "firstName": "太郎", "lastName": "田中" },
    "age": 30
  },
  {
    "objectID": "002",
    "name": { "firstName": "花子", "lastName": "山田" },
    "age": 20
  },
  {
    "objectID": "003",
    "name": { "firstName": "次郎", "lastName": "鈴木" },
    "age": 18
  }
]
```

生成したいビュー

|objectID|lastName|firstName|age|
|:--:|:--:|:--:|:--:|
|001|田中|太郎|30|
|002|山田|花子|20|
|003|鈴木|次郎|18|

### クエリの作成
json形式から取り出すには`JSON_EXTRACT`を使います。引数にjson形式のデータとプロパティ名を指定すると、文字列として取得できます。ネストしたオブジェクト形式のものを取り出す場合は関数もネストさせればOKです。整数データを取り出す場合は`CAST`で一度文字列に変換した後数値型に戻してあげればOKです。他にも`JSON_EXTRACT_SCALAR`がありますが、これはjson形式で数値→文字列に変換する場合に使います。

```sql
SELECT 
  JSON_EXTRACT(data, '$.objectID') as objectID, /* 文字型 */
  JSON_EXTRACT(JSON_EXTRACT(data, '$.name'), '$.lastName') as lastName, /* ネストしたオブジェクト */
  JSON_EXTRACT(JSON_EXTRACT(data, '$.name'), '$.firstName') as firstName, 
  CAST(JSON_EXTRACT(data, '$.age') as INT64) as age /*数値型*/
FROM `dataのあるテーブル名`
```
あとはこれをビューで保存するだけでOKです。

### 実行結果
例と異なりますが、こんな感じになります。
これで今日からあなたもSQLライフを楽しめますね！
![スクリーンショット 2019-12-03 10.35.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305510/78dff12d-ba8d-d704-fbd7-73f9fcf9bcf7.png)

## まとめ

私はGCP自体あまり触ったこと無いし、BigQueryはつい最近使い始めたばかりでしたが、色々調べながらうまく連携することができました。料金のコスパは全く考えてないのでそれが一番の心配ですが。。。実験的に使っていこうと思います。