Qiitaではグループ会社のプロダクトでもあるmicroCMSを一部プロダクトで利用しております。今回、microCMS上にあるコンテンツをBigQueryに転送したいケースが発生しました。
microCMSのデータをBigQueryに転送する場合、いくつかの方法が存在します。
今回はmicrocms-ruby-sdkとgoogle-cloud-bigqueryを使って簡単な転送プログラムを作成します。
利用するGem
今回は先に挙げた、microcms-ruby-sdkとgoogle-cloud-bigqueryと環境変数を扱うdotenv、さらにmicroCMSから取得するデータを扱うためにostructを使います。
# -*- mode: ruby -*-
# frozen_string_literal: true
source 'https://rubygems.org'
gem 'dotenv'
gem 'google-cloud-bigquery'
gem 'microcms-ruby-sdk'
gem 'ostruct' # Remove this line when resolve https://github.com/microcmsio/microcms-ruby-sdk/issues/8
環境変数設定
- 以下の環境変数を入力していきます
- microCMSの設定で利用
-
MICROCMS_SERVICE_ID: microCMSのサブドメイン -
MICROCMS_API_KEY: 利用するAPIキー -
MICROCMS_ENDPOINT: 利用するAPIのエンドポイント
-
- Google Cloudの設定で利用
-
GOOGLE_CLOUD_*: サービスアカウントキーの対応するキーの値を入力する
-
- BigQueryの設定で利用
-
BIGQUERY_*: データを転送したいデータセット・テーブルの名前をそれぞれ入力
-
- microCMSの設定で利用
MICROCMS_SERVICE_ID=
MICROCMS_API_KEY=
MICROCMS_ENDPOINT=
GOOGLE_CLOUD_PROJECT_ID=
GOOGLE_CLOUD_PRIVATE_KEY_ID=
GOOGLE_CLOUD_PRIVATE_KEY=
GOOGLE_CLOUD_CLIENT_ID=
GOOGLE_CLOUD_CLIENT_EMAIL=
GOOGLE_CLOUD_CLIENT_X509_CERT_URL=
BIGQUERY_DATASET_ID=
BIGQUERY_TABLE_ID=
実装
転送は大きく分けて、microCMSからのデータ取得とBigQueryへの転送の2フェーズに分かれます。
まずはmicroCMSから見ていきます。
なお、今回転送するAPIは以下のフィールドを保有するものとします。
- id(microCMS側で自動作成する文字列)
- title(テキストフィールド)
- body(リッチエディタ)
- pr(真偽値)
microCMS
APIキーを設定して、10件ずつ取得します。攻撃にならないように、APIを実行するたびにsleepを1秒入れています。
コードの通り、list関数にエンドポイントを引数で渡すだけで、簡単に記事データを取得することができます。
limitの引数を増やすことで一回に取れる記事数を増やせますが、5MBの制限があるため一括で取得できない場合は、limitとoffsetを活用しループさせることで全ての記事データを取得することができます。
require 'dotenv'
require 'microcms'
require 'ostruct' # Remove this line when resolve https://github.com/microcmsio/microcms-ruby-sdk/issues/8
Dotenv.load
MicroCMS.service_domain = ENV.fetch('MICROCMS_SERVICE_ID')
MicroCMS.api_key = ENV.fetch('MICROCMS_API_KEY')
contents = []
loop do
response = MicroCMS.list(ENV.fetch('MICROCMS_ENDPOINT'), limit: 10, offset: contents.size)
contents += response.contents
break if contents.size >= response.total_count || response.contents.empty?
sleep 1
end
BigQuery
先ほどのプログラムの続きです。
まずはGoogle Cloudの設定をしつつ、転送の準備をします。
今回はカラムを気軽に変えたいのでテーブルの作成前に、一度削除をするようにしています。今回は転送に大きな時間はかからないのでこのようにしていますが、Embulkのように一度別名のテーブルで作って、転送完了後リネームするなどの方法でも良いでしょう。
require 'google/cloud/bigquery'
credential = {
type: 'service_account',
project_id: ENV.fetch('GOOGLE_CLOUD_PROJECT_ID'),
private_key_id: ENV.fetch('GOOGLE_CLOUD_PRIVATE_KEY_ID'),
private_key: ENV.fetch('GOOGLE_CLOUD_PRIVATE_KEY').to_s.gsub('\\n', "\n"),
client_email: ENV.fetch('GOOGLE_CLOUD_CLIENT_EMAIL'),
client_id: ENV.fetch('GOOGLE_CLOUD_CLIENT_ID'),
auth_uri: 'https://accounts.google.com/o/oauth2/auth',
token_uri: 'https://oauth2.googleapis.com/token',
auth_provider_x509_cert_url: 'https://www.googleapis.com/oauth2/v1/certs',
client_x509_cert_url: ENV.fetch('GOOGLE_CLOUD_CLIENT_X509_CERT_URL'),
universe_domain: 'googleapis.com'
}
client ||= Google::Cloud::Bigquery.new(project: ENV.fetch('GOOGLE_CLOUD_PROJECT_ID'), credentials: credential)
dataset ||= client.dataset(ENV.fetch('BIGQUERY_DATASET_ID'))
table = dataset.table(ENV.fetch('BIGQUERY_TABLE_ID'))
table.delete if table&.exists?
最後にテーブルを作成し、転送します。今回テーブルはprカラムだけboolean、それ以外はstringです。
APIのデータはOpneStructとして渡されるので、それをハッシュにして転送しています。
また、テーブル作成後数十秒ほどデータ素挿入できるようになるまでラグが存在するため、転送前に10秒待機し、失敗した場合はさらに10秒待ってからリトライするようにしています。
table = @dataset.create_table(ENV.fetch('BIGQUERY_TABLE_ID')) do |updater|
updater.string 'id', mode: :required
updater.string 'title'
updater.boolean 'pr', mode: :required
updater.string 'body'
end
formed_contents = contents.map do |content|
{
id: content.id,
title: content.title,
pr: content.pr,
body: content.body
}
end
sleep 10
tries = 3
begin
table.insert(rows)
rescue Google::Cloud::NotFoundError
tries -= 1
raise if tries.zero?
sleep 10
retry
end
まとめ
このプログラムをGitHub Actionsなどに載せたりすれば簡単に定期的な転送を行うことができます。
この記事を書いてるタイミングで、API情報を取れるようになった(Refs参照)ので、それを使ってテーブルの型生成の自動化などもできそうです。
転送する際は自作プログラムで簡単にできるのでぜひお試しください。
Refs
Rubyとの連携|microCMSドキュメント
API情報を取得するマネジメントAPIが追加されました | microCMSブログ
