😀

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

2022/11/28に公開

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で使いたいという要件ならおそらく

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

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初心者のつぶやきです・・・・

Discussion