Java UDF
バージョン 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
とは異なり、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
とは異なり、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 は、UDTF が複数の行と 1 列からなるテーブルを返すことを許可します。
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。
ステップ 5: Java プロジェクトをアップロード
JAR ファイル udf-1.0-SNAPSHOT-jar-with-dependencies.jar を、StarRocks クラスター内のすべての FEs および BEs からアクセス可能な HTTP サーバーにアップロードします。次に、次のコマンドを実行してファイルをデプロイします。
mvn deploy
Python を使用して簡単な HTTP サーバーをセットアップし、その HTTP サーバーに JAR ファイルをアップロードすることができます。
NOTE
ステップ 6 では、FEs が UDF のコードを含む JAR ファイルをチェックし、チェックサムを計算し、BEs が JAR ファイルをダウンロードして実行します。