Shopify のデータを BigQuery にエクスポートする方法（Ruby）

## 前提
- Shopify のデータは Bulk Operations で取得する物とします。詳しい方法は[公式ドキュメント](https://shopify.dev/api/usage/bulk-operations/queries)をご覧ください。
- BigQuery へのデータのエクスポートには [google-cloud-bigquery gem](https://github.com/googleapis/google-cloud-ruby/tree/main/google-cloud-bigquery) を使用します。

## 実装

```:Gem
$ gem install google-cloud-bigquery
```

```ruby
require "google/cloud/bigquery"
require "open-uri"

# Ref: https://googleapis.dev/ruby/google-cloud-bigquery/v1.37.0/file.AUTHENTICATION.html
Google::Cloud::Bigquery.configure do |config|
  config.project_id  = "プロジェクトIDを入力"
  config.credentials = "サービスアカウント認証のjsonファイルへのパスを入力"
end

bigquery = Google::Cloud::Bigquery.new retries: 5, timeout: 600
dataset = bigquery.dataset "データセット名を入力"
table = dataset.create_table "テーブル名を入力"

bulk_operation_url = "Bulk Operation で得た URL を入力"
URI.open(bulk_operation_url) do |file|
  load_job = table.load_job file do |config|
    config.format = "json"   # Bulk Operation の出力ファイルが jsonl 形式であるため。
    config.autodetect = true # 当行を省略する場合は schema の事前指定が必須になります。
  end
  load_job.wait_until_done!
end
```
## 補足
- デフォルトだと[60秒](https://github.com/googleapis/google-cloud-ruby/issues/3235#issuecomment-487091490)でタイムアウトするので、 `execution expired` エラーが発生する場合は timeout の値を変更すると良さそうです。（上記実装例では600秒に設定。）
- `load_job.failed?` 等を使用してエラー処理を書くことが出来ます。詳しくは Google::Cloud::Bigquery::Job の[公式ドキュメント](https://googleapis.dev/ruby/google-cloud-bigquery/latest/Google/Cloud/Bigquery/Job.html)をご覧ください。
- [load_job](https://googleapis.dev/ruby/google-cloud-bigquery/latest/Google/Cloud/Bigquery/Table.html#load_job-instance_method) を使用するとエラー内容が分かりづらい場合があるので、うまく行かない場合は一旦 [load](https://googleapis.dev/ruby/google-cloud-bigquery/latest/Google/Cloud/Bigquery/Table.html#load-instance_method) を使ってみると失敗理由が分かる事があります。
- `dataset.create_table` の実行時に [schema を事前指定](https://github.com/googleapis/google-cloud-ruby/blob/main/google-cloud-bigquery/OVERVIEW.md#creating-datasets-and-tables) する事で、 `config.autodetect = true` を省略する事が出来ます。
- 環境にも依るかと思いますが、5GB程度の jsonl ファイルであれば数分で同期出来ました。

## Schema の auto-detection
各カラムのデータ型を一つ一つ事前定義する必要があったら骨が折れるな〜と思っていたのですが、BigQuery が良い感じに schema を autodetect してくれました。
![Screen Shot 2021-11-07 at 1.26.54 AM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179851/ac176ddc-b032-bb86-415a-309bec93f2b5.png)

## 参照
- [Perform bulk operations with the GraphQL Admin API | Shopify](https://shopify.dev/api/usage/bulk-operations/queries)
- [googleapis/google-cloud-ruby | GitHub](https://github.com/googleapis/google-cloud-ruby/tree/main/google-cloud-bigquery)
- [Google Cloud BigQuery (OVERVIEW.md) | GitHub](https://github.com/googleapis/google-cloud-ruby/blob/main/google-cloud-bigquery/OVERVIEW.md)
- [Batch loading data | Google Cloud](https://cloud.google.com/bigquery/docs/batch-loading-data)
- [Quotas and limits | Google Cloud](https://cloud.google.com/bigquery/quotas)
- [Authentication | Google APIs](https://googleapis.dev/ruby/google-cloud-bigquery/latest/file.AUTHENTICATION.html)
- [Class: Google::Cloud::Bigquery::Table | Google APIs](https://googleapis.dev/ruby/google-cloud-bigquery/latest/Google/Cloud/Bigquery/Table.html#load_job-instance_method)
- [Class: Google::Cloud::Bigquery::Job | Google APIs](https://googleapis.dev/ruby/google-cloud-bigquery/latest/Google/Cloud/Bigquery/Job.html)