こんにちは、トレタ Advent Calendar 2017の8日目記事です。
トレタでは膨大なデータを分析する際に Google BigQuery を使ってますが、今年はデータサイエンティストチームも立ち上がってより大規模な分析が可能になりました。
さてさて、お硬い話は置いといて。クリスマス前なので、ここでは何か面白おかしい SQL 芸でもしましょう。BigQuery は JavaScript ベースのユーザー定義の関数(UDF)をサポートしています、つまり必要であれば好き勝手に UDF で BigQuery の関数を拡張することができます。この際 UDF を使って別の言語を実装して BigQuery のなかで呼び出しましょう!!
イメージ
イメージとしては SQL でこんな呼び出しをできたら面白いと思います。
select mal('(fn* [a b c] (reduce + 0 [a b c]))', [1.1, 2.2, 3.3])
内部はコードを read
関数で AST に変換して、 eval
関数で環境と共に評価します。
Make a Lisp (mal)
今回は Make a Lisp (mal) という Lisp 系の処理系をもとに実装してみようというプロジェクトです。mal の特徴といえば説明が詳しく、多くの言語(現時点で71言語)での実装例を参考にすることが可能です。
内部実装はほとんど mal の JavaScript 実装例を持ってきましたが、Node.js や Browser の環境と異なるため、モジュール周りは変更して、console
オヴジェクトがないため prn
系の関数も除外してます。
完成したコードはこちらに公開しております。
https://github.com/hden/mal
UDF 周り
BigQuery の UDF は基本こんな構造になってますが
create temporary function multiplyInputs(x int64, y int64)
returns int64
language js as """
return x * y
""";
OPTIONS
セクションを付けることで GCS 上の外部コード、またはライブラリを読み込むことが可能です。例えば自分の gs://hden/mal/
バケット内にコードを置くと
#standardsql
create temporary function mal (str string, args array<float64>)
returns float64
language js as 'return mal.apply(str, args)'
options (
library=['gs://hden/mal/bundle.js']
)
;
こういう風に読み込むことが可能です。
データ型
BigQuery の int 型は基本 int64 ですが、 JavaScript に int64 はないので文字列として渡されます、parseInt()
などの関数である程度丸め込むことが可能ですが、注意が必要です。
データ型のマッピングはこちらです。
https://cloud.google.com/bigquery/sql-reference/user-defined-functions?hl=ja#sql-type-encodings-in-javascript
最後に
楽しんでいただけたでしょうか?
#standardsql
create temporary function mal (str string, args array<float64>)
returns float64
language js as 'return mal.apply(str, args)'
options (
library=['gs://hden/mal/bundle.js']
)
;
select mal('(fn* [a b c] (reduce + 0 [a b c]))', [1.1, 2.2, 3.3])
では皆さん、良いクリスマスを。