すべてのプロダクト
Search
ドキュメントセンター

E-MapReduce:Java UDF

最終更新日:Apr 15, 2025

このトピックでは、ユーザー定義関数 (UDF) を記述および使用する方法について説明します。

背景情報

StarRocks 2.2.0 以降では、Java で記述された UDF がサポートされています。

StarRocks 3.0 以降では、グローバル UDF がサポートされています。CREATE、SHOW、DROP などの SQL 文に GLOBAL キーワードを追加するだけで、SQL 文がグローバルに有効になります。各データベースに対して SQL 文を 1 つずつ実行する必要はありません。ビジネスシナリオに基づいて UDF を構成し、StarRocks の関数機能を拡張できます。

StarRocks は、以下のタイプの UDF をサポートしています。

  • ユーザー定義スカラー関数 (スカラー UDF)

  • ユーザー定義集計関数 (UDAF)

  • ユーザー定義ウィンドウ関数 (UDWF)

  • ユーザー定義テーブル値関数 (UDTF)

前提条件

StarRocks の Java UDF 機能を使用する前に、以下の要件が満たされていることを確認する必要があります。

  • 関連する Java プロジェクトを作成および記述するために、Apache Maven をインストールします。

  • サーバーに Java 開発キット (JDK) 1.8 をインストールします。

  • UDF 機能を有効にします。EMR Serverless StarRocks インスタンスの詳細ページの [インスタンス構成] タブの FE セクションで、enable_udf パラメーターを TRUE に設定し、インスタンスを再起動して構成を有効にします。

データ型のマッピング

SQL 型

Java 型

BOOLEAN

java.lang.Boolean

TINYINT

java.lang.Byte

SMALLINT

java.lang.Short

INT

java.lang.Integer

BIGINT

java.lang.Long

FLOAT

java.lang.Float

DOUBLE

java.lang.Double

STRING/VARCHAR

java.lang.String

UDF の開発と使用

Maven プロジェクトを作成し、対応する機能を Java で記述する必要があります。

手順 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 を使用してデータをクエリする場合、各データ行は最終的に結果セットに行ごとに表示されます。典型的なスカラー UDF には、UPPERLOWERROUNDABS などがあります。

次の例は、JSON データを抽出する方法を示しています。たとえば、JSON データのフィールドの値は、JSON オブジェクトではなく JSON 文字列である場合があります。この場合、JSON 文字列を抽出する場合、SQL 文で関数 GET_JSON_STRING のネスト呼び出し、つまり GET_JSON_STRING(GET_JSON_STRING('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key"), "$.k0") が必要になります。

SQL 文を簡略化するために、MY_UDF_JSON_GET('{"key":"{\\"k0\\":\\"v0\\"}"}', "$.key.k0") など、JSON 文字列を直接抽出する UDF を開発できます。

package com.starrocks.udf.sample;
import com.alibaba.fastjson.JSONPath;

public class UDFJsonGet {
    public final String evaluate(String jsonObj, String key) {
        if (obj == null || key == null) return null; // オブジェクトまたはキーが null の場合は null を返す
        try {
            // JSONPath ライブラリは、フィールドの値が JSON 文字列であっても完全に展開できます。
            return JSONPath.read(jsonObj, key).toString();
        } catch (Exception e) {
            return null; // 例外が発生した場合は null を返す
        }
    }
}

ユーザー定義クラスは、以下のメソッドを実装する必要があります。

説明

メソッドのリクエストパラメーターとレスポンスパラメーターのデータ型は、手順 6 の CREATE FUNCTION 文で宣言されているものと同じである必要があり、メソッドのパラメーターと CREATE FUNCTION 文で宣言されているパラメーター間のデータ型マッピングは、「データ型のマッピング」に準拠している必要があります。

メソッド

説明

TYPE1 evaluate(TYPE2, ...)

evaluate メソッドは、UDF の呼び出しエントリポイントであり、パブリックメンバーメソッドである必要があります。

UDAF を開発する

UDAF を使用して、複数のデータ行に対して操作を実行し、単一の結果行を生成できます。典型的な UDAF には、SUMCOUNTMAXMIN などがあります。これらの関数は、各 GROUP BY グループの複数のデータ行を集計し、単一の結果行を生成するために使用できます。

次の例では、関数 MY_SUM_INT を使用します。戻り値のデータ型が BIGINT である組み込み関数 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(); // State オブジェクトを作成
    }

    public void destroy(State 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; // 最終結果を返す
    }
}

ユーザー定義クラスは、以下のメソッドを実装する必要があります。

説明

メソッドの入力パラメーターとレスポンスパラメーターのデータ型は、手順 6 の CREATE FUNCTION 文で宣言されているものと同じである必要があり、メソッドのパラメーターと CREATE FUNCTION 文で宣言されているパラメーター間のデータ型マッピングは、「データ型のマッピング」に準拠している必要があります。

メソッド

説明

State create()

State を作成します。

void destroy(State)

State を破棄します。

void update(State, ...)

State を更新します。最初のパラメーターは State で、残りのパラメーターは関数によって宣言された入力パラメーターです。単一の入力パラメーターまたは複数のパラメーターを指定できます。

void serialize(State, ByteBuffer)

State をシリアル化します。

void merge(State, ByteBuffer)

State をマージおよび逆シリアル化します。

TYPE finalize(State)

State を使用して関数の最終結果を取得します。

UDAF を開発する場合は、バッファークラス java.nio.ByteBuffer を使用して中間結果を保存および表現し、ローカル変数 serializeLength を使用して中間結果のシリアル化された長さを指定する必要があります。

クラスとローカル変数

説明

java.nio.ByteBuffer()

中間結果を保存するために使用されるバッファークラス。中間結果は、異なる実行ノード間で転送されるときにシリアル化および逆シリアル化されるため、serializeLength を使用して中間結果のシリアル化された長さを指定する必要があります。

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() メソッドを使用して State を逆シリアル化することはできません。

  • ByteBuffer で clear() メソッドを呼び出すことはできません。

  • serializeLength の値は、実際に書き込まれるデータの長さと一致している必要があります。そうでない場合、シリアル化および逆シリアル化中に正しくない結果が得られる可能性があります。

UDWF を開発する

UDWF は、ユーザー定義ウィンドウ関数の略語です。一般的な UDAF とは異なり、UDWF は一連の行 (ウィンドウ) の計算を実行し、各行の結果を返すために使用できます。ほとんどの場合、UDWF には、データ行を複数のグループに分割するために使用できる OVER 句が含まれています。UDWF は、各データ行が存在するグループ (ウィンドウ) に基づいて計算を実行し、各行の結果を返します。

次の例では、関数 MY_WINDOW_SUM_INT を使用します。戻り値のデータ型が BIGINT である組み込み関数 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(); // State オブジェクトを作成
    }

    public void destroy(State 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 に必要なメソッドと windowUpdate() メソッドを実装する必要があります。UDWF は特別な UDAF です。

説明

メソッドのリクエストパラメーターとレスポンスパラメーターのデータ型は、手順 6 の CREATE FUNCTION 文で宣言されているものと同じである必要があり、メソッドのパラメーターと CREATE FUNCTION 文で宣言されているパラメーター間のデータ型マッピングは、「データ型のマッピング」に準拠している必要があります。

実装する必要があるその他のメソッド

メソッド

説明

void windowUpdate(State state, int, int, int , int, ...)

ウィンドウデータを更新します。UDWF の詳細については、「Window_function」をご参照ください。データ行を入力すると、対応するウィンドウ情報が取得され、中間結果が更新されます。

  • peer_group_start: 現在のパーティションの開始を指定します。

    パーティション: OVER 句で PARTITION BY を使用して、パーティションキー列を指定できます。同じパーティションキー列値を持つ行は、同じパーティションにあると見なされます。

  • peer_group_end: 現在のパーティションの終了を指定します。

  • frame_start: 現在のウィンドウフレームの開始を指定します。

    ウィンドウフレーム: ウィンドウフレーム句は、ウィンドウ関数が操作する行の範囲を指定します。現在の行が参照として使用され、現在の行の前後の特定の数の行が、ウィンドウ関数が操作するオブジェクトになります。たとえば、ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING は、操作範囲が現在の行、現在の行の前の行、および現在の行の後の行であることを示します。

  • frame_end: 現在のウィンドウフレームの終了を指定します。

  • inputs: ウィンドウ内の入力データを指定します。入力データはラッパークラス配列です。ラッパークラスは、入力データのデータ型に対応している必要があります。この例では、入力データのデータ型は INT です。したがって、ラッパークラス配列は Integer[] です。

UDTF を開発する

UDTF は、データ行を読み取り、テーブルと見なすことができる複数の値を生成するために使用できます。UDTF は、行から列への変換を実装するためによく使用されます。

説明

UDTF は、単一の列で複数の行を返すことのみをサポートしています。

次の例では、関数 MY_UDF_SPLIT を使用します。関数 MY_UDF_SPLIT は、スペースを区切り文字として使用して文字列を分割することをサポートしています。入力パラメーターとレスポンスパラメーターのデータ型は STRING です。

package com.starrocks.udf.sample;

public class UDFSplit{
    public String[] process(String in) { // 入力文字列を処理するメソッド
        if (in == null) return null; // 入力が null の場合は null を返す
        return in.split(" "); // スペースで分割して配列を返す
    }
}

ユーザー定義クラスは、以下のメソッドを実装する必要があります。

説明

メソッドのリクエストパラメーターとレスポンスパラメーターのデータ型は、手順 6 の CREATE FUNCTION 文で宣言されているものと同じである必要があり、メソッドのパラメーターと CREATE FUNCTION 文で宣言されているパラメーター間のデータ型マッピングは、「データ型のマッピング」に準拠している必要があります。

メソッド

説明

TYPE[] process()

process() メソッドは、UDTF の呼び出しエントリポイントであり、配列を返す必要があります。

手順 4: Java プロジェクトをパッケージ化する

次のコマンドを実行して、Java プロジェクトをパッケージ化します。

mvn package

target ディレクトリに、udf-1.0-SNAPSHOT.jarudf-1.0-SNAPSHOT-jar-with-dependencies.jar の 2 つのファイルが生成されます。

手順 5: プロジェクトをアップロードする

ファイル udf-1.0-SNAPSHOT-jar-with-dependencies.jar を Object Storage Service (OSS) にアップロードし、JAR パッケージにパブリック読み取り権限を付与します。詳細については、「シンプルアップロード」および「バケット ACL」をご参照ください。

説明

手順 6 では、フロントエンド (FE) ノードが UDF の JAR パッケージを検証し、検証値を計算します。バックエンド (BE) ノードは、UDF の JAR パッケージをダウンロードして実行します。

手順 6: StarRocks で UDF を作成する

StarRocks は、データベースレベルとグローバルレベルの両方の名前空間の UDF を提供します。

  • UDF の可視性に関する特別な要件がない場合は、グローバル UDF を作成できます。グローバル UDF を参照する場合、カタログとデータベースをプレフィックスとして使用する必要なく、関数名を直接呼び出すことができるため、アクセスがより便利になります。

  • UDF の可視性に関する特別な要件がある場合、または異なるデータベースに同じ名前の UDF を作成する場合は、データベースに UDF を作成できます。この場合、セッションが UDF が属するデータベース内にある場合は、関数名を直接呼び出すことができます。セッションが別のカタログまたはデータベース内にある場合は、catalog.database.function など、カタログとデータベースをプレフィックスとして含める必要があります。

説明

グローバル UDF を作成するには、システムレベルの CREATE GLOBAL FUNCTION 文を実行する権限が必要です。データベースレベルの UDF を作成するには、データベースレベルの CREATE FUNCTION 文を実行する権限が必要です。UDF を使用するには、UDF に対する USAGE 権限が必要です。必要な権限を付与する方法については、「GRANT」をご参照ください。

JAR パッケージをアップロードした後、ビジネス要件に基づいて StarRocks で対応する UDF を作成する必要があります。グローバル UDF を作成するには、SQL 文に GLOBAL キーワードを含めるだけで済みます。

構文

CREATE [GLOBAL][AGGREGATE | TABLE] FUNCTION function_name(arg_type [, ...])
RETURNS return_type
[PROPERTIES ("key" = "value" [, ...]) ]

パラメーター

パラメーター

必須

説明

GLOBAL

いいえ

グローバル UDF を作成する場合は、このキーワードを指定する必要があります。このパラメーターは、StarRocks 3.0 以降でサポートされています。

AGGREGATE

いいえ

UDAF または UDWF を作成する場合は、このキーワードを指定する必要があります。

TABLE

いいえ

UDTF を作成する場合は、このキーワードを指定する必要があります。

function_name

はい

関数名。db1.my_func など、データベース名を含めることができます。関数名にデータベース名が含まれている場合、UDF は対応するデータベースに作成されます。そうでない場合、UDF は現在のデータベースに作成されます。新しい関数とそのパラメーターの両方に、ターゲットデータベースにある既存の関数と同じ名前を付けることはできません。そうしようとすると、関数の作成に失敗します。ただし、関数名が同じでパラメーターが異なる場合は、作成は成功します。

arg_type

はい

関数のパラメーターのデータ型。サポートされているデータ型の詳細については、「データ型のマッピング」をご参照ください。

return_type

はい

関数の戻り値のデータ型。サポートされているデータ型の詳細については、「データ型のマッピング」をご参照ください。

properties

はい

関数のプロパティ。作成するさまざまなタイプの UDF のプロパティを構成する必要があります。詳細と例については、以下のセクションをご参照ください。

スカラー UDF を作成する

次のコマンドを実行して、StarRocks にスカラー UDF を作成します。

CREATE [GLOBAL] FUNCTION MY_UDF_JSON_GET(string, string) 
RETURNS string
PROPERTIES (
    "symbol" = "com.starrocks.udf.sample.UDFJsonGet", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

パラメーター

説明

symbol

UDF が属するプロジェクトのクラス名。クラス名は、<package_name>.<class_name> 形式で指定します。

type

UDF のタイプ。値を StarrocksJar に設定します。これは、Java ベースの UDF であることを示します。

file

UDF が属する JAR パッケージの HTTP パス。値を OSS の内部エンドポイントに対応する HTTP URL に設定します。形式は http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/<jar_package_name> です。

UDAF を作成する

次のコマンドを実行して、StarRocks に UDAF を作成します。

CREATE [GLOBAL] AGGREGATE FUNCTION MY_SUM_INT(INT) 
RETURNS INT
PROPERTIES 
( 
    "symbol" = "com.starrocks.udf.sample.SumInt", 
    "type" = "StarrocksJar",
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

PROPERTIES のパラメーターの説明は、「スカラー UDF を作成する」のプロパティの説明と同じです。

UDWF を作成する

次のコマンドを実行して、手順 3 で使用した UDWF を StarRocks に作成します。

CREATE [GLOBAL] AGGREGATE FUNCTION MY_WINDOW_SUM_INT(Int)
RETURNS Int
PROPERTIES 
(
    "analytic" = "true",
    "symbol" = "com.starrocks.udf.sample.WindowSumInt", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

analytic: 作成される関数がウィンドウ関数であるかどうかを指定します。値を true に設定します。残りのパラメーターの説明は、「スカラー UDF を作成する」で説明されているものと同じです。

UDTF を作成する

次のコマンドを実行して、手順 3 で使用した UDTF を StarRocks に作成します。

CREATE [GLOBAL] TABLE FUNCTION MY_UDF_SPLIT(string)
RETURNS string
PROPERTIES 
(
    "symbol" = "com.starrocks.udf.sample.UDFSplit", 
    "type" = "StarrocksJar", 
    "file" = "http://<YourBucketName>.oss-cn-xxxx-internal.aliyuncs.com/<YourPath>/udf-1.0-SNAPSHOT-jar-with-dependencies.jar"
);

PROPERTIES のパラメーターの説明は、「スカラー UDF を作成する」のプロパティの説明と同じです。

手順 7: UDF を使用する

UDF を作成した後、開発した UDF をテストおよび使用できます。

スカラー UDF を使用する

次のコマンドを実行して、手順 6 で作成したスカラー UDF を使用します。

SELECT MY_UDF_JSON_GET('{"key":"{\\"in\\":2}"}', '$.key.in');

UDAF を使用する

次のコマンドを実行して、手順 6 で作成した UDAF を使用します。

SELECT MY_SUM_INT(col1);

UDWF を使用する

次のコマンドを実行して、手順 6 で作成した UDWF を使用します。

SELECT MY_WINDOW_SUM_INT(intcol) 
            OVER (PARTITION BY intcol2
                  ORDER BY intcol3
                  ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
FROM test_basic;

UDTF を使用する

次のコマンドを実行して、手順 6 で作成した UDTF を使用します。

-- 列 a、b、c1 を含むテーブル t1 が存在すると仮定します。
SELECT t1.a,t1.b,t1.c1 FROM t1;
> output:
1,2.1,"hello world"
2,2.2,"hello UDTF."

-- MY_UDF_SPLIT() 関数を使用します。
SELECT t1.a,t1.b, MY_UDF_SPLIT FROM t1, MY_UDF_SPLIT(t1.c1); 
> output:
1,2.1,"hello"
1,2.1,"world"
2,2.2,"hello"
2,2.2,"UDTF."
説明
  • 最初の MY_UDF_SPLIT は、関数 MY_UDF_SPLIT の呼び出し後に生成される列エイリアスです。

  • AS t2(f1) メソッドを使用して、UDTF によって返されるテーブルのテーブルエイリアスと列エイリアスを指定することはできません。

UDF 情報を表示する

次のコマンドを実行して、UDF 情報を表示します。

SHOW [GLOBAL] FUNCTIONS;

UDF を削除する

次のコマンドを実行して、指定した UDF を削除します。

DROP [GLOBAL] FUNCTION <function_name>(arg_type [, ...]);

FAQ

Q: UDF を開発するときに静的変数を使用できますか? 異なる UDF の静的変数は互いに影響しますか?

A: UDF を開発するときに静的変数を使用できます。異なる UDF の静的変数は、UDF のクラス名が同じであっても互いに分離されており、互いに影響しません。