PrestoのUDFをscalaで開発する

## Overview

**この記事の対象読者はPrestoでUDFを作ったことがある人です。**

PrestoとはFacebookが開発したオープンソースの分散クエリ処理エンジンです。

詳細は[こちら](https://prestodb.io/overview.html)を参照。

Prestoはプラグインで拡張することが可能でUDFもプラグインとして導入することが出来ます。

これまでいくつかのUDFをJavaで作って来ましたが、最近何年かぶりにscalaに興味が出てきたのでUDFをscalaで書いてみました。


## Sample Code
今回のコードはここにあります。

- https://github.com/yuokada/presto-sample-udf/

## Update pom.xml

まず、pom.xmlを編集してscalaをコンパイル出来る環境を整えます。  
今回は比較のためにJavaでも同じようなUDFを作成するのでJava/Scala混生のmavenプロジェクトのための設定を導入します。

`scala-maven-plugin` のドキュメントにJava/Scala混在プロジェクトでのビルド方法が書かれているのでそれを参考にします。

- http://davidb.github.io/scala-maven-plugin/example_java.html
- http://davidb.github.io/scala-maven-plugin/example_incremental.html

次の3つのコードをpom.xmlに追加します。

```xml
   <properties>
     <scala.major.version>2.12</scala.major.version>
     <scala.version>${scala.major.version}.0</scala.version>
     <scala.maven.plugin.version>3.4.2</scala.maven.plugin.version>
   </properties>
```

```xml
  <dependencies>
    <!--scala-->
    <dependency>
      <groupId>org.scala-lang</groupId>
      <artifactId>scala-compiler</artifactId>
      <version>${scala.version}</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>
```

```xml
  <build>
    <plugins>

      <plugin>
        <groupId>net.alchim31.maven</groupId>
        <artifactId>scala-maven-plugin</artifactId>
        <version>${scala.maven.plugin.version}</version>
        <executions>
          <execution>
            <id>scala-compile-first</id>
            <phase>process-resources</phase>
            <goals>
              <goal>add-source</goal>
              <goal>compile</goal>
            </goals>
          </execution>
          <execution>
            <id>scala-test-compile</id>
            <phase>process-test-resources</phase>
            <goals>
              <goal>testCompile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    
    </plugins>
   </build>    
```

## scala UDFの開発

次にscalaでハローワールドな関数を書きます。

```scala
object HelloWorldScalaUDF {
  @Description("Hello World(UDF Practice)")
  @ScalarFunction("hello_worlds")
  @SqlType(StandardTypes.VARCHAR)
  def helloworld(@SqlNullable @SqlType(StandardTypes.VARCHAR) name: Slice): Slice =
    if (name == null || name.toStringUtf8.isEmpty) {
      utf8Slice("Hello World from scala")
    } 
    else {
      utf8Slice(String.format("Hello %s from scala", name.toStringUtf8))
    }
}
```

ほぼ同じコードをJava版で書くとこちらです。簡単なコードなので関数のコードだけを比べればそれほど大きな差はありません。
`return`, `()`の数が減ったぐらいの差しかありません。

```java
public class HelloWorldUDF
{
    private HelloWorldUDF()
    {
    }

    @Description("Hello World(UDF Practice)")
    @ScalarFunction("hello_world")
    @SqlType(StandardTypes.VARCHAR)
    public static Slice helloworld(@SqlNullable @SqlType(StandardTypes.VARCHAR) Slice name)
    {
        if (name == null || name.toStringUtf8().isEmpty()) {
            return utf8Slice("Hello World");
        }
        else {
            return utf8Slice(String.format("Hello %s", name.toStringUtf8()));
        }
    }
}
```

最後に `UDFPlugin#getFunctions` にscalaのクラスを登録して完了です。

```java
public class UdfPlugin implements Plugin {
    @Override
    public Set<Class<?>> getFunctions()
    {
        return ImmutableSet.<Class<?>>builder()
                .add(HelloWorldScalaUDF.class)
                .build();
    }
}
```

### ビルド

あとはこれをmavenでビルドしてjarを作るだけです。

```
$ ./mvnw clean compile package
```

scala -> Javaの順番にコンパイルしてあげないと`UdfPlugin`でコンパイルが失敗するのですが`scala-maven-plugin`がその辺を面倒を見てくれています。

```
[INFO] --- scala-maven-plugin:3.4.2:add-source (scala-compile-first) @ presto-sample-udf ---
[INFO] Add Source directory: /Users/callistoiv/works/IdeaProjects/prestodb/ecosystem/presto-sample-udf/src/main/scala
[INFO] Add Test Source directory: /Users/callistoiv/works/IdeaProjects/prestodb/ecosystem/presto-sample-udf/src/test/scala
[INFO]
[INFO] --- scala-maven-plugin:3.4.2:compile (scala-compile-first) @ presto-sample-udf ---
[INFO] /Users/callistoiv/works/IdeaProjects/prestodb/ecosystem/presto-sample-udf/src/main/java:-1: info: compiling
[INFO] /Users/callistoiv/works/IdeaProjects/prestodb/ecosystem/presto-sample-udf/src/main/scala:-1: info: compiling
[INFO] Compiling 4 source files to /Users/callistoiv/works/IdeaProjects/prestodb/ecosystem/presto-sample-udf/target/classes at 1537073450711
[INFO] prepare-compile in 0 s
[INFO] compile in 3 s
```

## まとめ

scalaでPresto UDFを作る環境構築の方法を紹介しました。
今回のサンプルコードだとscala化のメリットがあるとは言えませんが業務で作るような`if-else`文満載のコードをパターンマッチで置き換えることで
コードの可読性があがるのではないかという可能性を感じました。

また、ユニットテストのscala化も検討したが自分が書くpresto UDFのユニットテストがそんなに複雑なものではなくscala化のメリットが得られそうになかったので見送りました。