注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
Pipeline Builder の既存のデータ加工オプションではデータを操作できない場合、外部の Java または Python ライブラリを組み込みたい場合、あるいはパイプライン間で再利用したい複雑なロジックがある場合は、独自のユーザー定義関数 (UDF) を作成できます。ユーザー定義関数を使用すると、Pipeline Builder または Code Repositories で独自の任意の Java または Python コードを実行し、そのコードのバージョン管理やアップグレードを行えます。Pipeline Builder での Python UDF の使用について詳しくはこちら。
ユーザー定義関数は必要な場合にのみ適用し、可能な限り Pipeline Builder または Java トランスフォームリポジトリの既存のデータ加工を使用することを推奨します。
UDF を使用するには、エンロールメントが Palantir のクラウドホスト型インフラストラクチャ上にある必要があります。Pipeline Builder では現在、行マップ、フラットマップ、キー付きプロセス、およびキー付きコプロセスの UDF がサポートされています。非同期 UDF はまだサポートされていません。ユーザー定義関数は高度な機能です。パイプラインでユーザー定義関数を使用することによる影響を理解するため、以下のドキュメントを十分に確認してください。
ユーザー定義関数リポジトリを作成するには、まずリポジトリを保存するプロジェクトに移動します。
次に、新規を選択し、コードリポジトリを選択します。Foundry UDF 定義の選択肢で、マップ UDF 定義/実装を選択して、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 で作業できない場合は、Code Repositories でファイルを直接編集し、Task Runner で主なコマンドを実行できます。

主なコマンド:
generateUdfResourcesgenerateEddieLockfiletestUDF は、その名前、入力スキーマ、出力スキーマ、および引数のデータ型を指定するファイルで定義します。このファイルに基づいて Java クラスが生成され、公開すると Pipeline Builder にファイル名が表示されます。
新しい UDF を作成するには、src/main/resources/udfs/definitions/ に <YourUdfName>.yml という名前のファイルを追加し、次のフィールドを記述します。
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 定義セクションを参照してください。
src/main/resources/udfs/definitions に別の定義ファイルを追加することで、このリポジトリ内に複数の UDF を定義できます。
UDF 定義ファイルを作成または編集したら、以下のコマンドでコード生成を実行します。コード生成では、定義ファイルから厳密に型付けされたオブジェクトが作成され、udf-definitions-map-udf-definition/build/generated/sources/udf/main/java に配置されます。生成されるクラスには、定義のスキーマに一致する入力および出力オブジェクトと、変換の作成およびロジックを実装するためのインターフェースが含まれます。また、YourUdfNameMapUdfFactoryImpl または YourUdfNameMapUdfImpl クラスを変更するたびにコード生成を再実行してください。これにより、Java パッケージがコンパイルされ、チェックに合格しない場合はエラーが表示されます。
ローカル環境:
./gradlew idea./gradlew eclipseCode Repositories:
generateUdfResources を実行します。生成されたファイルは Code Repositories に表示されませんが、このコマンドによりコード補完が利用可能になります。
行マップ UDF は1行を入力として受け取り、入力ごとに必ず1行を出力します。行マップはデフォルトの UDF の種類です。
UDF の作成および変換ロジックを実装するには、src/main/resources/java/myproject に次の2つの新しい Java クラスファイルを作成します。
YourUdfNameMapUdfFactoryImpl.java: UDF のインスタンスを作成し、実行に必要なパラメーターを渡します。
YourUdfNameMapUdfFactory インターフェースを実装します。args.config() パラメーターを通じて利用できます。create メソッドは実行時に1回呼び出され、UDF をインスタンス化します。YourUdfNameMapUdfImpl.java: 入力オブジェクトから出力オブジェクトへの実際の変換ロジックを実装します。
YourUdfNameMapUdf インターフェースを実装します。argument_one には argumentOne() という対応するメソッドがあります。リポジトリテンプレートには、以下の Java クラスの実装例が含まれています。これらの例を参考に独自の実装を作成できます。編集後にコード生成を再実行し、問題がないことを確認してください。
src/main/java/myproject/MultiplyMapUdfFactoryImpl.javasrc/main/java/myproject/MultiplyMapUdfImpl.javaフラットマップ UDF は1行を入力として受け取り、入力ごとに0行、1行、または多数の行を出力できます。
フラットマップ UDF の作成および変換ロジックを実装するには、以下の例を参考に、src/main/resources/java/myproject に2つの新しい Java クラスファイルを作成します。
YourUdfNameFlatMapUdfFactoryImpl.java: UDF のインスタンスを作成し、実行に必要なパラメーターを渡します。
YourUdfNameFlatMapUdfFactory インターフェースを実装します。args.config() パラメーターを通じて利用できます。create メソッドは実行時に1回呼び出され、UDF をインスタンス化します。例: DuplicateRowsFlatMapUdfFactoryImpl.java
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 30package 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 コンストラクターが必要です。 */ 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
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 28package 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 のデータ加工ドキュメントには表示されません。
例のテストケースの定義は任意ですが、Pipeline Builder のデータ加工ドキュメントに表示され、ユーザーが UDF の動作をより簡単に理解できるようになるため、強く推奨します。例を含めたくない場合は、src/test フォルダーを削除して次のセクションに進みます。
Map UDF Definition/Implementation リポジトリのデフォルトテンプレートに、Examples クラスのサンプルがあります。
src/test/java/examples/registry/MultiplyExamples.javaUDF の新しい例を定義するには、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("Multiply values by 2.") // Pipeline Builder でのこの例の表示設定を PROMINENT または DEFAULT のどちらにするか .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 testtest を実行します。ExamplesTest.testExamples() を実行すると、examples.registry パッケージ内の Examples の実装が読み込まれ、その妥当性がチェックされます。上記の MultiplyExamples.examples() メソッドの場合、configuration に指定された引数(multiplier = 2.0d)を使用して、2行の input(["key", 1.5d] と ["key", 3.0d])に対して UDF を実行し、指定された出力行(["key", 3.0d] と ["key", 6.0d])になることを確認します。
開発作業が完了したら、必ず変更をコミットしてください。
ローカル環境:以下のコマンドを順番に実行して、変更を Foundry Code Repositories にプッシュします。
git add .git commit -m "<commit message>"git pushCode Repositories: コミットを選択します。
UDF は Pipeline Builder(推奨)または標準の UDF デプロイ用リポジトリを通じてデプロイできます。
Pipeline Builder への公開は、現在 Rubix エンロールメント上の row map、flat map、keyed process、および keyed co-process UDF で利用可能です。非同期 UDF(async custom および async deployed app)はまだサポートされていません。
以下の手順に従って、UDF を Pipeline Builder に公開します。
generateEddieLockfile を入力します。これにより、Pipeline Builder がリポジトリ内の各 UDF を登録するための一意の識別子を含むファイルが生成されます。generateEddieLockfile コマンドは、UDF 名に基づくランダムな一意の識別子を返します。公開後に UDF の名前を変更すると、コードは以前の UDF を上書きせず、新しい UDF として公開されます。同様に、ロックファイルを削除して再生成すると、UDF に新しい一意の識別子が割り当てられ、新しい UDF として登録されます。古い名前、新しい名前、新しいロックファイルを含む UDF のすべてのバージョンは、Pipeline Builder で UDF をインポートするときに表示されるポップアップウィンドウに表示されます。
0.0.1)のタグを付けます。ロックファイルが存在しない場合、UDF はパイプラインにインポートするために Pipeline Builder に登録されません。ロックファイルを生成するには、generateEddieLockfile を実行します。Code Repositories で作業している場合、ロックファイルは不要です。
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 21name: # (string) この UDF の名前。 customTypes: # (optional<CustomTypes>) 現在の UDF 定義内で参照できるカスタム型。詳細は、以下の[カスタム型](#custom-types)セクションを参照してください。 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 列挙型で許可される値: # - DEFAULT # - ASYNC_DEPLOYED_APP_UDF # - ASYNC_CUSTOM_UDF # - FLAT_MAP_UDF
特に UDF を Pipeline Builder にデプロイする場合は、UDF とその引数に任意の description フィールドを含めることを強く推奨します。これらの説明により、UDF の動作や、引数の変更が出力にどのような影響を与えるかについて、ユーザーの理解を深められます。
UDF スキーマは、次の形式になります。
Copied!1 2 3 4 5 6 7 8name: # (string) コード生成で生成されるオブジェクトの接頭辞として使用する、プロジェクト内で一意のスキーマ名。 description: # (optional<string>) Pipeline Builder でこの UDF を使用するときに、コンテキストとして表示される、このスキーマの説明。 fields: # (list<Field>) このスキーマのすべてのフィールド - name: # (string) このフィールドのローカルで一意の名前 nullable: # (boolean) 値が null になる可能性があるかどうか type: # (FieldType) このフィールドのデータ型と、関連するメタデータ。たとえば: type: double double: {}
前述のとおり、Pipeline Builder のユーザーが入力と出力の満たすべき条件を理解できるように、各スキーマに説明を含めることを強く推奨します。
UDF の FieldType は、すべての Foundry データセットのデータ型をサポートしており、次の形式になります。
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: # (boolean) 配列の値が null になる可能性があるかどうか # バイナリ type: binary binary: {} # ブール値 type: boolean boolean: {} # Byte type: byte byte: {} # カスタム type: custom custom: # (string) `customTypes` ブロックで定義されたカスタム型の名前。 # 日付 type: date date: {} # Decimal type: decimal decimal: precision: # (integer) 1以上38以下の整数。 scale: # (integer) 0以上 precision 以下の整数。 # Double type: double double: {} # Float type: float float: {} # Integer type: integer integer: {} # Long type: long long: {} # マップ type: map map: keyType: type: <FieldType> nullable: # (boolean) マップのキーが null になる可能性があるかどうか valueType: type: <FieldType> nullable: # (boolean) マップの値が null になる可能性があるかどうか # Short 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 9customTypes: # このブロックは任意で、カスタム型を使用していない場合は含める必要はありません。 types: # この `types` ブロックを忘れないでください。このブロックは、`imports` などの追加フィールドを `customTypes` で将来サポートする場合に備えて用意されています。 customType: # キーはこのカスタム型の一意の名前にする必要があります # (FieldType) このカスタムフィールドのデータ型と、関連するメタデータ。たとえば: type: double double: {} anotherCustomType: # カスタム型を定義します # ...
カスタム型は、プリミティブ、配列、マップ、構造体を含む任意のフィールドのデータ型のエイリアスとして定義できます。ただし、一般的には、入力および出力スキーマ全体で繰り返し使用される構造体のデータ型を定義する場合に、カスタム型が最も役立ちます。
Copied!1 2 3 4 5 6 7 8 9 10 11customTypes: types: customStruct: type: struct struct: fields: - name: "doubleField" nullable: false type: type: double double: {}
カスタム型は、スキーマ内で次のように参照できます。
Copied!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16inputSchema: 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 7public final Output map(FoundryRowMapUdf.Context ctx, Input input) { // 入力の構造体フィールドを出力の構造体フィールドに渡す、何も処理しない UDF Output output = Output.create(ctx.getRowBuilderFactory()); CustomStruct inputStruct = input.inputStruct(); // `CustomStruct` を返します output.outputStruct(inputStruct); // `CustomStruct` 引数を想定しています return output; }
カスタム構造体は、カスタム型を使用する大きな利点です。入力と出力のコード生成データ型が構造的に同じ場合、入力のコード生成データ型を出力のコード生成データ型に変換するための追加作業は不要です。
このセクションでは、ユーザー定義関数の実装とデプロイに関する一般的な問題と、デバッグの手順を説明します。
通常、これは解析できない UDF 定義 YAML が原因です。
以下の手順に従って解決します。
./gradlew idea または ./gradlew eclipse コマンドを実行して、コンソールに出力されるエラーを確認します。一度公開した UDF は、Pipeline Builder から公開を取り消すことができません。ロックファイルのエントリを削除し、リポジトリで generateEddieLockfile コマンドを再実行すると、UDF に新しい ID が割り当てられ、Pipeline Builder のトランスフォームリストの UDF(ユーザー定義関数) セクションに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 でコマンドを実行できない場合は、以下を確認してください。
