MongoDB のデータを BigQuery にインポートするための mongobq

# mongobq とは

MongoDB の特定のコレクションのデータを BigQuery のテーブルとしてインポートするためのコマンドラインツールです。

## 想定している使用方法

- fluent-plugin-bigqueryなどでBigQueryにログやトランザクション系のデータが登録済みである
- ログ中には商品IDなどは入っているが、商品の値段などの詳細データはMongoDBで管理されている
- 両者を JOIN して分析するために、MongoDB 上のマスタデータを BigQuery に日次バッチなどでインポートしたい

## インストール方法

[npm パッケージとして公開](https://www.npmjs.com/package/mongobq)しているので、Node 0.10 以上が必要です。

```
$ npm install -g mongobq
```

ソースはgithub上で公開しています。ご意見、機能追加要望などありましたら、[こちら](https://github.com/hakobera/mongobq)まで。

なお、mongobq を利用するには Google Cloud Platform の利用登録が必要です。また、利用の際には無料枠を超えた文については料金がかかることがありますのでご注意ください。

## 使い方

### オプション一覧

```
  Usage: mongobq [options]

  Options:

    -h, --help                      output usage information
    -V, --version                   output the version number
    --host <hostname>               specifies a hostname for the mongod. (Default: "localhost")
    --port <port>                   specifies the TCP port for the mongod. (Default: 27017)
    -u, --username <username>       specifies a mongodb username to authenticate
    -p, --password <password>       specifies a mongodb password to authenticate
    -d, --database <database>       specifies the name of the database
    -c, --collection <collection>   specifies the collection to export
    -f, --fields <field1[,field2]>  specifies a field or fields to include in the export
    -q, --query <JSON>              provides a JSON document as a query that optionally limits the documents returned in the export
    -K, --keyfile <keyfile>         specifies the key file path
    -B, --bucket <bucket>           specifies the Google Cloud Storage bucket name
    -O, --path <path>               specifies the output path of the bucket (Default: "/")
    -X, --suffix <suffix format>    specifies the suffix format (Default: "YYYYMMDDHHmmss")
    -P, --project <project>         specifies the project ID of Google Cloud Platform
    -D, --dataset <dataset>         specifies the dataset ID of BigQuery
    -T, --table <table>             specifies the table ID of BigQuery
    -S, --schema <schemafile>       specifies the table schema of BigQuery table to import
    --autoclean                     clean after run
    --async                         no wait load job
    --nocompress                    nocompress data when upload to Google Cloud Storage
    --dryrun                        run as dryrun mode
```

仕事で使うパターンを網羅していたら色々とオプションが増えてしまいましたが、代表的な利用パターンを例示しておきます。大体この4パターンで事足ります。なお、下記例では以下の条件を暗黙的に付与してあります。

- localhost 上の MongoDB に接続
- 対象コレクションが MongoDB 上のデータが1GB以下である

### 例1: 全部お任せパターン

- GCS上の一時ファイルはインポート完了後に削除
- BigQuery のスキーマは MongoDB のコレクションデータから自動判定
- BigQuery のテーブル名は MongoDB のコレクション名と同じで良い

```
$ mongobq --database [dbname] --collection [collection-name] \
  --project [bigquery-project] --dataset [bigquery-dataset] \
  --keyfile [your-gcp-json-keyfile.json] \
  --bucket [bucket-name] --autoclean
```

### 例2: スキーマファイルを指定したいパターン

- GCS上の一時ファイルはインポート完了後に削除
- BigQuery のスキーマはスキーマファイルで指定する
    - フォーマットは[bqコマンドと同じ](https://cloud.google.com/bigquery/bq-command-line-tool#creatingtablefromfile)）
    - スキーマで指定されないフィールドは無視されます
- BigQuery のテーブル名は MongoDB のコレクション名と同じで良い

```
$ mongobq --database [dbname] --collection [collection-name] \
  --project [bigquery-project] --dataset [bigquery-dataset] \
  --keyfile [your-gcp-json-keyfile.json] \
  --bucket [bucket-name] --schema [schema.json] --autoclean
```

### 例3: バックアップも兼ねて指定したGCS上のディレクトリに出力しデータは残すパターン

- バックアップも兼ねて、GCS上の一時ファイルは残す
  - 下記では今日の日付でディレクトリを作っています
- スキーマは自動判定
- BigQuery のテーブル名は MongoDB のコレクション名と同じで良い

```
$ mongobq --database [dbname] --collection [collection-name] \
  --project [bigquery-project] --dataset [bigquery-dataset] \
  --keyfile [your-gcp-json-keyfile.json] \
  --bucket [bucket-name] --path `date +%Y%m%d`
```

## 興味のある方向けの追加情報

## mongoexport 使わないのはなぜ？

mongoexport は BSONでexportされるので、そのままでは BigQuery に取り込めないから。CSV出力だと取り込めなくもないが、arrayのデータはそのまま取り込めないので、カンマ区切りにするなどの対応するのが大変で遅いので諦めました。

```
$ mongoexport -d test -c users
{ "_id" : { "$oid" : "54752d762300e880f600007e" } ... }
```

### mongobq の便利な点

- dryrunモードがある。dryrunモード時はGCPへの操作が行われない。一時ファイル名の確認や、スキーマの自動判定の結果が見たい場合などにどうぞ。
- GCSのバケットとディレクトリはなければ自動作成してくれる
- GCSにアップロード時に圧縮してくれる
- MongoDB のデータから BigQuery のスキーマデータを自動判定してくれる。MongoDB のArray は BigQuery の REPATED フィールドとして判定してくれる。
- MongoDB -- 整形 --> GCS の転送を Node の Stream で実装しているので、どんなにサイズが大きいコレクションに対してもメモリ／ディスクの少ないサーバでも実行可能
  - 1億レコードのコレクションをインポートしてもメモリは150MB程度だし、ディスク仕様は原則ゼロ

### mongobq のいまいちな点

- ZIP圧縮の部分がCPUバウンドな処理で、1コア100%ごっつり持っていかれること
- 埋め込みオブジェクトに対応していない（JSON.stringify された結果が STRING 型としてインポートされる）
- スキーマ自動判定だと、数字データは全部 FLOAT になる。頑張れば整数判定もできたのですが、JavaScript には整数型はないので、初期リリースでは素直にそのまま処理することにしました。

### 処理時間の目安

原則、データのサイズよりは件数に比例して処理時間が伸びます。環境に依存すると思いますが、参考までに実際に試したデータを書いておきます。下記は全て AWS の us-east-1 梨状ん上の m3.xlarge インスタンスから BigQuery にインポートした時にかかった時間です。

- 件数多め、データサイズは小さめ
  - 約1億レコード、JSON出力時13.3GB(非圧縮）: 4時間47分
- 件数少なめ、データサイズは大きめ
  - 約500万レコード、JSON出力時34.7GB(非圧縮）: 1時間50分
- 件数少なめ、データサイズは小さめ（これが本来の想定している使用方法）
  - 約30万レコード、JSON出力時3MB(圧縮）: 2分23秒

### 容量制限

Stream処理をしているので、実行サーバのリソースには原則制限はありません。また、`--uncompress` オプションを使えば、1TBまでのデータはインポートできるので、よほど大規模じゃない限り大丈夫だと思います。そもそもマスターデータのインポート用なので、データがどんどん増えていくトランザクション系のデータは、おとなしく [fluent-plugin-bigquery](https://github.com/kaizenplatform/fluent-plugin-bigquery) を使って streaming インサートすることをお勧めします。

### なんでバージョン1.0じゃないの？

依存ライブラリである [gcloud-node](https://github.com/GoogleCloudPlatform/gcloud-node) の最新版（github 上の masterブランチ）に依存しているから。gcloud-node に追加された GCS への resumable upload 機能が利用したかったのですが、現状リリースされているバージョンにはまだ取り込まれていないので、このような対応になりました。gcloud-node の次のバージョンが出た際に、それを取り込んでバージョン1.0としてリリースする予定です。

mongobq自体は仕事でも使っているので、バグがないとは言えませんが、ある程度の規模のデータで利用できることは実証済みです。