📝

【Golang】GCSに上げられたCSVファイルからBigQueryにテーブルを作成する

2022/01/01に公開

実際にやってみようと記事を探し回ったところ、何箇所かつまづいたので記録を残す。

目標

GCSにCSVファイルがアップロードされたことを検知し、そのスキーマをパースしてBigQueryにテーブルを新しく生やす。

やること

  1. Cloud Functionsにデプロイするコードを用意する
  2. gcloudコマンドでデプロイ

コード

main.go
package gcstobq

import (
	"cloud.google.com/go/bigquery"
	"context"
	"log"
	"strconv"
	"time"
)

type GCSEvent struct {
	Bucket string `json:"bucket"`
	Name   string `json:"name"`
}

func GcsToBQ(ctx context.Context, e GCSEvent) error {
	const (
		dataset = "<BigQueryのDataset名>"
		table = "<作成したいテーブル名>"
	)
	projectId := "<対象のBigQueryが存在するプロジェクトのID>"
	client, err := bigquery.NewClient(ctx, projectId)
	if err != nil {
		log.Printf("client init error: %v", err)
		return err
	}

	defer client.Close()

	// create table from csv
	gcsRef := bigquery.NewGCSReference("gs://" + e.Bucket + "/" + e.Name)
	// ヘッダ行は無視
	gcsRef.SkipLeadingRows = 1
	// スキーマを自動取得
	gcsRef.AutoDetect = true

	loader := client.Dataset(dataset).Table(table).LoaderFrom(gcsRef)
	// 対象のテーブルが存在しない場合のみ作成(上書きなどは行わない)
	loader.WriteDisposition = bigquery.WriteEmpty

	job, err := loader.Run(ctx)
	if err != nil {
		log.Printf("load run error: %v", err)
		return err
	}

	status, err := job.Wait(ctx)
	if err != nil {
		log.Printf("wait error: %v", err)
		return err
	}

	if status.Err() != nil {
		log.Printf("job completed with error: %v", status.Err())
		return err
	}

	return nil
}
go.mod
module example.com/gcstobq

go 1.16

require cloud.google.com/go/bigquery v1.25.0
以下略

デプロイ

gcloud functions deploy GcsToBQ --region=asia-northeast1 \                   
		   --runtime go116 \
		   --trigger-resource <Bucket名> \
		   --trigger-event google.storage.object.finalize

https://cloud.google.com/functions/docs/calling/storage?hl=ja
https://cloud.google.com/functions/docs/deploying/filesystem

ハマったところ

エントリポイントの関数の引数

引数を無しでデプロイすると、ビルドは通過するが以下のようなエラー文とともにデプロイに失敗した。
This is likely due to a bug in the user code.
上記のサンプルコードのように引数を渡すと無事デプロイ成功した。

func GcsToBQ(ctx context.Context, e GCSEvent) error {

参照するCSVファイルのパスを適切に渡す

gs://から始まるパスを渡す必要があった。

gcsRef := bigquery.NewGCSReference("gs://" + e.Bucket + "/" + e.Name)

モジュール名

example.com/<好きな名前>の形にしないとビルド時に失敗した。

module example.com/gcstobq

AutoDetect

テーブルのスキーマ自動生成は列名が英語でないと文字化けする。
列名を英語にしておくか、スキーマ定義を自分であらかじめ定義しておく必要がある。

gcsRef.AutoDetect = true

参考にさせていただいた記事

https://www.tweeeety.blog/entry/2021/04/21/211730

Discussion