emahiro/b.log

Drastically Repeat Yourself !!!!

Go で BigQuery を叩くときは公式の Nullable 型を利用する

Overview

Go で BigQuery (以下 BQ) のクライアントの実装して返されたクエリの結果を Entity に Unmarshal する方法について調べたのでその備忘録です。

pkg.go.dev

Note

結論から言ってしまうと自前でゴリゴリ型指定して Unmarshal するよりも BQ の package に BQ のデータ型の定義があるのでこれを参考にすると良さそうです。

// Each BigQuery column type corresponds to one or more Go types; a matching struct
// field must be of the correct type. The correspondences are:
//
//   STRING      string
//   BOOL        bool
//   INTEGER     int, int8, int16, int32, int64, uint8, uint16, uint32
//   FLOAT       float32, float64
//   BYTES       []byte
//   TIMESTAMP   time.Time
//   DATE        civil.Date
//   TIME        civil.Time
//   DATETIME    civil.DateTime
//
// A repeated field corresponds to a slice or array of the element type. A STRUCT
// type (RECORD or nested schema) corresponds to a nested struct or struct pointer.
// All calls to Next on the same iterator must use the same struct type.
//
// It is an error to attempt to read a BigQuery NULL value into a struct field,
// unless the field is of type []byte or is one of the special Null types: NullInt64,
// NullFloat64, NullBool, NullString, NullTimestamp, NullDate, NullTime or
// NullDateTime. You can also use a *[]Value or *map[string]Value to read from a
// table with NULLs.

ref: https://github.com/googleapis/google-cloud-go/blob/v0.46.3/bigquery/iterator.go#L83-L104

特に以下の部分

It is an error to attempt to read a BigQuery NULL value into a struct field, unless the field is of type byte or is one of the special Null types: NullInt64, NullFloat64, NullBool, NullString, NullTimestamp, NullDate, NullTime or NullDateTime. You can also use a Value or map[string]Value to read from a table with NULLs.

カラムによっては NULL のケースもあると思うので以下のように BQ での NULL を安全に処理する型が用意されているのは助かります。

type NullInt64 struct {
    Int64 int64
    Valid bool // Valid is true if Int64 is not NULL.
}

https://github.com/googleapis/google-cloud-go/blob/5a2ed6b2cd1c304e0f59daa29959863bff9b5c29/bigquery/nulls.go#L40-L43

最初自前でEntity の定義をして時刻型や INTERGER あたりでデバックしながら進めてたんですが、カラム数が多いとカラムによってあったりなかったりする(= Null の扱い) のが面倒だったりそこそこなデータ量のクエリ叩くのでお金の不安もあり、ミスるとやり直してたりして辛かったのですが、GoDoc 眺めに行ったら一発で解決しました。