注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
Pipeline Builder の既存の変換オプションでデータを操作できない場合や、外部の Java ライブラリを組み込みたい場合、または複数のパイプラインで再利用したい複雑なロジックがある場合は、独自のユーザー定義関数 (UDF) を作成できます。ユーザー定義関数を使用すると、Pipeline Builder やコードリポジトリで独自の任意の Java コードを実行できるようになり、バージョン管理やアップグレードが可能となります。
ユーザー定義関数は必要に応じてのみ適用し、可能な場合は Pipeline Builder または Java 変換リポジトリ の既存の変換を使用することをお勧めします。
UDF を使用するには、エンロールメントが Rubix になっている必要があります。現在、Pipeline Builder では row map および flat map UDF のみがサポートされていますが、他のタイプのサポートも追加される予定です。ユーザー定義関数は高度な機能です。パイプラインでユーザー定義関数を使用する際の影響を理解するために、以下のドキュメントを十分に確認してください。
ユーザー定義関数リポジトリを作成するには、まずリポジトリを保存したいプロジェクトに移動します。
次に、新規作成 を選択し、コードリポジトリ を選択します。Foundry UDF Definitions オプションの下で、Map UDF Definition/Implementation を選択して、リポジトリを UDF テンプレートでブートストラップします。最後に、リポジトリの初期化 を選択します。
独自のローカル環境でローカルで作業することもできます(推奨)、または Code Repositories で直接作業することもできます。
UDF の作成にはローカルでの作業をお勧めします。以下の手順に従ってローカル環境をセットアップしてください。
コマンドラインインターフェース(CLI)を開き、git clone <repo url>
を実行します。
次のコマンドを使用して、開発環境でプロジェクトを開きます。
./gradlew idea open
./gradlew eclipse open
主要な CLI コマンド:
./gradlew idea
./gradlew eclipse
../gradlew generateEddieLockfile
./gradlew test --tests examples.ExamplesTest.testExamples
./gradlew test
ローカルの IDE で作業できない場合は、コードリポジトリでファイルを直接編集し、Task Runner で主要なコマンドを実行することができます。
主要なコマンド:
generateUdfResources
generateEddieLockfile
test
UDF は、名前、入力スキーマ、出力スキーマ、および引数タイプを指定するファイルで定義されます。このファイルに基づいて Java クラスが生成され、ファイル名は Pipeline Builder で公開すると表示されます。
新しい UDF を作成するには、<YourUdfName>.yml
という名前のファイルを src/main/resources/udfs/definitions/
の下に追加し、以下のフィールドを指定します:
name
: コード生成が入力、出力、引数の説明を含むクラスを作成するために使用する UDF の名前。この名前は、デプロイメントリポジトリの deployment.yml
ファイルでこの UDF を参照するためにも使用され、Pipeline Builder の変換として表示されます。customTypes
(オプション): inputSchema
および outputSchema
の定義全体で再利用できるカスタムタイプを定義するためのブロック。詳細については、以下の カスタムタイプ セクションを参照してください。inputSchema
: UDF に対して実行する入力行が準拠する必要があるスキーマ。ランタイムがこのスキーマに選択できる場合、このスキーマよりも列が多いデータセットが受け入れられます。コード生成は、このスキーマに対して強く型付けされた入力オブジェクトを作成します。パラメーターは Pipeline Builder で表示されます。詳細については、以下の スキーマ定義セクション を参照してください。outputSchema
: この UDF によって出力される行のスキーマ。コード生成は、このスキーマに対して強く型付けされた出力オブジェクトを作成します。詳細については、以下の スキーマ定義セクション を参照してください。arguments
: UDF のパラメーターの説明。これらの引数は、Pipeline Builder または対応するデプロイメントリポジトリでユーザーによってビルド時に指定されます。コード生成は、この定義に対して強く型付けされた引数オブジェクトを作成します。リポジトリテンプレートには、src/main/resources/udfs/definitions/Multiply.yml
の下に例の定義が提供されています。以下の UDF 定義セクション で引数の詳細を読むことができます。
このリポジトリで複数の UDF を定義するには、src/main/resources/udfs/definitions
に別の定義ファイルを追加します。
UDF 定義ファイルを作成または編集したら、以下のコマンドでコード生成を実行します。コード生成は、定義ファイルから強く型付けされたオブジェクトを作成し、udf-definitions-map-udf-definition/build/generated/sources/udf/main/java
に配置します。生成されたクラスには、定義のスキーマに一致する入力および出力オブジェクト、および変換作成およびロジックのインターフェースが含まれます。また、YourUdfNameMapUdfFactoryImpl
または YourUdfNameMapUdfImpl
クラスを変更するたびにコード生成を再実行することで、Java パッケージをコンパイルし、チェックが通らない場合にエラーを提供できます。
ローカル環境:
./gradlew idea
./gradlew eclipse
コードリポジトリ:
generateUdfResources
を実行します。生成されたファイルはコードリポジトリで表示されませんが、このコマンドはコード補完を提供します。row map UDF は、1 行の入力を受け取り、入力ごとに正確に 1 行の出力を行います。row map はデフォルトの UDF タイプです。
UDF の作成と変換ロジックを実装するには、以下の内容を含む src/main/resources/java/myproject
の下に 2 つの新しい Java クラスファイルを作成します。
YourUdfNameMapUdfFactoryImpl.java
: UDF のインスタンスを作成し、実行のためのパラメーターを提供する責任があります。
YourUdfNameMapUdfFactory
インターフェースを実装する必要があります。args.config()
パラメーターを通じて利用できます。create
メソッドは、ランタイムで UDF をインスタンス化するために 1 回呼び出されます。YourUdfNameMapUdfImpl.java
: 入力オブジェクトから出力オブジェクトへの実際の変換ロジックを実装します。
YourUdfNameMapUdf
インターフェースを実装する必要があります。argument_one
は argumentOne()
という一致するメソッドを持ちます。リポジトリテンプレートには、以下に示す Java クラスの例が含まれています。これらの例を使用して、独自の実装をモデル化できます。編集後にコード生成を再実行して、問題がないことを確認してください。
src/main/java/myproject/MultiplyMapUdfFactoryImpl.java
src/main/java/myproject/MultiplyMapUdfImpl.java
flat map UDF は、1 行の入力を受け取り、入力ごとに 0、1、または複数の行を出力できます。
flat map UDF の作成と変換ロジックを実装するには、以下の例を使用して、src/main/resources/java/myproject
の下に 2 つの新しい Java クラスファイルを作成します。
YourUdfNameFlatMapUdfFactoryImpl.java
: UDF のインスタンスを作成し、実行のためのパラメーターを提供する責任があります。
YourUdfNameFlatMapUdfFactory
インターフェースを実装する必要があります。args.config()
パラメーターを通じて利用できます。create
メソッドは、ランタイムで UDF をインスタンス化するために 1 回呼び出されます。例: DuplicateRowsFlatMapUdfFactoryImpl.java
package myproject;
import com.google.auto.service.AutoService;
import com.palantir.foundry.duplicaterows.config.DuplicateRowsConfiguration;
import com.palantir.foundry.duplicaterows.DuplicateRowsFlatMapUdf;
import com.palantir.foundry.duplicaterows.DuplicateRowsFlatMapUdfAdapter;
import com.palantir.foundry.duplicaterows.DuplicateRowsFlatMapUdfFactory;
import com.palantir.foundry.udf.api.flatmap.FoundryRowFlatMapUdf;
import com.palantir.foundry.udf.api.flatmap.FoundryRowFlatMapUdfFactory;
/**
* "ChangeMe" UDFを作成するファクトリ。
*/
@AutoService(FoundryRowFlatMapUdfFactory.class)
public final class DuplicateRowsFlatMapUdfFactoryImpl implements DuplicateRowsFlatMapUdfFactory {
/**
* ランタイムでのサービスローディングには、パブリックで引数のないコンストラクタが必要です。
*/
public DuplicateRowsFlatMapUdfFactoryImpl() {}
/**
* ランタイムで使用するUDF実装を作成します。著者は、UDF実装をラップするアダプターを返す必要があります。
*/
@Override
public final FoundryRowFlatMapUdf create(FoundryRowFlatMapUdfFactory.Arguments<DuplicateRowsConfiguration> args) {
DuplicateRowsFlatMapUdf impl = new DuplicateRowsFlatMapUdfImpl(args.config());
return new DuplicateRowsFlatMapUdfAdapter(impl);
}
}
YourUdfNameFlatMapUdfImpl.java
:入力オブジェクトから出力オブジェクトへの実際の変換ロジックを実装します。
YourUdfNameFlatMapUdf
インターフェースを実装する必要があります。argument_one
は、対応するメソッド argumentOne()
を持つでしょう。例:DuplicateRowsFlatMapUdfImpl.java
package myproject;
import com.palantir.foundry.duplicaterows.config.DuplicateRowsConfiguration;
import com.palantir.foundry.duplicaterows.DuplicateRowsFlatMapUdf;
import com.palantir.foundry.duplicaterows.input.InputRow;
import com.palantir.foundry.duplicaterows.output.OutputRow;
import com.palantir.foundry.udf.api.flatmap.Collector;
/**
* "DuplicateRows" UDFのロジック実装。
*/
public final class DuplicateRowsFlatMapUdfImpl implements DuplicateRowsFlatMapUdf {
private final DuplicateRowsConfiguration config;
public DuplicateRowsFlatMapUdfImpl(DuplicateRowsConfiguration config) {
this.config = config;
}
@Override
public void flatMap(Context ctx, InputRow input, Collector<OutputRow> out) throws Exception {
OutputRow outputRow = OutputRow.create(ctx.getRowBuilderFactory())
.key(input.key())
.value(input.value());
// 入力された行を2回収集してすべての行を複製する
out.collect(outputRow);
out.collect(outputRow);
}
}
例は、特定のユーザー定義関数に関する情報を提供し、ユニットテストフレームワークとして使用できます。これらの例は、UDFをパイプラインにインポートするときにPipeline Builderにも表示され、UDFの機能を理解しやすくなります。Examplesフレームワークでは、行マップユーザー定義関数のみがサポートされており、フラットマップユーザー定義関数の場合は、独自のJUnitテストを作成することをお勧めします。ただし、これらはBuilder変換ドキュメントには表示されません。
Exampleテストケースの定義は任意ですが、Pipeline Builderの変換ドキュメント内で表示されるため、UDFの機能を理解する上で非常に役立ちます。Exampleを含めたくない場合は、src/test
フォルダーを削除し、次のセクションに進んでください。
Map UDF Definition/Implementation
リポジトリのデフォルトテンプレートには、サンプルのExamples
クラスが含まれています。
src/test/java/examples/registry/MultiplyExamples.java
UDFの新しい例を定義するには、Examples
インターフェースを実装するexamples.registry
の下にクラスを作成します。インターフェースの期待値については、MultiplyExamples.java
を参照してください。
例の実装のname()
メソッドは、テスト対象のUDFと同じ名前を返さなければなりません。そうでない場合、例は正しく登録されず、Pipeline Builderインターフェースに公開されません。
実際の例は、examples()
メソッドで定義する必要があります。以下は、デフォルトのMultiplyExamples
クラスで定義されたexamples()
メソッドの実装です。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
@Override public List<UdfExample<SettableMultiplicand, Product, MultiplyConfiguration>> examples() { return List.of(UdfExample.<SettableMultiplicand, Product, MultiplyConfiguration>builder() // この例の一意のID .id(ExampleId.of("baseCase")) // オプション: この例が何を表しているかの説明 .description("値を2倍にします。") // この例がPipeline Builderで目立つかデフォルトの可視性を持つべきか .visibility(ExampleVisibility.DEFAULT) // この例が示すケース(基本ケース、nullケース、エッジケース) .category(ExampleCategory.BASE) // この例が示すUDFに渡す引数 .configuration(MultiplyConfiguration.builder().multiplier(2.0d).build()) // UDFへの例の入力行 .input( SettableMultiplicand.create(ctx().getRowBuilderFactory()) .key("key") .value(1.5d), SettableMultiplicand.create(ctx().getRowBuilderFactory()) .key("key") .value(3.0d)) // 例の出力行(UDFが呼び出された後の入力行がどのように見えるべきか) .output( Product.create(ctx().getRowBuilderFactory()).key("key").value(3.0d), Product.create(ctx().getRowBuilderFactory()).key("key").value(6.0d)) .build()); }
例を定義したら、すべてのテストを実行できます。
./gradlew test --tests examples.ExamplesTest.testExamples
./gradlew test
test
を実行する。ExamplesTest.testExamples()
を実行すると、examples.registry
パッケージ内のExamples
の実装がロードされ、その有効性がチェックされます。上に示したMultiplyExamples.examples()
メソッドの場合、input
行(["key", 1.5d]
と["key", 3.0d]
)に対してconfiguration
(multiplier = 2.0d
)で提供された引数を使用してUDFを実行し、指定された出力行(["key", 3.0d]
と["key", 6.0d]
)が得られることを確認します。
開発作業が完了したら、必ず変更をコミットしてください。
ローカル環境:以下のコマンドを順に実行して、変更をFoundry Code Repositoriesにプッシュします。
git add .
git commit -m "<commit message>"
git push
Code Repositories:コミットを選択します。
UDFは、Pipeline Builder(推奨)または標準のUDFデプロイリポジトリを通じてデプロイできます。
現在、Pipeline Builderへの公開は、Rubixエンロールメントのrow mapとflat mapのUDFに対してのみ利用可能です。他のUDFタイプへのサポートは追加される予定です。
UDFをPipeline Builderに公開するには、以下の手順を実行します:
generateEddieLockfile
を入力して、リポジトリ内の各UDFをPipeline Builderに登録するための一意の識別子を含むファイルを生成します。generateEddieLockfile
コマンドは、UDF名に基づいてランダムな一意の識別子を返します。公開後にUDFの名前を変更すると、コードは以前のUDFを上書きするのではなく、新しいUDFとして公開されます。同様に、ロックファイルを削除して再生成すると、UDFは新しい一意の識別子を受け取り、新しいUDFとして登録されます。古い名前、新しい名前、新しいロックファイルを含むUDFのすべてのバージョンは、Pipeline BuilderでUDFをインポートする際に表示されるポップアップウィンドウに表示されます。
0.0.1
)。ロックファイルが存在しない場合、UDFはパイプラインにインポートするためのPipeline Builderへの登録が行われません。ロックファイルを生成するには、generateEddieLockfile
を実行します。Code Repositoriesで作業している場合はロックファイルは必要ありません。
Add
を選択します。あなたのUDFはPipeline Builderの変換ピッカーに表示され、パイプラインの他の変換と同様に使用できます。UDFの最初のバージョン以降に新しい変更や修正をデプロイする場合、定義YMLへの編集を含む、上記の実装と公開の手順を繰り返します。そして、Pipeline Builderパイプライン内で:
UDFをCode Repositoriesに公開するには、以下の手順を実行します:
UDFの最初のバージョン以降に新しい変更や修正をデプロイする場合、定義YAMLへの編集を含む、上記の実装と公開の手順を繰り返します。その後、デプロイリポジトリのbuild.gradleの依存関係で参照されるタグバージョンを更新します。
UDF定義のYAMLファイルは次の形式をとります:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
name: # (string) このUDFの名前。 customTypes: # (optional<CustomTypes>) 現在のUDF定義内で参照可能なカスタムタイプ。詳細については下の[カスタムタイプ](#カスタムタイプ)セクションを参照してください。 inputSchema: # (Schema) このUDFに対して実行するために入力行が遵守しなければならないスキーマ。詳細については下の[スキーマYAML定義](#schema-yaml-definition-documentation)セクションを参照してください。 outputSchema: # (Schema) このUDFの出力行のスキーマ。詳細については下の[スキーマYAML定義](#schema-yaml-definition-documentation)セクションを参照してください。 arguments: # (map<ArgumentId, Argument>) ビルド時に指定され、デプロイ時にUDF著者に提供される引数。 # (ArgumentId: string) 引数のローカルに一意な名前。 [ArgumentId]: required: # (Boolean) この引数が必須かどうか description: # (optional<string>) この引数がどのように使用されるかをユーザーが理解するためのPipeline Builderに表示される説明。 type: # (FieldType) 引数のデータタイプ。 # Keyed Process UDFに固有 keyColumns: # (list<string>) パーティショニングのためのキーカラム。カラムは入力スキーマに存在しなければならない。 eventTimeColumn: # (string) イベント時間を含むカラム。このカラムは入力スキーマに存在しなければならない。 # 他の全てのUDFタイプに固有 description: # (optional<string>) このUDFの説明がPipeline Builderに表示され、ユーザーがこのUDFが何であるかを理解するのを助けます。 type: # (optional<Type>) このUDFのタイプは、UDFのロジックがどのように定義され、実行されるかを決定します。 # Type enumの許可される値: # - DEFAULT # - ASYNC_DEPLOYED_APP_UDF # - ASYNC_CUSTOM_UDF # - FLAT_MAP_UDF
UDFとその引数のオプショナルな description
フィールドを含めることを強く推奨します。特に、UDFがPipeline Builderでデプロイされる場合、これらの説明はユーザーがUDFの機能と、引数の変更が出力にどのように影響するかを理解するのに役立ちます。
UDFのスキーマは次のような形になります:
Copied!1 2 3 4 5 6 7 8
name: # (文字列) コード生成が生成オブジェクトに接頭辞を付けるために使用する、プロジェクト固有のスキーマ名。 description: # (任意<文字列>) このスキーマの説明。Pipeline BuilderでこのUDFを使用する際のコンテキストとして表示されます。 fields: # (list<Field>) このスキーマに含まれるすべてのフィールド - name: # (文字列) このフィールドのローカルにユニークな名前 nullable: # (ブーリアン) 値がnullかもしれないかどうか type: # (FieldType) このフィールドのデータ型と、関連するメタデータ。例えば: type: double double: {}
上記のように、Pipeline Builderでの入力と出力の期待をユーザーが理解するために、各スキーマに説明を含めることを強く推奨します。
FoundryのすべてのデータセットタイプはUDF FieldType
によってサポートされ、次の形状をとります:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
# 配列 type: array array: elementType: type: <FieldType> nullable: # (ブーリアン) 配列の値がnullである可能性があるかどうか # バイナリ type: binary binary: {} # ブーリアン type: boolean boolean: {} # バイト type: byte byte: {} # カスタム type: custom custom: # (文字列) `customTypes` ブロックで定義されたカスタム型の名前。 # 日付 type: date date: {} # 10進数 type: decimal decimal: precision: # (整数) 1から38までの整数(両端含む)。 scale: # (整数) 0からprecisionまでの整数(両端含む)。 # ダブル type: double double: {} # 浮動小数点数 type: float float: {} # 整数 type: integer integer: {} # ロング type: long long: {} # マップ type: map map: keyType: type: <FieldType> nullable: # (ブーリアン) マップのキーがnullである可能性があるかどうか valueType: type: <FieldType> nullable: # (ブーリアン) マップの値がnullである可能性があるかどうか # ショート type: short short: {} # 文字列 type: string string: {} # タイムスタンプ type: timestamp timestamp: {} # 構造体 type: struct struct: fields: [] # (list<Field>)
UDF内のカスタムタイプは、スキーマ定義全体で繰り返し参照できるタイプを定義することができます。
Copied!1 2 3 4 5 6 7 8 9
customTypes: # このブロックはオプションで、カスタムタイプを使用していない場合は含める必要はありません。 types: # この`types`ブロックを忘れないでください!このブロックは、`customTypes`の追加フィールド、例えば`imports`のサポートを後で追加するために存在します。 customType: # このカスタムタイプのためのユニークな名前をキーにするべきです # (FieldType) このカスタムフィールドのデータタイプと、関連するメタデータ。例えば: type: double double: {} anotherCustomType: # カスタムタイプを定義します # ...
カスタム型は、プリミティブ、配列、マップ、および構造体を含む任意のフィールド型のエイリアスとして定義できます。ただし一般的に、カスタム型は、入力および出力スキーマ全体で繰り返される構造体型を定義する際に最も役立ちます。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
customTypes: types: customStruct: type: struct struct: fields: - name: "doubleField" nullable: false type: type: double double: {} # customTypes: カスタム型の定義 # types: 各カスタム型のリスト # customStruct: カスタム構造体の名前 # type: struct, 構造体を示す # struct: 構造体の定義 # fields: 構造体のフィールドのリスト # name: "doubleField", フィールド名 # nullable: false, このフィールドはnullを許可しない # type: double, このフィールドの型は倍精度浮動小数点数 # double: {}, 倍精度浮動小数点数の詳細設定(空の場合はデフォルト設定)
カスタムタイプは、スキーマ内で以下のように参照できます:
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
inputSchema: name: "Input" fields: - name: "inputStruct" nullable: false type: type: custom custom: customStruct # 上で定義されたカスタム型の名前 outputSchema: name: "Output" fields: - name: "outputStruct" nullable: false type: type: custom custom: customStruct # 上で定義されたカスタム型の名前
上記のようにUDFスキーマ内でカスタム構造体が参照されると、コード生成はInputStruct
とOutputStruct
のための別々のクラスを生成しません。代わりに、入力と出力の両方のオブジェクトが参照されているカスタムタイプ(以下の例ではCustomStruct
)のコード生成クラスを参照します:
Copied!1 2 3 4 5 6 7
public final Output map(FoundryRowMapUdf.Context ctx, Input input) { // 入力構造体フィールドを出力構造体フィールドに渡すno-op UDF Output output = Output.create(ctx.getRowBuilderFactory()); CustomStruct inputStruct = input.inputStruct(); // `CustomStruct` を返す output.outputStruct(inputStruct); // `CustomStruct` 引数が必要 return output; }
カスタム型を使用する際の大きな利点は、カスタム構造体です。入力コード生成型と出力コード生成型が構造的に同じ場合、追加の作業を行わずに変換できます。
このセクションでは、ユーザー定義関数の実装とデプロイメントに関する一般的な問題と、デバッグ手順について説明します。
通常、これは解析できないUDF定義YAMLが原因です。
以下の手順で解決してください:
Code Assist task failed
ポップアップで識別可能なエラーがあるかどうか、Code Assistを確認してください。Code Assist running
メッセージにマウスオーバーし、Refreshを選択してCode Assistを更新してください。./gradlew idea
または./gradlew eclipse
コマンドを実行して、コンソールに表示されるエラーを確認してください。一度公開されると、UDFはPipeline Builderから非公開にすることはできません。ロックファイルエントリを削除し、リポジトリでgenerateEddieLockfile
コマンドを再実行すると、UDFに新しいIDが付与され、Pipeline Builderの変換リストのUDFsセクションに2回表示されるようになります。
./gradlew idea open
または./gradlew eclipse open
がCould not open proj generic class cache for build file <build.gradle> ... Unsupported class file major version
エラーで失敗する場合、Gradleと互換性のないJavaバージョンを実行している可能性があります。
以下の手順で解決してください:
./gradlew --version
を実行して、JavaとGradleのバージョンを確認してください。export JAVA_HOME=<jdk install directory>/Contents/Home/
に設定してください。これで、openコマンドを正常に実行できるはずです。
Failed to start Java server
エラーが表示される場合や、Task Runnerでコマンドを実行できない場合は、以下を確認してください: