tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

【BigQuery】GolangでBigQueryのTable Create時のSchema指定方法 - bigquery.Schema, bigquery.InferSchema, bigquery.SchemaFromJSON

はじめに

GolangでBigQueryをざつに扱っています。
GolangでBigQueryのtableを作成時のSchema指定方法についての自分メモです。

この記事のサンプルコードは以下においてあります。

アジェンダ

1. schemaの指定方法は何があるか

まずは基本的なtable create時のschema指定方法ですが、以下のように行います。

   schema := /* 何らかの方法でSchemaを生成 */
    metaData := &bigquery.TableMetadata{Schema: schema}
    t := client.Dataset(dataset).Table(table)
    if err := t.Create(ctx, metaData); err != nil {
        return err
    }

/* 何らかの方法でSchemaを生成 */ですが、
主には以下の3点があると思います。

  • bigquery.Schema: field個別にSchema指定
  • bigquery.InferSchema: struct + タグでSchema指定してみる
  • bigquery.SchemaFromJSON: jsonファイルでSchema指定

ということで、以下では3つの方法を試してみます。
おもむろにサンプルコードを載せて解説は省きます。

2. bigquery.Schema: field個別にSchema指定

fieldを個別にschemaを指定する方法です。

   // client
    ctx := context.Background()
    client, err := bigquery.NewClient(ctx, projectID)
    if err != nil {
        return err
    }

    // schema
    schema := bigquery.Schema{
        {Name: "id", Required: true, Type: bigquery.StringFieldType},
        {Name: "data", Required: false, Type: bigquery.StringFieldType},
        {Name: "timestamp", Required: false, Type: bigquery.TimestampFieldType},
    }
    metaData := &bigquery.TableMetadata{Schema: schema}

    // create table
    t := client.Dataset(dataset).Table(table1)
    if err := t.Create(ctx, metaData); err != nil {
        return err
    }

3. bigquery.InferSchema: struct + タグでSchema指定してみる

structにbigqueryタグを指定し、bigquery.InferSchemaでschemaを作成する方法です。

type Item struct {
    ID        string    `bigquery:"id"`
    Data      string    `bigquery:"data"`
    Timestamp time.Time `bigquery:"timestamp"`
}

func createTableBySchema2() error {
    // client
    ctx := context.Background()
    client, err := bigquery.NewClient(ctx, projectID)
    if err != nil {
        return err
    }

    // schema
    schema, err := bigquery.InferSchema(Item{})
    metaData := &bigquery.TableMetadata{Schema: schema}

    // create table
    t := client.Dataset(dataset).Table(table2)
    if err := t.Create(ctx, metaData); err != nil {
        return err
    }
    return nil
}

4. bigquery.SchemaFromJSON: jsonファイルでSchema指定

jsonからSchemaを作成する方法です。
ファイルでなくても大丈夫ですが、この例ではファイルから読み込んでいます。

   // client
    ctx := context.Background()
    client, err := bigquery.NewClient(ctx, projectID)
    if err != nil {
        return err
    }

    // schema
    path := "./schema/schemaSampleTable3.json"
    buf, err := ioutil.ReadFile(path)
    if err != nil {
        panic(err)
    }
    schema, err := bigquery.SchemaFromJSON(buf)
    metaData := &bigquery.TableMetadata{Schema: schema}

    // create table
    t := client.Dataset(dataset).Table(table3)
    if err := t.Create(ctx, metaData); err != nil {
        return err
    }

5. どれが良いのか?

1つのポイントとして、nullable指定したいか?がキーになりそうです。

InferSchema(struct + タグ)指定ですが、
以下のようにnullable指定ができません。(というかやり方を調べたけどわからなかった)

  • 2. bigquery.Schema: field個別にSchema指定のテーブル

f:id:tweeeety:20210405025757p:plain

  • 3. bigquery.InferSchema: struct + タグでSchema指定してみるのテーブル

f:id:tweeeety:20210405025806p:plain

  • 4. bigquery.SchemaFromJSON: jsonファイルでSchema指定のテーブル

f:id:tweeeety:20210405025815p:plain

そのため、nullを許容する場合はInferSchema以外が良さそうです。

なぜ今回の調査をしたか

なぜ今回調査をしたかですが、
「GolangでAPI(json形式)を受けて、その結果をBigQueryに保存したい」
という要件がありました。

この場合、APIの結果jsonとBigQueryのschemaのstructを一緒にできないか?と考えていました。

以下のような感じですね。

// APIの結果jsonとbigqueryのschemaを同じstructにしたい
type Item struct {
    ID        string    `json:"id" bigquery:"id",`
    Data      string    `json:"data" bigquery:"data"`
    Timestamp time.Time `json:"time_stamp" bigquery:"timestamp"`
}

ただし、nullableにできない(requiredになってしまう)という事に悩んだため、なくなく他の方法を探してみたという感じでした。

参照

試したコードスニペットと参考にした公式です。

おわりに

InferSchemaでNullalbeを指定する方法がわかったら知りたい...!!