えいのうにっき

a-knowの日記です

Google APIs Client Library for Ruby を使って BigQuery にデータをロードする

表題の件で躓いてしまったので、ここで書いておく。 "Job configuration must contain exactly one job-specific configuration object (e.g., query, load, extract, spreadsheetExtract), but there were 0: " というエラーが出て、うまくロードできない、という現象。

結論からいうと、ここのStackOverflowでのやりとりが手がかりとなって、下記のようなコードでなんとかロードすることができた。リファクタもなにもしてないコードで、失礼。(gist

require 'bundler/setup'
require 'google/api_client'
require 'yaml'
require 'json'

def multipart_boundary
  'xxx'
end

# load credential yaml
oauth_yaml = YAML.load_file('.google-api.yaml')

# Initialize the client.
client = Google::APIClient.new(
  :application_name => 'Example Ruby Bigquery',
  :application_version => '1.0.0')
client.authorization.client_id = oauth_yaml["client_id"]
client.authorization.client_secret = oauth_yaml["client_secret"]
client.authorization.scope = oauth_yaml["scope"]
client.authorization.refresh_token = oauth_yaml["refresh_token"]
client.authorization.access_token = oauth_yaml["access_token"]

# Initialize Bigquery client.
bq_client = client.discovered_api('bigquery', 'v2')

job_config = {
  'configuration' => {
    'load' => {
      'sourceUris' => ['gs://a-know-test/sample.csv'],
      'schema' => {
        'fields' => [
          {
            'name' => 'id',
            'type' => 'INTEGER'
          },
          {
            'name' => 'name',
            'type' => 'STRING'
          },
          {
            'name' => 'price',
            'type' => 'INTEGER'
          },
        ]
      },
      'destinationTable' => {
        'projectId' => 'test-001',
        'datasetId' => 'test',
        'tableId'   => 'sample'
      },
      'createDisposition' => 'CREATE_NEVER',
      'writeDisposition' => 'WRITE_APPEND'
    }
  }
}

body = "--#{multipart_boundary}\n"
body += "Content-Type: application/json; charset=UTF-8\n"
body += "\n"
body += "#{job_config.to_json}\n"
body += "--#{multipart_boundary}--\n"

# Make an API call.
result = client.execute(
  :api_method => bq_client.jobs.insert,
  :parameters => {
    'projectId' => '234230709110',
    'uploadType' => 'multipart'
  },
  :body => body,
  :headers => { 'Content-Type' => "multipart/related; boundary=#{multipart_boundary}" }
)


puts result.data
puts result.response.body

自分的につまづきポイントだと思った点を、いくつか挙げてみる。

  • "Job configuration must contain〜" のエラーは、リクエストのパラメータになんらかの不備があった場合に出る
    • 特定のパラメータに不備があるときだけに出るようなものではないので厄介
  • body には configuration のjsonだけじゃだめ
    • Content-Type の指定とか multipart boundary とかもいる
  • しかも Content-Type の指定の次に空行が必要
  • headers には以下の2点の指定が必要
    • Content-Type として multipart/related
    • ハイフンのプレフィックスを省いた multipart boundary
  • body末尾の boundary にはサフィックスとしてハイフンが必要
  • parameters に 'uploadType' => 'multipart' の指定が必要(これはここにも書いてある)

この body の書き方って、こういうお作法でしたっけ。今までにも APIリクエストの際のリクエストbody をいじってたことはあったけど、こんな書き方、したっけなぁ。

Ruby で BigQuery」は、ちょっとわけあって今後も少しづつ試していこうと思っているので、同じような境遇の方はこちらwatch して頂ければと。



follow us in feedly