BigQueryで不要フィールドを無視する

いつだったかBigQueryにデータをロードする際に未定義フィールドを無視したい事があった。

Google Cloud StorageからデータをBigQueryに読み込むときは Schema を渡す。 JSON, Avro といった形式ではフィールド情報が含まれているので Schema との不一致を認識できる。 このとき Schema に含まれないフィールドをテーブルに読み込ませないようにしたい。

そんな時は setIgnoreUnknownValues(true) を呼び出す。 (試してないけど csv だと多分できない)

擬似コードは下にかいた。waitOptions, bqOptions だったりは未定義で動かない。 一緒に使っている setCreateDisposition() を使うと対象がない場合にテーブル作成できたりと便利。 JobId.of() でUUID(v4)を渡しているのは特に意味はない。

 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243
package me.masumi.samples.bq

import com.google.cloud.bigquery.*

private val logger = LogManager.getLogger(javaClass)
val waitOptions: Array<out RetryOption> = null
val bqOptions: BigQueryOptions = null

fun loadFromGS(dest: TableId, srcUri: String, schema: Schema, format: FormatOptions) {
    this.execute(
        LoadJobConfiguration.of(dest, srcUri, format).toBuilder()
            .setCreateDisposition(CreateDisposition.CREATE_IF_NEEDED)
            .setIgnoreUnknownValues(true)
            .setSchema(schema).build())
}

fun execute(conf: JobConfiguration) {
    val id = JobId.of(java.util.UUID.randomUUID().toString())
    try {
        val completed = bqOptions.service
                .create(JobInfo.newBuilder(conf).setJobId(id).build())
                .waitFor(*waitOptions)
        if (completed != null) {
            val e = completed.status.error
            if (e == null) {
                logger.info(successMessage(id, conf))
                return
            }
            throw JobException(id, conf, e.message)
        }
        throw JobException(id, conf, "var completed is null")
    } catch (e: InterruptedException) {
        throw JobException(id, conf, e)
    }
}
class JobException : Exception {
    internal constructor(id: JobId, conf: JobConfiguration, msg: String) : super(failureMessage(id, conf, msg))
    internal constructor(id: JobId, conf: JobConfiguration, e: java.lang.Exception) : super(failureMessage(id, conf, e))
}

private fun failureMessage(id: JobId, conf: JobConfiguration, msg: String): String {
    return "${messagePrefix(id, conf)}[FAILURE] ($msg)"
}

何か新しいものを使うときは一通りメソッドを眺めておかないと見落としが起きる。 簡単な事を簡単にできない可能性もあるから、新しくやりたいことが出たらオプション何かないかなって癖も大事だ。 (このとき初めて使ったわけではない)

comments powered by Disqus