Java UDFs
バージョン v2.2.0 以降、Java プログラミング言語を使用して、特定のビジネスニーズに合わせたユーザー定義関数 (UDF) をコンパイルできます。
バージョン v3.0 以降、StarRocks はグローバル UDF をサポートしており、関連する SQL ステートメント (CREATE/SHOW/DROP) に GLOBAL キーワードを含めるだけで済みます。
このトピックでは、さまざまな UDF の開発と使用方法について説明します。
現在、StarRocks はスカラー UDF、ユーザー定義集計関数 (UDAF)、ユーザー定義ウィンドウ関数 (UDWF)、およびユーザー定義テーブル関数 (UDTF) をサポートしています。
前提条件
-
Apache Maven をインストールしており、Java プロジェクトを作成およびコンパイルできます。
-
サーバーに JDK 1.8 がインストールされています。
-
Java UDF 機能が有効になっています。この機能を有効にするには、FE 設定ファイル fe/conf/fe.conf で FE 設定項目
enable_udfをtrueに設定し、FE ノードを再起動して設定を反映させます。詳細については、パラメーター設定 を参照してください。
UDF の開発と使用
Maven プロジェクトを作成し、Java プログラミング言語を使用して必要な UDF をコンパイルする必要があります。
ステップ 1: Maven プロジェクトの作成
次のような基本的なディレクトリ構造を持つ Maven プロジェクトを作成します。
project
|--pom.xml
|--src
| |--main
| | |--java
| | |--resources
| |--test
|--target
ステップ 2: 依存関係の追加
pom.xml ファイルに次の依存関係を追加します。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>udf</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</plugin>
</plugins>
</build>
</project>
ステップ 3: UDF のコンパイル
Java プログラミング言語を使用して UDF をコンパイルします。
スカラー UDF のコンパイル
スカラー UDF は単一のデータ行に対して動作し、単一の値を返します。クエリでスカラー UDF を使用する場合、各行は結果セットの単一の値に対応します。典型的なスカラ関数には UPPER、LOWER、ROUND、ABS などがあります。
JSON データのフィールドの値が JSON オブジェクトではなく JSON 文字列であると仮定します。SQL ステートメントを使用して JSON 文字列を抽出する場合、GET_JSON_STRING を 2 回実行する必要があります。例えば、GET_JSON_STRING(GET_JSON_STRING('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key"), "$.k0") のようにします。
SQL ステートメントを簡素化するために、JSON 文字列を直接抽出できるスカラー UDF をコンパイルできます。例えば、MY_UDF_JSON_GET('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key.k0") のようにします。
package com.starrocks.udf.sample;
import com.alibaba.fastjson.JSONPath;
public class UDFJsonGet {
public final String evaluate(String obj, String key) {
if (obj == null || key == null) return null;
try {
// JSONPath ライブラリは、フィールドの値が JSON 文字列であっても完全に展開できます。
return JSONPath.read(obj, key).toString();
} catch (Exception e) {
return null;
}
}
}
ユーザー定義クラスは、次の表に示すメソッドを実装する必要があります。
NOTE
メソッドのリクエストパラメーターと戻りパラメーターのデータ型は、ステップ 6 で実行される
CREATE FUNCTIONステートメントで宣言されたものと同じであり、このトピックの「SQL データ型と Java データ型のマッピング」セクションで提供されるマッピングに準拠している必要があります。
| メソッド | 説明 |
|---|---|
| TYPE1 evaluate(TYPE2, ...) | UDF を実行します。evaluate() メソッドはパブリックメンバーアクセスレベルを必要とします。 |
UDAF のコンパイル
UDAF は複数のデータ行に対して動作し、単一の値を返します。典型的な集計関数には SUM、COUNT、MAX、MIN などがあり、各 GROUP BY 句で指定された複数のデータ行を集計し、単一の値を返します。
MY_SUM_INT という名前の UDAF をコンパイルしたいと仮定します。組み込みの集計関数 SUM は BIGINT 型の値を返しますが、MY_SUM_INT 関数は INT データ型のリクエストパラメーターと戻りパラメーターのみをサポートします。
package com.starrocks.udf.sample;
public class SumInt {
public static class State {
int counter = 0;
public int serializeLength() { return 4; }
}
public State create() {
return new State();
}
public void destroy(State state) {
}
public final void update(State state, Integer val) {
if (val != null) {
state.counter+= val;
}
}
public void serialize(State state, java.nio.ByteBuffer buff) {
buff.putInt(state.counter);
}
public void merge(State state, java.nio.ByteBuffer buffer) {
int val = buffer.getInt();
state.counter += val;
}
public Integer finalize(State state) {
return state.counter;
}
}
ユーザー定義クラスは、次の表に示すメソッドを実装する必要があります。
NOTE
メソッドのリクエストパラメーターと戻りパラメーターのデータ型は、ステップ 6 で実行される
CREATE FUNCTIONステートメントで宣言されたものと同じであり、このトピックの「SQL データ型と Java データ型のマッピング」セクションで提供されるマッピングに準拠している必要があります。
| メソッド | 説明 |
|---|---|
| State create() | 状態を作成します。 |
| void destroy(State) | 状態を破棄します。 |
| void update(State, ...) | 状態を更新します。最初のパラメーター State に加えて、UDF 宣言で 1 つ以上のリクエストパラメーターを指定できます。 |
| void serialize(State, ByteBuffer) | 状態をバイトバッファにシリアル化します。 |
| void merge(State, ByteBuffer) | 状態をバイトバッファからデシリアライズし、バイトバッファを最初のパラメーターとして状態にマージします。 |
| TYPE finalize(State) | 状態から UDF の最終結果を取得します。 |
コンパイル 中には、次の表に示すバッファクラス java.nio.ByteBuffer とローカル変数 serializeLength も使用する必要があります。
| クラスとローカル変数 | 説明 |
|---|---|
| java.nio.ByteBuffer() | バッファクラスで、中間結果を格納します。中間結果は、実行のためにノード間で送信される際にシリアル化またはデシリアライズされる可能性があります。そのため、serializeLength 変数を使用して、中間結果のデシリアライズに許可される長さを指定する必要があります。 |
| serializeLength() | 中間結果のデシリアライズに許可される長さ。単位: バイト。このローカル変数を INT 型の値に設定します。例えば、State { int counter = 0; public int serializeLength() { return 4; }} は、中間結果が INT データ型であり、デシリアライズの長さが 4 バイトであることを指定します。これらの設定はビジネス要件に基づいて調整できます。例えば、中間結果のデータ型を LONG に指定し、デシリアライズの長さを 8 バイトにする場合は、State { long counter = 0; public int serializeLength() { return 8; }} を渡します。 |
java.nio.ByteBuffer クラスに格納された中間結果のデシリアライズに関して、次の点に注意してください。
ByteBufferクラスに依存するremaining()メソッドを呼び出して状態をデシリアライズすることはできません。ByteBufferクラスでclear()メソッドを呼び出すことはできません。serializeLengthの値は、書き込まれたデータの長さと同じでなければなりません。そうでない場合、シリアル化およびデシリアライズ中に誤った結果が生成されます。
UDWF のコンパイル
通常の集計関数とは異なり、UDWF はウィンドウと呼ばれる複数の行のセットに対して動作し、各行に対して値を返します。典型的なウィンドウ関数には OVER 句が含まれており、行を複数のセットに分割します。各セットの行に対して計算を行い、各行に対して値を返します。
MY_WINDOW_SUM_INT という名前の UDWF をコンパイルしたいと仮定します。組み込みの集計関数 SUM は BIGINT 型の値を返しますが、MY_WINDOW_SUM_INT 関数は INT データ型のリクエストパラメーターと戻りパラメーターのみをサポートします。
package com.starrocks.udf.sample;
public class WindowSumInt {
public static class State {
int counter = 0;
public int serializeLength() { return 4; }
@Override
public String toString() {
return "State{" +
"counter=" + counter +
'}';
}
}
public State create() {
return new State();
}
public void destroy(State state) {
}
public void update(State state, Integer val) {
if (val != null) {
state.counter+=val;
}
}
public void serialize(State state, java.nio.ByteBuffer buff) {
buff.putInt(state.counter);
}
public void merge(State state, java.nio.ByteBuffer buffer) {
int val = buffer.getInt();
state.counter += val;
}
public Integer finalize(State state) {
return state.counter;
}
public void reset(State state) {
state.counter = 0;
}
public void windowUpdate(State state,
int peer_group_start, int peer_group_end,
int frame_start, int frame_end,
Integer[] inputs) {
for (int i = (int)frame_start; i < (int)frame_end; ++i) {
state.counter += inputs[i];
}
}
}
ユーザー定義クラスは、UDAF に必要なメソッド (UDWF は特別な集計関数であるため) と、次の表に示す windowUpdate() メソッドを実装する必要があります。
NOTE
メソッドのリクエストパラメーターと戻りパラメーターのデータ型は、ステップ 6 で実行される
CREATE FUNCTIONステートメントで宣言されたものと同じであり、このトピックの「SQL データ型と Java データ型のマッピング」セクションで提供されるマッピングに準拠している必要があります。
| メソッド | 説明 |
|---|---|
| void windowUpdate(State state, int, int, int , int, ...) | ウィンドウのデータを更新します。UDWF の詳細については、ウィンドウ関数 を参照してください。入力として行を入力するたびに、このメソッドはウィンドウ情報を取得し、中間結果を適宜更新します。
|
UDTF のコンパイル
UDTF は 1 行のデータを読み取り、複数の値を返します。これらの値はテーブルと見なすことができます。テーブル値関数は通常、行を列に変換するために使用されます。
NOTE
StarRocks は、複数の行と 1 列からなるテーブルを返す UDTF を許可します。
MY_UDF_SPLIT という名前の UDTF をコンパイルしたいと仮定します。MY_UDF_SPLIT 関数はスペースを区切り文字として使用し、STRING データ型のリクエストパラメーターと戻りパラメーターをサポートします。
package com.starrocks.udf.sample;
public class UDFSplit{
public String[] process(String in) {
if (in == null) return null;
return in.split(" ");
}
}
ユーザー定義クラスで定義されたメソッドは、次の要件を満たす必要があります。
NOTE
メソッドのリクエストパラメーターと戻りパラメーターのデータ型は、ステップ 6 で実行される
CREATE FUNCTIONステートメントで宣言されたものと同じであり、このトピックの「SQL データ型と Java データ型のマッピング」セクションで提供されるマッピングに準拠している必要があります。
| メソッド | 説明 |
|---|---|
| TYPE[] process() | UDTF を実行し、配列を返します。 |
ステップ 4: Java プロジェクトのパッケージ化
次のコマンドを実行して Java プロジェクトをパッケージ化します。
mvn package
target フォルダーに次の JAR ファイルが生成されます: udf-1.0-SNAPSHOT.jar および udf-1.0-SNAPSHOT-jar-with-dependencies.jar。