見出し画像

BigQuery の Javascript UDF を試した

この記事は、 BigQuery Advent Calendar 2022 の 9 日目です。

はじめに

こんにちは、 Zaim でデータエンジニアをしている、 @naoki85 です。
BigQuery 自体は半年前くらいから本格的に触り始め、やっと慣れてきた感じです。
さて、今回は Javascript UDF を試した話をします。

例えば、テーブル内のカラム文字列を名寄せ、正規化する際に、このようなクエリを書くこともあるかと思います。

SELECT 
REPLACE(normalize(original_name, NFKC),”削除したい文字”, ”削除したい文字”) as name
FROM `my_table`

一つ二つだけならいいのですが、複数あった場合は引数の中に引数を含めないといけません。
通常は、バッチ処理などであらかじめ前処理をしておくのですが、 BigQuery 内でなんとかできないかと思っていました。

そこで、 Remote UDF と Javascript UDF を試しました。
Remote UDF は別日の 記事で紹介があるようなので、今回は Javascript UDF の方を記載したいと思います。

作り方

コンソールから実行する場合は簡単です。
以下のクエリを実行すれば作成できます。

– 一時関数でよければ、 TEMP キーワードをつけます。 CREATE OR REPLACE TEMP FUNCTION

CREATE OR REPLACE FUNCTION `project.dataset.replace_name`(name STRING)
RETURNS STRING LANGUAGE js AS R"""
    var text = '' + name;
    text = text.replace(/削除したい文字/, "");
    text = text.replace(/置き換えたい文字/, "置き換え後の文字");
    // …
    return text;
""";

私はまだ試せていませんが、 GCS にライブラリを配置しておくことで、そのコードを使用することも可能なようです。

作成しておけば、他の関数と同じように呼び出すことができます。

SELECT 
`project.dataset.replace_name`(name) as name
FROM `my_table`

普通にクエリを書いた場合と UDF の場合で速度を比較してみた

Javascript UDF は、以下のように 2 回 replace を挟む関数を作成しました。

CREATE OR REPLACE FUNCTION `project.dataset.replace_name`(name STRING)
RETURNS STRING LANGUAGE js AS R"""
    var text = '' + name;
    text = text.replaceAll(/削除したい文字/, "");
    text = text.replaceAll(/置き換えたい文字/, "おきかえたいもじ");
    // …
    return text;
""";

比較に使用したクエリの方はこちらです。

SELECT
REPLACE(REPLACE(name,"削除したい文字", ""), "置き換えたい文字", "おきかえたいもじ") as name
FROM `my_table`;

こちらでサンプルテーブルを作成して、実行してみました。
実行タイミングによって差異はありますが、基本的に、 Javascript UDF の方が時間がかかる傾向が得られました。
おそらく、レコード数が増える、もしくは関数の処理が複雑になるほど、差が大きくなるのではないかと考えています。

terraform で UDF のコード管理

弊社では、汎用的に使用できる UDF などは terraform を使用してコード管理しております。
この UDF を terraform でインポートしたところ、このようになりました。

resource "google_bigquery_routine" "replace_name" {
   dataset_id         = “dataset_id”
   definition_body    = <<-EOT
       var text = '' + name;                                     
           text = text.replaceAll(/削除したい文字/, "")
       text = text.replaceAll(/置き換えたい文字/, "おきかえたいもじ");
          // …
       return text;
   EOT
   imported_libraries = []
   language           = "JAVASCRIPT"
   project            = "project_id"
   return_type        = jsonencode(
       {
           typeKind = "STRING"
       }
   )
   routine_id         = "replace_name"
   routine_type       = "SCALAR_FUNCTION"
 
   arguments {
       data_type = jsonencode(
           {
               typeKind = "STRING"
           }
       )
       name      = "name"
   }
 
   timeouts {}
}

先にレビューを通したい場合などは、これで PR を出してもいいかもしれません。

さいごに

BigQuery には他にもさまざまなサポート機能があり、直近でも機能拡充が頻繁に行われています。
より良いデータ基盤の整備のため、今後も弊社では情報をキャッチアップし、試していきたいと考えています。

BigQuery やその他最新の技術を検証し、 Zaim のデータ基盤を整備してくれる仲間も募集しています!

この記事が気に入ったらサポートをしてみませんか?