Ruby
Rails
google
bigquery

google-api-ruby-clientでBigQuery使い方メモ

More than 1 year has passed since last update.

rubyでgoogleのAPIを使うならgoogle-api-ruby-clientや他のラッパーしたgemを使用することがあると思いますが、google-api-ruby-client でBigQueryを扱うのに結構苦労したので忘れない為のメモになります。

今回のメモはbatchでbigqueryを叩く前提になります。

なのでフロント側でユーザー認証させたい場合は認証の仕方が変わりますのでご注意ください。

先に謝っておきます。当方rubyはまだ初心者なのでツッコミ歓迎です。


環境


  • centos6.7


事前準備に必要なもの

認証情報のjsonは下記のようなフォーマットのjsonになります。

{

"type": "service_account",
"project_id": "xxxxxxxxxx",
"private_key_id": "xxxxxxxxxxxxx",
"private_key": "-----BEGIN PRIVATE KEY-----xxxxxxxxxxxxxxxxxxx\n-----END PRIVATE KEY-----\n",
"client_email": "123456789-xxxxxx@developer.gserviceaccount.com",
"client_id": "123456789-xxxxxxxxxx.apps.googleusercontent.com",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/123456789-xxxxxxxxx%40developer.gserviceaccount.com"
}


参考にしたサイト


最近google-api-ruby-clientが大幅なアップデートがされています

すみません。最近っていつなんだよ?ってところまでは調べていません。

参考: google-api-ruby-clientが非互換なアップデートされた

なのでbigquery ruby とかでググって試してみても、メソッドのインターフェースが大分違っているので注意しましょう。

この関係でabronte/BigQueryなどはgemのバージョン指定しないと動かないので注意が必要です。

gem 'google-api-client', '~> 0.8.6'

abronte/BigQueryを使う手もあったですが、最近Google関連のものが頻繁にアップデートされている印象を受けたので

本家のgoogle-api-clientを使うことにしました。

ちゃんと試していないですが、BigQueryをrubyで使いたいという要件ならおそらく

* abronte/BigQuery

* ttanimichi/bigquery-client

を使ったほうが楽に扱えると思います。


exampleとかググってもアップデート後の使い方はほとんどありません

なのでruby初心者だけども、Gemの中身見まくって頑張りました。


BigQuery UIで動作確認をするときの注意事項


  • データセットの作成

  • データセットにテーブルを作成する

まではUIで確認できますが、テーブルにデータ投入後のデータの確認は人によって見れない場合があります。

僕はコレでAPIレスポンスが200なのにBigQuery UI でデータが入っていない現象(実際はbqで見れば入っていた)で半日ハマりました。。

データ投入の確認は BigQuery CommandLine Toolを使って確認することを強くおすすめします。


/path/to/hoge

bq query "SELECT count(*) FROM dataset_id.table_id"



google-api-clientの構成と仕組み

ちゃんと調べていないのですが、google-api-clientのリポジトリを調べてもservice単位でのソースコードは検索できません。

なぜならばサービス単位のAPIはgenerateされて作られてるみたいです。

通常のGemの構成ならば lib/ 以下にファイルがあるはずですが、google-api-clientの場合は generated/ 以下にAPIサービス関連のロジックが配置されています。

lib/ 以下は認証系のメソッドとAPIコール系のメソッドとかしかない感じです(厳密には調べていません)

以下tree


/path/to/rails_root

vendor/bundle/ruby/2.3.0/gems/google-api-client-0.9.6

├── bin
├── generated
│   └── google
│   └── apis #一部APIは省略
│   ├── analytics_v3
│   ├── bigquery_v2
│   ├── youtube_partner_v1
│   ├── youtube_v3
│   └── youtubereporting_v1
├── lib
│   └── google
│   ├── api_client
│   │   └── auth
│   │   └── storages
│   └── apis
│   ├── core
│   └── generator
│   └── templates
├── rakelib
├── samples
│   ├── cli
│   │   └── lib
│   │   └── samples
│   └── web
│   └── views
└── third_party
3 [error opening dir]


google-api-client-0.9.6/generated/google/apis/bigquery_v2.rb

Google APIのscopeの定義が記載されています。


google-api-client-0.9.6/generated/google/apis/bigquery_v2/service.rb

API実行のインターフェースメソッドがあります。

今回はservice.rbの中のメソッド


  • get_table


    • BigQueryデータセットの中にあるテーブル情報を取得



  • insert_table


    • BigQueryデータセットにテーブルを作成する



  • delete_table


    • BigQueryデータセットの中にあるテーブルを削除する



  • insert_all_table_data


    • BigQueryデータセットの中にあるテーブルにstreamingデータ挿入をする



の使い方のメモです

(select系は追って記載します)


何はともあれGemをインストールする

Gemfileに記載しましょ


Gemfile

gem 'google-api-client'


bundle install


/path/to/rails_root

bundle install



認証をする

batch実行(rails runner)を想定した認証になります。

scopeは状況に応じて適切な設定を行ってください。


/path/to/hoge/rails_root/lib/tasks/hoge.rb

require 'googleauth'

require 'googleauth/stores/file_token_store'
require 'google/apis/bigquery_v2'

module Tasks
module Hoge
GOOGLE_CRED_JSON_PATH = File.join(File.expand_path(File.dirname(__FILE__)), "../../config/tasks/google-auth-cred.json")
ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
BigQuery = Google::Apis::BigqueryV2

def self.init
@bigquery = BigQuery::BigqueryService.new
@bigquery.authorization = ServiceAccountCredentials.make_creds(
json_key_io: File.open(GOOGLE_CRED_JSON_PATH),
scope: [
self::BigQuery::AUTH_BIGQUERY,
self::BigQuery::AUTH_CLOUD_PLATFORM,
self::BigQuery::AUTH_DEVSTORAGE_FULL_CONTROL,
self::BigQuery::AUTH_BIGQUERY_INSERTDATA
]
)

@bigquery
end

end
end



get_table

BigQueryデータセットの中にあるテーブル情報を取得します。

動作確認をするために最初に何かしらのサンプルテーブルを作成しておいてください。

先ほどの認証部分を除外した内容です。

table_idはtable名を指定してください。


/path/to/hoge/rails_root/lib/tasks/hoge.rb

module Tasks

module Hoge
PROJECT_ID = "sample-bigquery"
DATASET_ID = "sample-dataset"

def self.execute
# データテーブル情報を取得する
@bigquery.get_table(self::PROJECT_ID, self::DATASET_ID, "teble_id")
end
end
end



insert_table

BigQueryのデータセットにテーブルを作成する

schemaの定義一覧などはBigQueryUI等をみれば、どういう定義があるかわかると思います。


/path/to/hoge/rails_root/lib/tasks/hoge.rb

module Tasks

module Hoge
PROJECT_ID = "sample-bigquery"
DATASET_ID = "sample-dataset"

def self.execute
schema = [
{ name: "id", type: "INTEGER", mode: 'required', description: "uniq id"},
{ name: "path", type: "STRING", description: "testing string filed"},
{ name: "pv", type: "INTEGER", description: "testing string filed"}
]

 table = BigQuery::Table.new(
table_reference: { project_id: self::PROJECT_ID, dataset_id: self::DATASET_ID, table_id: "table_id" },
schema: { fields: schema }
)
begin
@bigquery.insert_table(
self::PROJECT_ID,
self::DATASET_ID,
table
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end
end
end



delete_table

BigQueryデータセットの中にあるテーブルを削除する


/path/to/hoge/rails_root/lib/tasks/hoge.rb

module Tasks

module Hoge
PROJECT_ID = "sample-bigquery"
DATASET_ID = "sample-dataset"

def self.execute
begin
@bigquery.delete_table(
self::PROJECT_ID,
self::DATASET_ID,
"table_id"
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end
end
end



insert_all_table_data

BigQueryデータセットの中にあるテーブルにstreamingでデータ挿入をします。

streamingデータの場合は容量単位で課金されますので、ご注意ください。

BigQuery 料金形態


/path/to/hoge/rails_root/lib/tasks/hoge.rb

module Tasks

module Hoge
PROJECT_ID = "sample-bigquery"
DATASET_ID = "sample-dataset"

def self.execute
rows = [
{
:insertId => "hoge",
:json => { id: 1, path: "12345", pv: 99999 }
},
{
:insertId => "fuga",
:json => { id: 2, path: "67890", pv: 99999 }
},
]
insert_all_table_data_request = Google::Apis::BigqueryV2::InsertAllTableDataRequest.new({
:rows => rows
})
begin
@bigquery.insert_all_table_data(
self::PROJECT_ID,
self::DATASET_ID,
"table_id",
insert_all_table_data_request
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end
end
end



認証までまとめるとこんな感じ


/path/to/hoge/rails_root/lib/tasks/hoge.rb

module Tasks

module Hoge
GOOGLE_CRED_JSON_PATH = File.join(File.expand_path(File.dirname(__FILE__)), "../../config/tasks/google-auth-cred.json")
ServiceAccountCredentials = Google::Auth::ServiceAccountCredentials
BigQuery = Google::Apis::BigqueryV2

def self.init
@bigquery = BigQuery::BigqueryService.new
@bigquery.authorization = ServiceAccountCredentials.make_creds(
json_key_io: File.open(GOOGLE_CRED_JSON_PATH),
scope: [
self::BigQuery::AUTH_BIGQUERY,
self::BigQuery::AUTH_CLOUD_PLATFORM,
self::BigQuery::AUTH_DEVSTORAGE_FULL_CONTROL,
self::BigQuery::AUTH_BIGQUERY_INSERTDATA
]
)

@bigquery
end

#table情報を参照
def self.get_table
@bigquery.get_table(self::PROJECT_ID, self::DATASET_ID, "teble_id")
end

#テーブルの作成
def self.create_table
schema = [
{ name: "id", type: "INTEGER", mode: 'required', description: "uniq id"},
{ name: "path", type: "STRING", description: "testing string filed"},
{ name: "pv", type: "INTEGER", description: "testing string filed"}
]

 table = BigQuery::Table.new(
table_reference: { project_id: self::PROJECT_ID, dataset_id: self::DATASET_ID, table_id: "table_id" },
schema: { fields: schema }
)
begin
@bigquery.insert_table(
self::PROJECT_ID,
self::DATASET_ID,
table
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end

# テーブルの削除
def self.delete_table
begin
@bigquery.delete_table(
self::PROJECT_ID,
self::DATASET_ID,
"table_id"
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end

# streaming insert
def self.insert_all_table_data
rows = [
{
:insertId => "hoge",
:json => { id: 1, path: "12345", pv: 99999 }
},
{
:insertId => "fuga",
:json => { id: 2, path: "67890", pv: 99999 }
},
]
insert_all_table_data_request = Google::Apis::BigqueryV2::InsertAllTableDataRequest.new({
:rows => rows
})
begin
@bigquery.insert_all_table_data(
self::PROJECT_ID,
self::DATASET_ID,
"table_id",
insert_all_table_data_request
)
rescue Google::Apis::ClientError => ex
p ex.message
end

end
end



最後に

結局シンプルなのですが、ここまでたどり着くまでに苦労した・・・・・

なruby初心者のつぶやきです・・・・