================== 演算子リファレンス ================== この文書では、Asakusa Frameworkが提供する演算子について個々に紹介します。 Asakusa DSLの演算子 =================== Asakusa FrameworkのOperator DSLとFlow DSLでは、それぞれ演算子を利用しています。 前者のOperator DSLでは、主に演算子を「作成」します。 Asakusa Frameworkが提供するのは演算子の「種類」であって、それぞれの演算子の細かな挙動はOperator DSLでカスタマイズできます。 後者のFlow DSLでは、主に演算子を「複合」します。 Operator DSLで作成する演算子はデータフローの「部品」であり、これらを複合して複雑なデータフローを構築することになります。 レコードとグループ ------------------ それぞれの演算子が処理するデータの単位は、「レコード」または「グループ」のいずれかです。 レコード データフローに流れるデータ1つ分。 Asakusa DSLでは「データモデルオブジェクト」として表現される。 グループ レコードを特定のキーでグループ化したもの。 Asakusa DSLでは、データモデルオブジェクトのリストや反復子などで表現される。 キー注釈 ~~~~~~~~ Operator DSLでは上記のグループを作成するために、 ``Key`` [#]_ という注釈を多用します。 これは、グループ化のための方法や、グループ内での整列順序を指定するためのものです。 詳しくは :doc:`user-guide` - :ref:`dsl-key-annotation` を参照してください。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.model.Key` 演算子の分類 ------------ それぞれの演算子は、処理の内容ごとにいくつかに分類できます。 `フロー制御演算子`_ データの流れそのものを制御する。 `データ操作演算子`_ データフローに流れるデータを加工する。 `結合演算子`_ 複数のレコードを結合する。 `集計演算子`_ レコードをグループごとに集計する。 `特殊演算子`_ 上記の分類に含まれない特別な演算子。 コア演算子とユーザー演算子 -------------------------- 演算子を実装方法の面から2種類に分類できます。 コア演算子 Asakusa Frameworkが **APIとして** 提供する演算子。 この種類の演算子はOperator DSLで記述する必要はなく、Flow DSLから直接利用できる。 ユーザー演算子 演算子の種類ごとに演算子注釈のみを提供している演算子。 この種類の演算子を利用するには、Operator DSLで必要な演算子メソッドを定義する必要がある。 なお、ユーザー演算子にもメソッド本体の実装が必要なものと不要なものに分かれています。 Flow DSLでコア演算子を利用する場合、一般的に ``CoreOperatorFactory`` [#]_ というファクトリクラスが提供するメソッドを使用します。 また、上記ファクトリクラスと同名のクラスメソッド群を持つ ``CoreOperators`` [#]_ も利用可能です。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.flow.util.CoreOperatorFactory` .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.flow.util.CoreOperators` 演算子の性能特性 ---------------- それぞれの演算子はその性能特性から、いくつかの種類に分類できます。 `Extract` 系演算子 もっとも単純な演算子の種類で、入力データからデータモデルオブジェクトを一つずつ取り出して処理を行います。 この種類は並列分散処理が比較的容易で、データサイズが大きくなっても、コンピューターのCPUの個数やクラスターの台数などを増大させることで性能を向上させられるケースが多くあります。 ほとんどのコア演算子や、更新演算子、抽出演算子などはこの `Extract` 系演算子に含まれています。 `CoGroup` 系演算子 入力データを指定した条件でグループごとに分割し、それぞれのグループを一つずつ取り出して処理を行います。 この種類は、グループごとにデータを分割する操作において、データのコピー・移動や待ち合わせが発生してしまいます。 そのため、分散処理を行う際にはこの処理がバッチ全体のボトルネックになる場合があります。 また、分割したグループの大きさに偏りがある場合、処理が特定のノードに集中してしまう場合もあります。 グループ整列演算子や、グループ結合演算子などがこの `CoGroup` 系演算子に含まれています。 `Join` 系演算子 入力データに、別の入力データを結合して処理を行います。 この演算子は「トランザクション入力」と「マスタ入力」という2つの入力をとり、マスタ入力のデータが十分に小さな場合に `Extract` 系演算子と同程度の性能を発揮します。 そうでなく、マスタ入力のデータが大きな場合にはおよそ `CoGroup` 系演算子程度の性能になります。 マスタ結合演算子や、マスタ付き更新演算子などがこの `Join` 系演算子に含まれています。 `Fold` 系演算子 入力データを指定した条件でグループごとに分割し、それぞれのグループを集計する処理を行います。 この種類は `CoGroup` 系演算子と似通っていますが、コンパイラの設定や、処理系によっては `CoGroup` 系よりも効率よく動作します。 単純集計演算子や、畳み込み演算子などがこの `Fold` 系演算子に含まれています。 演算子の表記 ------------ この文書での演算子に関する表記を説明します。 コア演算子の表記 ~~~~~~~~~~~~~~~~ コア演算子は次のような表で表記します。 .. list-table:: コア演算子の表記 :widths: 3 7 :header-rows: 1 * - 項目 - 内容 * - 分類 - "コア" * - 導入 - この演算子が導入されたフレームワークのバージョン * - メソッド - ``CoreOperatorFactory`` 内のメソッド名 * - 性能特性 - `Extract` | `CoGroup` | `Join` | `Fold` * - 入力数 - この演算子への入力数 * - 出力数 - この演算子からの出力数 ユーザー演算子の表記 ~~~~~~~~~~~~~~~~~~~~ ユーザー演算子は次のような表で表記します。 .. list-table:: ユーザー演算子の表記 :widths: 3 7 :header-rows: 1 * - 項目 - 内容 * - 分類 - "ユーザー" * - 導入 - この演算子が導入されたフレームワークのバージョン * - 演算子注釈 - 演算子注釈の名前 * - 本体の実装 - "必要"または"不要" [#]_ * - 性能特性 - `Extract` | `CoGroup` | `Join` | `Fold` * - 入力数 - この演算子への入力数 * - 出力数 - この演算子からの出力数 * - ビュー引数 - この演算子にビュー引数を指定可能かどうか [#]_ * - 値引数 - この演算子に値引数を指定可能かどうか * - 型引数 - この演算子に型引数を指定可能かどうか [#]_ .. [#] 本体の実装が不要なユーザー演算子は、抽象メソッドとして宣言します。 .. [#] ビュー引数については、 :doc:`view-api` を参照してください。 .. [#] 型引数は多相演算子で使用します。詳しくは :doc:`generic-dataflow` を参照してください。 入出力の表記 ~~~~~~~~~~~~ それぞれの入出力は、次のような項目を表記します。 .. list-table:: 入出力の表記 :widths: 3 7 :header-rows: 1 * - 項目 - 内容 * - 分類 - "入力" または "出力" * - 名前 - 標準的な名前 * - 単位 - 処理単位 ("レコード", "グループ") * - 型 - データの種類 * - 備考 - 備考欄 演算子メソッドの表記 ~~~~~~~~~~~~~~~~~~~~ 演算子メソッドの形式は、次のような項目を表記します。 .. list-table:: 演算子メソッドの表記 :widths: 3 7 :header-rows: 1 * - 項目 - 内容 * - 分類 - 返戻値または引数1~ * - 対応 - 演算子の入出力との対応 * - 型 - 指定する型 * - キー - ``Key`` の指定 * - 備考 - 備考欄 「型」には主に次のようなものがあります。 モデル データモデル型 リスト データモデル型を要素に取る ``List`` [#]_ ビュー データモデル型を要素に取る ``View`` および ``GroupView`` [#]_ 結果 データモデル型を要素に取る ``Result`` [#]_ プリミティブ Javaのプリミティブ型、または文字列型 列挙型 Javaの列挙型 ( ``enum`` ) ``Javaの型`` Javaの対応する型 .. [#] ``java.util.List`` .. [#] :asakusafw-javadoc:`com.asakusafw.runtime.core.View` および :asakusafw-javadoc:`com.asakusafw.runtime.core.GroupView` ビューについては、 :doc:`view-api` を参照してください。 .. [#] :asakusafw-javadoc:`com.asakusafw.runtime.core.Result` 演算子の出力となるデータモデルオブジェクトを保持します。 ``add`` メソッドにより複数のオブジェクトを追加することができます。 .. _flow-control-operators: フロー制御演算子 ================ フロー制御系の演算子は、主にデータフローの構造を制御するための演算子です。 .. _branch-operator: 分岐演算子 ---------- レコードを入力にとって、レコードの内容に応じていずれかの出力にレコードを振り分ける演算子です。 「条件に応じて出力先を変える」などの用途に利用できます。 .. list-table:: 分岐演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Branch`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 任意 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 .. list-table:: 分岐演算子の入出力 :widths: 1 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - (任意) - レコード - inと同様 - 任意の個数を指定可 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Branch` 分岐演算子の実装 ~~~~~~~~~~~~~~~~ 分岐演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 分岐演算子の実装 :widths: 2 2 2 1 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 全出力 - 列挙型 - 不可 - 列挙定数ごとに出力 * - 引数1 - 入力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は一つの引数を取り、分岐先を表現する列挙定数を返します。 返戻型に指定する列挙型は、分岐先の出力名を表しています。 メソッドから列挙定数を返すと、その時点の引数に渡された入力が、返した列挙定数に対応する出力に渡されます。 引数には同メソッドで宣言した型変数を利用できますが、 戻り値の型に型変数を含めることはできません。 分岐演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型には分岐先を表現する列挙型を指定する * その列挙型は、public として宣言されている * その列挙型は、一つ以上の列挙定数を持つ * 以下の引数を宣言する * データモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract .. attention:: 分岐演算子の内部では、入力の内容を変更しないようにしてください。 そのような動作を期待する場合、 `更新演算子`_ を利用するようにしてください。 以下は実装例です。 .. code-block:: java public abstract class OperatorClass { ... /** * レコードの状態ごとに処理を分岐する。 * @param hoge 対象のレコード * @return 分岐先を表すオブジェクト */ @Branch public Status select(Hoge hoge) { int price = hoge.getPrice(); if (price < 0) { return Status.ERROR; } if (price >= 1000000) { return Status.EXPENSIVE; } return Status.CHEAP; } /** * 値段に関するレコードの状態。 */ public enum Status { /** * 高い。 */ EXPENSIVE, /** * 安い。 */ CHEAP, /** * エラー。 */ ERROR, } ... } .. ** .. _confluent-operator: 合流演算子 ---------- 複数の入力を合流して、単一の出力にまとめる演算子です。 `分岐演算子`_ の逆の動作を行い、SQLの ``UNION ALL`` のように動きます。 .. list-table:: 合流演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.1 * - メソッド - ``confluent`` * - 性能特性 - `Extract` * - 入力数 - 任意 * - 出力数 - 1 .. list-table:: 合流演算子の入出力 :widths: 1 2 2 2 4 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - (任意) - レコード - 任意 - 任意の入力数だが、全て同じ型 * - 出力 - out - レコード - 入力と同様 - 合流演算子の実装 ~~~~~~~~~~~~~~~~ 合流演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in1, in2, in3; Out out; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); Confluent op = core.confluent(in1, in2, in3); out.add(op); } .. _replicate-operator: 複製演算子 ---------- レコードを入力にとって、同じ内容のレコードを複数の出力にそれぞれ出力する演算子です。 .. list-table:: 複製演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.1 * - メソッド - 特殊 [#]_ * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 任意 .. list-table:: 複製演算子の入出力 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - (任意) - レコード - inと同様 - 任意の個数 .. [#] データの複製は同一の出力を何度も利用するだけで実現できるため、特別なメソッドを用意していません。 複製演算子の実装 ~~~~~~~~~~~~~~~~ 複製演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは演算子からの出力を複数回利用すると、複製演算子と同じ効果を得られます。 .. code-block:: java Out out1, out2, out3; @Override protected void describe() { ... SomeOperator op = ...; out1.add(op.out); out2.add(op.out); out3.add(op.out); } .. _data-manipulation-operators: データ操作演算子 ================ データ操作系の演算子は、主にレコードを加工したり変形したりするための演算子です。 .. _update-operator: 更新演算子 ---------- レコードの内容を更新する演算子です。 レコードの型そのものを変更したい場合には、 `変換演算子`_ を利用します。 .. list-table:: 更新演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Update`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 .. list-table:: 更新演算子の入出力 :widths: 2 2 2 2 2 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - inと同様 - .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Update` 更新演算子の実装 ~~~~~~~~~~~~~~~~ 更新演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 更新演算子の実装 :widths: 2 2 2 2 2 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - なし - ``void`` - 不可 - * - 引数1 - 入出力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、単一の入力に流れるデータの項目を変更し、出力に流します。 対象のメソッドは一つの引数を取り、メソッドの本体で引数の内容を変更するプログラムを記述します。 メソッド内で引数のデータモデルオブジェクトを破壊的に変更すると、変更結果が演算子の出力になります。 引数には同メソッドで宣言した型変数を利用できます。 更新演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * データモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * レコードの値に100を設定する。 * @param hoge 更新するレコード */ @Update public void edit(Hoge hoge) { hoge.setValue(100); } .. ** .. _convert-operator: 変換演算子 ---------- レコードを別の型のレコードに変換する演算子です。 主に「レコードから別のレコードを作成する」という目的で利用します。 .. list-table:: 変換演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Convert`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 2 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 .. list-table:: 変換演算子の入出力 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - 任意 - 変換後のデータ * - 出力 - original - レコード - inと同様 - 変換前のデータ .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Convert` .. hint:: レコードから不要なプロパティを除去したり、新たなプロパティを追加する場合は `射影演算子`_ や `拡張演算子`_ 、 `再構築演算子`_ の利用を推奨しています。 変換演算子の実装 ~~~~~~~~~~~~~~~~ 変換演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 変換演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - モデル - 不可 - 型引数は指定不可 * - 引数1 - 入力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、入力されたデータを他の種類のデータに変換し、出力に流します。 一つの引数を取り、変換して別のデータモデルオブジェクトを返すプログラムを記述します。 メソッドから返したデータモデルオブジェクトが演算子の出力になります。 このデータモデルオブジェクトは、演算子クラスと一緒に一度だけインスタンス化して再利用することが可能です。 この演算子をフロー部品やジョブフローで使用する際に、出力 ``original`` は他の演算子等と結線されていなくてもエラーにならない、という特性を持ちます。 ただし、Asakusa on MapReduce 利用時には ``original`` の結線を省略することはできません。 引数には同メソッドで宣言した型変数を利用できますが、 戻り値の型に型変数を含めることはできません。 変換演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型に変換後のデータモデルオブジェクト型を指定する * 以下の引数を宣言する * データモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract このフレームワークを正しく利用する限り、この注釈を付与するメソッドはスレッド安全となります。 ただし、同メソッドが共有データを操作したり、または共有データを操作する別のメソッドを起動したりする場合についてはスレッド安全ではありません。 以下は実装例です。 .. code-block:: java // スレッド安全なので変換後のオブジェクトは再利用可能 private final Foo foo = new Foo(); /** * レコードHogeを等価なFooに変換して返す。 * @param hoge 変換するレコード * @return 変換後のレコード */ @Convert public Foo toFoo(Hoge hoge) { foo.setValue(hoge.getValue()); return foo; } .. ** .. _extend-operator: 拡張演算子 ---------- レコードに新たなプロパティを追加した別の型に変換する演算子です。 計算のために一時的にプロパティを追加したい場合などに利用することを想定しています。 .. list-table:: 拡張演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.2 * - メソッド - ``extend`` * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 .. list-table:: 拡張演算子の入出力 :widths: 1 1 1 1 2 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - 任意 - 入出力の型に制約あり .. attention:: 拡張演算子を利用する場合、変換後のレコードには変換前の型にある全てのプロパティが定義されている必要があります。 つまり、この演算子は「プロパティを増やす」場合のみに利用できます。 拡張演算子の実装 ~~~~~~~~~~~~~~~~ 拡張演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in; Out out; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); Extend op = core.extend(in, Foo.class); out.add(op); } 上記の例では、 ``Hoge`` が持つすべてのプロパティを ``Foo`` も持っていなければなりません。 そうでない場合、コンパイル時にエラーとなります。 .. _project-operator: 射影演算子 ---------- レコードから不要なプロパティを除去した別の型に変換する演算子です。 計算のために一時的に導入していたプロパティなどを除去したり、出力前に適切な型に変換することを想定としています。 .. list-table:: 射影演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.2 * - メソッド - ``project`` * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 .. list-table:: 射影演算子の入出力 :widths: 1 1 1 1 2 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - 任意 - 入出力の型に制約あり .. attention:: 射影演算子を利用する場合、変換前の型には変換後のレコードにある全てのプロパティが定義されている必要があります。 つまり、この演算子は「プロパティを減らす」場合のみに利用できます。 射影演算子の実装 ~~~~~~~~~~~~~~~~ 射影演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in; Out out; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); Project op = core.project(in, Hoge.class); out.add(op); } 上記の例では、 ``Hoge`` が持つすべてのプロパティを ``Foo`` も持っていなければなりません。 そうでない場合、コンパイル時にエラーとなります。 .. _restructure-operator: 再構築演算子 ------------ レコードの内容を別の型に移し替える演算子です。 元の型と移し替える先の型のうち、両者に共通するプロパティのみをコピーします。 .. list-table:: 再構築演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.2.1 * - メソッド - ``restructure`` * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 .. list-table:: 再構築演算子の入出力 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - 任意 - .. hint:: 再構築演算子は、 `拡張演算子`_ や `射影演算子`_ の制約を緩めたものです。 これらの演算子が利用できる場面では通常再構築演算子も利用できますが、データ構造がむやみに変更された際にコンパイラによるチェックが甘くなります。 拡張演算子や射影演算子で十分である場合、できるだけそちらを利用することを推奨します。 再構築演算子の実装 ~~~~~~~~~~~~~~~~~~ 再構築演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in; Out out; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); Restructure op = core.restructure(in, Hoge.class); out.add(op); } 上記の例では、 ``Hoge`` と ``Foo`` に共通するプロパティのみが、 ``Hoge`` ( ``in`` ) から ``Foo`` にコピーされます。 .. _extract-operator: 抽出演算子 ---------- レコードに含まれるデータを抽出して、複数のレコードを生成する演算子です。 主に「レコードを分解して別のレコードを作成する」という目的で利用します。 .. list-table:: 抽出演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.2.1 * - 演算子注釈 - ``Extract`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 任意 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 .. list-table:: 抽出演算子の入出力 :widths: 1 1 1 1 2 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - (任意) - レコード - 任意 - 任意個数を指定可 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Extract` 抽出演算子の実装 ~~~~~~~~~~~~~~~~ 抽出演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 抽出演算子の実装 :widths: 1 1 1 1 2 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``void`` - 不可 - * - 引数1 - 入力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 以降の引数 - 各出力 - 結果 - 不可 - 任意の個数 * - 値引数 - なし - プリミティブ - 不可 - この演算子は、入力されたデータから任意のデータを抽出し、それぞれ出力に流します。 一つのデータモデルオブジェクト型の引数と、 複数の結果オブジェクト型の引数を取り、引数から抽出した任意のデータモデルオブジェクトを結果オブジェクトに出力を行うプログラムを記述します。 出力は任意個の結果で、メソッド内で同じ結果に対して複数回の結果を指定することも可能です。 引数には同メソッドで宣言した型変数を利用できますが、全ての結果オブジェクト型の出力に型変数を含める場合には、入力に同様の型変数を指定してある必要があります。 抽出演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * データモデルオブジェクト型の引数 * 一つ以上の結果型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java // スレッド安全なので抽出結果のオブジェクトは再利用可能 private final A a = new A(); private final B b = new B(); /** * レコードに含まれるそれぞれのフィールドを抽出し、出力する。 * @param hoge 抽出対象のデータモデル * @param aResult aの抽出結果 * @param bResult bの抽出結果 */ @Extract public void extractFields( Hoge hoge, Result aResult, Result bResult) { a.setValue(hoge.getA()); aResult.add(a); b.setValue(hoge.getB0()); bResult.add(b); b.setValue(hoge.getB1()); bResult.add(b); } .. ** .. _join-operators: 結合演算子 ========== 結合系の演算子は、複数のレコードを突き合わせたり結合したりするための演算子です。 .. _master-check-operator: マスタ確認演算子 ---------------- レコードと同様のキーを持つレコードを別の入力から探し、存在する場合としない場合で出力を振り分ける演算子です。 .. list-table:: マスタ確認演算子の概要 :widths: 4 6 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``MasterCheck`` [#]_ * - 本体の実装 - 不要 * - 性能特性 - `Join` * - 入力数 - 2 * - 出力数 - 2 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - マスタ選択を利用可能 .. list-table:: マスタ確認演算子の入出力 :widths: 1 2 2 2 4 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - master - グループ - 任意 - グループ化を指定 * - 入力 - tx - レコード - 任意 - グループ化を指定 * - 出力 - found - レコード - txと同様 - マスタが見つかったもの * - 出力 - missed - レコード - txと同様 - マスタが見つからなかったもの .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.MasterCheck` マスタ確認演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~ マスタ確認演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: マスタ確認演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``boolean`` - 不可 - * - 引数1 - 入力 - モデル - 必須 - マスタデータの入力 * - 引数2 - 入力 - モデル - 必須 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、トランザクションデータに対応するマスタデータを引き当てて確認し、マスタデータを発見できたものと発見できなかったものに分けて出力に流します。 このメソッドには本体を指定せず、抽象メソッドとして宣言します。 対象のメソッドは結合対象の二つのデータモデルオブジェクト型の引数を取ります。 このとき最初の引数は、マスタデータなど結合条件に対してユニークであるようなデータモデルオブジェクトである必要があります。 戻り値型にはboolean型を指定します。マスタデータを発見できた場合は ``ture`` 、 発見できなかった場合は ``false`` を返却します。 データモデルオブジェクト型の引数にはそれぞれ ``Key`` 注釈を指定し、 ``group`` でグループ化のためのプロパティ名を指定します。 それぞれのプロパティ列が完全に一致するものが結合対象になります。 整列のためのプロパティ名および整列方向に関する動作は規定されません。 引数には同メソッドで宣言した型変数を利用できます。 マスタ確認演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にboolean型を指定する * 以下の引数を宣言する * 結合対象のデータモデルオブジェクト型の引数 (マスタデータ)、さらに ``Key`` 注釈でグループ化のための情報を指定する * 結合対象のデータモデルオブジェクト型の引数、さらに ``Key`` 注釈でグループ化のための情報を指定する * 以下の修飾子を付与する * abstract * 以下の修飾子は付与しない * (特になし) 以下は実装例です。 .. code-block:: java /** * レコードHogeTrnに対するHogeMstが存在する場合に{@code true}を返す。 * @param master マスタデータ * @param tx トランザクションデータ * @return HogeMstが存在する場合のみtrue */ @MasterCheck public abstract boolean exists( @Key(group = "id") HogeMst master, @Key(group = "masterId") HogeTrn tx); .. ** また、この演算子注釈に ``selection`` を指定することで、非等価結合条件を記述することも可能です。 詳しくは `マスタ選択`_ を参照して下さい。 .. _master-join-operator: マスタ結合演算子 ---------------- レコードと同様のキーを持つレコードを別の入力から探し、それらを結合したレコードを出力する演算子です。 この演算子は、結合モデル [#]_ のレコードを構築します。 入力はそれぞれ結合モデルの元になったデータモデルを指定し、結合に成功した場合に結合モデルが出力され、失敗した場合には元になったレコードが出力されます。 また、結合条件や結合方法は結合モデルに指定したものを利用します。 .. list-table:: マスタ結合演算子の概要 :widths: 4 6 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``MasterJoin`` [#]_ * - 本体の実装 - 不要 * - 性能特性 - `Join` * - 入力数 - 2 * - 出力数 - 2 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定不可 * - 備考 - マスタ選択を利用可能 .. list-table:: マスタ結合演算子の入出力 :widths: 1 1 1 1 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - master - グループ - 任意 - * - 入力 - tx - レコード - 任意 - * - 出力 - joined - レコード - 任意 - 結合結果、結合モデルを指定 * - 出力 - missed - レコード - txと同様 - マスタが見つからなかったもの .. [#] 結合モデルについては :doc:`../dmdl/user-guide` を参照してください。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.MasterJoin` マスタ結合演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~ マスタ結合演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: マスタ結合演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - モデル - 不可 - 結合モデルのみ * - 引数1 - 入力 - モデル - 不可 - マスタデータの入力 * - 引数2 - 入力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、トランザクションデータに対して対応するマスタデータを結合し、結合結果のレコードを出力に流します。 このメソッドには本体を指定せず、抽象メソッドとして宣言します。 対象のメソッドは、結合対象の二つのデータモデルオブジェクト型の引数を取ります。 このとき最初の引数は、マスタデータなど結合条件に対してユニークであるようなデータモデルオブジェクトである必要があります。 戻り値型には結合結果を表す結合モデルの型を指定します。 結合モデルの型は、必ず結合対象の二つのデータモデルオブジェクトを結合したものである必要があります。 この演算子の結合条件や結合方法は、結合モデル型の注釈 [#]_ に全て埋め込まれています。 そのため、他のマスタ操作系の演算子とは異なり、 ``Key`` の指定は不要です。 この演算子メソッドには型引数を定義できません。 マスタ結合演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型に結合結果となるモデル型を指定する * 以下の引数を宣言する * 結合対象のデータモデルオブジェクト型の引数 (マスタデータ) * 結合対象のデータモデルオブジェクト型の引数 * 以下の修飾子を付与する * abstract * 以下の修飾子は付与しない * (特になし) 以下は実装例です。 .. code-block:: java /** * レコードHogeMstとHogeTrnを結合し、結合結果のHogeを返す。 * @param master マスタデータ * @param tx トランザクションデータ * @return 結合結果 */ @MasterJoin public abstract Hoge join(HogeMst master, HogeTrn tx); .. ** また、この演算子注釈に ``selection`` を指定することで、非等価結合条件を記述することも可能です。 詳しくは `マスタ選択`_ を参照して下さい。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.model.Joined` .. _master-branch-operator: マスタ分岐演算子 ---------------- レコードと同様のキーを持つレコードを別の入力から探し、両方の情報を元にそれぞれの出力にレコードを振り分ける演算子です。 この演算子は、マスタを引き当てつつ `分岐演算子`_ と同等の処理を行います。 .. list-table:: マスタ分岐演算子の概要 :widths: 4 6 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``MasterBranch`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Join` * - 入力数 - 2 * - 出力数 - 任意 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - マスタ選択を利用可能 .. list-table:: マスタ分岐の入出力 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - master - グループ - 任意 - グループ化を指定 * - 入力 - tx - レコード - 任意 - グループ化を指定 * - 出力 - (任意) - レコード - txと同様 - 任意の個数を指定可 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.MasterBranch` マスタ分岐演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~ マスタ分岐演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: マスタ分岐演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 全出力 - 列挙型 - 不可 - 列挙定数ごとに出力 * - 引数1 - 入力 - モデル - 必須 - マスタデータの入力 * - 引数2 - 入力 - モデル - 必須 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、トランザクションデータに対応するマスタデータを引き当て、それらの内容に応じてトランザクションデータをそれぞれの出力に振り分けます。 対象のメソッドは結合対象の二つのデータモデルオブジェクト型の引数を取ります。 このとき最初の引数は、マスタデータなど結合条件に対してユニークであるようなデータモデルオブジェクトである必要があります。 戻り値型には、分岐先を表現する列挙定数を返します。この列挙型は、分岐先の出力名を表しています。 メソッドから列挙定数を返すと、その時点の引数に渡された入力が、返した列挙定数に対応する出力に渡されます。 データモデルオブジェクト型の引数にはそれぞれ ``Key`` 注釈を指定し、 ``group`` でグループ化のためのプロパティ名を指定します。 それぞれのプロパティ列が完全に一致するものが結合対象になります。 整列のためのプロパティ名および整列方向に関する動作は規定されません。 引数には同メソッドで宣言した型変数を利用できます。 マスタ分岐演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型には分岐先を表現する列挙型を指定する * その列挙型は、public として宣言されている * その列挙型は、一つ以上の列挙定数を持つ * 以下の引数を宣言する * 結合対象のデータモデルオブジェクト型の引数 (マスタデータ)、さらに ``Key`` 注釈でグループ化のための情報を指定する * 結合対象のデータモデルオブジェクト型の引数、さらに ``Key`` 注釈でグループ化のための情報を指定する * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract .. attention:: マスタ分岐演算子の内部では、入力の内容を変更しないようにしてください。 そのような動作を期待する場合、 `マスタつき更新演算子`_ と `分岐演算子`_ を組み合わせて利用するようにしてください。 .. attention:: この演算子の引数1 (マスタデータの入力) には、引き当てるマスタが見つからなかった場合に ``null`` が渡されます。 これは他のマスタ系の演算子とは異なる動作ですので、注意が必要です。 以下は実装例です。 .. code-block:: java public abstract class OperatorClass { ... /** * レコードの状態ごとに処理を分岐する。 * @param master マスタデータ、存在しない場合は{@code null} * @param tx トランザクションデータ * @return 分岐先を表すオブジェクト */ @MasterBranch public Status branchWithJoin( @Key(group = "id") ItemMst master, @Key(group = "itemId") HogeTrn tx) { if (master == null) { return Status.ERROR; } int price = master.getPrice(); if (price < 0) { return Status.ERROR; } if (price >= 1000000) { return Status.EXPENSIVE; } return Status.CHEAP; } /** * 値段に関するレコードの状態。 */ public enum Status { /** * 高い。 */ EXPENSIVE, /** * 安い。 */ CHEAP, /** * エラー。 */ ERROR, } ... } .. ** また、この演算子注釈に ``selection`` を指定することで、非等価結合条件を記述することも可能です。 詳しくは `マスタ選択`_ を参照して下さい。 .. _master-join-update-operator: マスタつき更新演算子 -------------------- レコードと同様のキーを持つレコードを別の入力から探し、両方の情報を元に片方のレコードの内容を更新する演算子です。 この演算子は、マスタを引き当てつつ `更新演算子`_ と同等の処理を行います。 .. list-table:: マスタつき更新演算子の概要 :widths: 4 6 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``MasterJoinUpdate`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Join` * - 入力数 - 2 * - 出力数 - 2 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - マスタ選択を利用可能 .. list-table:: マスタつき更新演算子の入出力 :widths: 1 1 1 1 2 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - master - グループ - 任意 - グループ化を指定 * - 入力 - tx - レコード - 任意 - グループ化を指定 * - 出力 - updated - レコード - txと同様 - マスタが見つかったもの * - 出力 - missed - レコード - txと同様 - マスタが見つからなかったもの .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.MasterJoinUpdate` マスタつき更新演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~~~~~ マスタつき更新演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: マスタつき更新演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``void`` - 不可 - * - 引数1 - 入力 - モデル - 必須 - マスタデータの入力 * - 引数2 - 入力 - モデル - 必須 - 変更対象のデータ * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、トランザクションデータに対応するマスタデータを引き当てたのち、マスタデータの情報を利用してトランザクションデータの任意の項目を変更し、出力に流します。 対象のメソッドは結合対象の二つのデータモデルオブジェクト型の引数を取ります。 このとき最初の引数は、マスタデータなど結合条件に対してユニークであるようなデータモデルオブジェクトである必要があります。 データモデルオブジェクト型の引数にはそれぞれ ``Key`` 注釈を指定し、 ``group`` でグループ化のためのプロパティ名を指定します。 それぞれのプロパティ列が完全に一致するものが結合対象になります。 整列のためのプロパティ名および整列方向に関する動作は規定されません。 メソッドの本体では、引数のトランザクションデータの内容を変更するプログラムを記述します。 マスタデータの内容を変更した際の動作は規定されません。 引数には同メソッドで宣言した型変数を利用できますが、全ての結果オブジェクト型の出力に型変数を含める場合には、いずれかの入力に同様の型変数を指定してある必要があります。 マスタつき更新演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * 結合対象のデータモデルオブジェクト型の引数 (マスタデータ)、さらに ``Key`` 注釈でグループ化のための情報を指定する * 結合対象のデータモデルオブジェクト型の引数、さらに ``Key`` 注釈でグループ化のための情報を指定する * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * マスタの価格をトランザクションデータに設定する。 * @param master マスタデータ * @param tx 変更するトランザクションデータ */ @MasterJoinUpdate public void updateWithMaster( @Key(group = "id") ItemMst master, @Key(group = "itemId") HogeTrn tx) { tx.setPrice(master.getPrice()); } .. ** また、この演算子注釈に ``selection`` を指定することで、非等価結合条件を記述することも可能です。 詳しくは `マスタ選択`_ を参照して下さい。 .. _cogroup-operator: グループ結合演算子 ------------------ 複数の入力をキーでグループ化し、キーが一致する入力ごとのグループをまとめて操作する演算子です。 非常に複雑な操作を表現できますが、コンパイラの最適化を適用しにくかったり、グループごとの大きさに制限があるなどの問題もあります。 .. list-table:: グループ結合演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``CoGroup`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `CoGroup` * - 入力数 - 任意 * - 出力数 - 任意 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - .. list-table:: グループ結合演算子の入出力 :widths: 2 2 2 2 5 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - (任意) - グループ - 任意 - 任意の個数、グループ化を指定 * - 出力 - (任意) - レコード - 任意 - 任意の個数 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.CoGroup` グループ結合演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~~~ グループ結合演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: グループ結合演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``void`` - 不可 - * - 引数1~ - 各入力 - リスト - 必須 - 任意の個数 * - ビュー引数 - 入力 - ビュー - 任意 - * - 以降の引数 - 各出力 - 結果 - 不可 - 任意の個数 * - 値引数 - なし - プリミティブ - 不可 - この演算子は、2種類以上のデータをそれぞれ条件に応じてグループ化してリストを作成し、 それらのリストを処理した結果を出力します。 入力は任意個のリストで、それぞれに ``Key`` 注釈のグループ化条件 ``group`` でプロパティの一覧を指定し、プロパティ列が完全に一致するものごとにメソッド内の処理を行います。 いくつかのグループに要素が存在しない場合、対応する引数には要素数0のリストが渡されます。 なお、 ``Key`` 注釈の整列条件 ``order`` でプロパティの一覧を指定すると、対象のリストの各要素は指定されたプロパティの内容で整列されます。 出力は任意個の結果で、メソッド内で同じ結果に対して複数回の結果を指定することも可能です。 結果の要素型には型引数の指定が可能ですが、その型引数は入力でも利用されている必要があります。 グループ結合演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * 一つ以上のデータモデルオブジェクトを要素に取るリスト型の引数、 さらにそれぞれ ``Key`` 注釈でグループ化と整列のための情報を指定する * 一つ以上の結果型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * HogeとFooをHogeのIDでグループ化し、重複なしで突合できたもののみを結果として出力する。 * それ以外の値はエラーとして出力する。 * @param hogeList Hogeのグループごとのリスト * @param fooList Fooのグループごとのリスト * @param hogeResult 成功したHoge * @param fooResult 成功したFoo * @param hogeError 失敗したHoge * @param fooError 失敗したFoo */ @CoGroup public void checkUp( @Key(group = "id") List hogeList, @Key(group = "hogeId") List fooList, Result hogeResult, Result fooResult, Result hogeError, Result fooError) { // いずれも存在+重複なしで突合成功 if (hogeList.size() == 1 && fooList.size() == 1) { hogeResult.add(hogeList.get(0)); fooResult.add(fooList.get(0)); } // それ以外はエラー else { for (Hoge hoge : hogeList) { hogeError.add(hoge); } for (Foo foo : fooList) { fooError.add(foo); } } } .. ** .. _spill-input-buffer: 巨大な入力グループへの対応 ~~~~~~~~~~~~~~~~~~~~~~~~~~ `グループ結合演算子の実装`_ において、演算子の入力には ``List`` を指定しています。 この演算子は基本的に小さなグループごとに処理することを想定しており、大きなグループを処理する場合に ``List`` 内の要素が多くなりすぎて、メモリが不足してしまう場合があります。 これを回避するために、演算子メソッドの各入力の引数に `@Once注釈`_ 、または `@Spill注釈`_ を指定することができます。 .. attention:: Asakusa on MapReduce では ``@Once`` 注釈 および ``@Spill`` 注釈を利用できません。 Asakusa on MapReduce では後述の `InputBuffer.ESCAPE`_ に記載の方法を利用してください。 @Once注釈 ^^^^^^^^^ ``@Once`` [#]_ 注釈を演算子の入力に指定すると、メモリ消費を抑え大きな入力グループを安全に取り扱うことができます。 ただし、入力には以下の制約が課せられます。 * 引数の型は ``Iterable<...>`` でなければならない (通常は ``List<...>`` ) * 各要素の内容は一度だけしか読み出せない .. note: @Once 注釈は、各要素のオブジェクトを順次読み出し、要素全体を一度に蓄えないことで巨大なデータを取り扱えるようにしています。 以下は ``@Once`` 注釈を使用するグループ結合演算子メソッドの例です。 演算子メソッドの各入力に ``@Once`` 注釈を付与し、引数の型を ``List`` から ``Iterable`` に変更しています。 .. code-block:: java @CoGroup public void cogroupWithOnce( @Key(group = "hogeCode") @Once Iterable hogeList, @Key(group = "hogeId") @Once Iterable fooList, Result hogeResult, Result fooResult ) { for (Hoge hoge : hogeList) { ... } for (Foo foo : fooList) { ... } } .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.model.Once` @Spill注釈 ^^^^^^^^^^ ``@Spill`` [#]_ 注釈を演算子の入力に指定すると、演算子メソッドの引数に指定したリストはメモリ外のストレージを一時的に利用します。 Java VMのヒープ上に配置されるオブジェクトは全体の一部で、残りはファイルシステム上などの領域に保存します。 これにより ``List`` を利用しつつ巨大な入力データを取り扱えるようになります。 また、 ``@Once`` 注釈では制約のある、リストに対する複数回アクセスやランダムアクセスが可能です。 .. attention:: ``@Spill`` 注釈を利用するとほとんどの場合に著しくパフォーマンスが低下します。 また、一時領域のストレージ構成や空き領域の確保といった、環境面での考慮も必要になります。 このため、通常は ``@Once`` 注釈の利用を推奨します。 演算子メソッド実装の都合上 ``@Once`` 注釈の制約が許容できない場合にのみ、 ``@Spill`` 注釈の利用を検討してください。 ``@Spill`` 注釈を指定した入力の ``List`` には以下に示す多大な制約がかかります。 * それぞれの ``List`` からはひとつずつしかオブジェクトを取り出せなくなる。 * 2つ以上オブジェクトを取り出した場合、最後に取り出したオブジェクト以外はまったく別の内容に変更されている可能性がある。 * リストから取り出したオブジェクトを変更しても、リストの別の要素にアクセスしただけで変更したオブジェクトの内容が失われる可能性がある。 つまり、次のようなプログラムを書いた場合の動作は保証されません。 .. code-block:: java @CoGroup public void invalid(@Key(group = "id") @Spill List list, Result result) { // 二つ取り出すとaの内容が保証されない Hoge a = list.get(0); Hoge b = list.get(1); // 内容を変更しても、別の要素を参照しただけでオブジェクトの内容が変わる場合がある b.setValue(100); list.get(2); } 上記のようなプログラムを書きたい場合、かならずオブジェクトのコピーを作成してください。 .. code-block:: java Hoge a = new Hoge(); Hoge b = new Hoge(); @CoGroup public void valid(@Key(group = "id") @Spill List list, Result result) { a.copyFrom(list.get(0)); b.copyFrom(list.get(1)); b.setValue(100); list.get(2); ... } なお、下記のようにひとつずつ取り出して使う場合、オブジェクトをコピーする必要はありません(ただし、このケースでは ``@Once`` で代替可能です)。 .. code-block:: java @CoGroup public void valid(@Key(group = "id") @Spill List list, Result result) { for (Hoge hoge : list) { hoge.setValue(100); result.add(hoge); } } .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.model.Spill` InputBuffer.ESCAPE ^^^^^^^^^^^^^^^^^^ .. attention:: バージョン |version| において、 ``InputBuffer.ESCAPE`` を指定する方法は過去バージョンとの互換性維持のため提供していますが、将来のバージョンでは非推奨となる予定です。 Asakusa on Spark、および |M3BP_FEATURE| ではこの機能の代わりに `@Once注釈`_ および `@Spill注釈`_ の利用を推奨します。 `グループ結合演算子の実装`_ において、演算子の入力には ``List`` を指定しています。 この演算子は基本的に小さなグループごとに処理することを想定しており、大きなグループを処理する場合に ``List`` 内の要素が多くなりすぎて、メモリが不足してしまう場合があります。 これを回避するには、演算子注釈の要素 ``inputBuffer`` に ``InputBuffer.ESCAPE`` [#]_ を指定します。 何も指定しない場合は、ヒープ上に全てのデータを保持する ``InputBuffer.EXPAND`` が利用されます。 ``InputBuffer.ESCAPE`` を指定した場合、巨大な入力データを取り扱えるようになる代わりに、演算子メソッドの引数に指定した ``List`` に以下に示す多大な制約がかかります。 * それぞれの ``List`` からはひとつずつしかオブジェクトを取り出せなくなる。 * 2つ以上オブジェクトを取り出した場合、最後に取り出したオブジェクト以外はまったく別の内容に変更されている可能性がある。 * リストから取り出したオブジェクトを変更しても、リストの別の要素にアクセスしただけで変更したオブジェクトの内容が失われる可能性がある。 .. warning:: ``ESCAPE`` を指定した場合、メモリ外のストレージを一時的に利用します。 そのため、ほとんどの場合に著しくパフォーマンスが低下します。 .. note:: ``ESCAPE`` を指定すると、演算子メソッドの引数に指定したリストは 内部的に「スワップ領域」を裏側に持ちます。 Java VMのヒープ上に配置されるオブジェクトは全体の一部で、残りはファイルシステム上などの領域に保存します。 ヒープ上には常に同じオブジェクトを利用して、スワップから復帰するときはそれらのオブジェクトを再利用しています。 この制約は、今後解消されるかもしれません。 つまり、次のようなプログラムを書いた場合の動作は保証されません。 .. code-block:: java @CoGroup(inputBuffer = InputBuffer.ESCAPE) public void invalid(@Key(group = "id") List list, Result result) { // 二つ取り出すとaの内容が保証されない Hoge a = list.get(0); Hoge b = list.get(1); // 内容を変更しても、別の要素を参照しただけでオブジェクトの内容が変わる場合がある b.setValue(100); list.get(2); } 上記のようなプログラムを書きたい場合、かならずオブジェクトのコピーを作成してください。 .. code-block:: java Hoge a = new Hoge(); Hoge b = new Hoge(); @CoGroup(inputBuffer = InputBuffer.ESCAPE) public void valid(@Key(group = "id") List list, Result result) { a.copyFrom(list.get(0)); b.copyFrom(list.get(1)); b.setValue(100); list.get(2); ... } なお、下記のようにひとつずつ取り出して使う場合、オブジェクトをコピーする必要はありません。 .. code-block:: java @CoGroup(inputBuffer = InputBuffer.ESCAPE) public void valid(List list, Result result) { for (Hoge hoge : list) { hoge.setValue(100); result.add(hoge); } } .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.flow.processor.InputBuffer` .. _split-operator: 分割演算子 ---------- 結合モデルから結合元のレコードを抽出してそれぞれ出力する演算子です。 この演算子への入力は、結合モデルである必要があります。 .. list-table:: 分割演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Split`` [#]_ * - 本体の実装 - 不要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 2 * - ビュー引数 - 指定不可 * - 値引数 - 指定不可 * - 型引数 - 指定不可 * - 備考 - .. list-table:: 分割演算子の入出力 :widths: 1 1 1 1 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - 結合モデルのみ * - 出力 - left - レコード - 特殊 - 結合モデルの左項の型 * - 出力 - right - レコード - 特殊 - 結合モデルの右項の型 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Split` 分割演算子の実装 ~~~~~~~~~~~~~~~~ 分割演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 分割演算子の実装 :widths: 1 1 1 1 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - なし - ``void`` - 不可 - * - 引数1 - 入力 - モデル - 不可 - 結合モデル * - 引数2 - 出力 - 結果 - 不可 - 結合モデルの元になったモデル * - 引数3 - 出力 - 結果 - 不可 - 結合モデルの元になったモデル この演算子は、結合済みのデータを入力に取り、結合前のデータに分割してそれぞれ出力します。 このメソッドには本体を指定せず、抽象メソッドとして宣言します。 対象のメソッドは一つのデータモデルオブジェクト型の引数と、二つの結果オブジェクトの引数を取ります。 このメソッドは結合データモデルオブジェクトの結合情報を元に、結合前のデータモデルオブジェクトをそれぞれ返すようなプログラムを自動的に生成します。 引数1には分割したい対象の結合モデルの型を指定します。 以降の引数には、分割結果を表す結果型を指定します。 この分割結果は、分割対象の結合モデルの元になったデータモデル型である必要があります。 この演算子メソッドには型引数を定義できません。 分割演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * 結合済みのデータモデルオブジェクト型の引数 * ``Result`` 型の型引数に、一つ目の分割先のデータモデルオブジェクト型を指定した引数 * ``Result`` 型の型引数に、二つ目の分割先のデータモデルオブジェクト型を指定した引数 * 以下の修飾子を付与する * abstract * 以下の修飾子は付与しない * (特になし) 以下は実装例です。 .. code-block:: java /** * レコードHogeFooをHogeとFooに分割する。 * @param joined 分割するレコード * @param hoge 分割後のHoge * @param foo 分割後のFoo */ @Split public abstract void split( HogeFoo joined, Result hoge, Result foo); .. ** .. _aggregate-operators: 集計演算子 ========== 集計系の演算子は、主にグループ化したレコード内での計算を行うための演算子です。 .. _summarize-operator: 単純集計演算子 -------------- レコードをキーでグループ化し、グループ内で集計した結果を出力する演算子です。 この演算子は、集計モデル [#]_ のレコードを構築します。入力は集計モデルの元になったデータモデルを指定し、集計結果の集計モデルが出力されます。 また、グループ化条件や集計方法は集計モデルに指定したものを利用します。 .. list-table:: 単純集計演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Summarize`` [#]_ * - 本体の実装 - 不要 * - 性能特性 - `Fold` * - 入力数 - 1 * - 出力数 - 1 * - ビュー引数 - 指定不可 * - 値引数 - 指定不可 * - 型引数 - 指定不可 * - 備考 - .. list-table:: 単純集計演算子の入出力 :widths: 1 2 2 1 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - グループ - 任意 - 集計モデルの元になった型 * - 出力 - out - レコード - 任意 - 集計結果、集計モデルの型 .. [#] 集計モデルについては :doc:`../dmdl/user-guide` を参照してください。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Summarize` NULL値に対する集約関数の動作 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 単純集計演算子を利用して集約するフィールドに ``null`` が含まれている場合、それぞれ以下のように動作します。 .. list-table:: nullに対する集約関数の動作 :widths: 3 7 :header-rows: 1 * - 集約関数 - NULL値が含まれる場合の動作 * - ``any`` - NULL値も他の値と同様に取り扱う * - ``sum`` - ``NullPointerException`` をスローする * - ``max`` - ``NullPointerException`` をスローする * - ``min`` - ``NullPointerException`` をスローする * - ``count`` - NULL値も他の値と同様に取り扱う 単純集計演算子の実装 ~~~~~~~~~~~~~~~~~~~~ 単純集計演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 単純集計演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - モデル - 不可 - 集計モデルのみ * - 引数1 - 入力 - モデル - 不可 - 集計モデルの元 この演算子は、単一の入力を特定の項目でグループ化し、グループ内で集計した結果を出力します。 このメソッドには本体を指定せず、抽象メソッドとして宣言します。 対象のメソッドは、集計対象のデータモデルオブジェクト型の引数を取ります。 返戻型には集計結果を表す集計モデルの型を指定します。 この演算子のグループ化条件や集計方法は、集計モデル型の注釈 [#]_ に全て埋め込まれているため、ここでは特に指定しません。 この演算子メソッドには型引数を定義できません。 単純集計演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型に集計結果となる集計モデル型を指定する * 以下の引数を宣言する * 集計対象のデータモデルオブジェクト型の引数 * 以下の修飾子を付与する * abstract * 以下の修飾子は付与しない * (特になし) 以下は実装例です。 .. code-block:: java /** * レコードHogeをHogeTotalに集計する。 * @param hoge 集計対象 * @return 集計結果 */ @Summarize public abstract HogeTotal summarize(Hoge hoge); .. ** .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.model.Summarized` .. _partial_aggregation: 部分集約 ~~~~~~~~ 単純集計演算子では、演算子注釈の ``partialAggregation`` を指定することで部分集約の設定を行えます。 この要素には ``PartialAggregation`` [#]_ を指定でき、指定した値ごとに次のような動作をします。 .. list-table:: 部分集約の設定 :widths: 3 7 :header-rows: 1 * - 指定する値 - 動作 * - ``TOTAL`` - 部分集約を行わない * - ``PARTIAL`` - 常に部分集約を行う * - ``DEFAULT`` - コンパイラオプションの設定に従う [#]_ 部分集約を行う場合、この演算子はグループの計算が完了する前にグループごとに集計の計算を始め、データのコピーや転送を削減しようとします。 単純集計演算子では部分集約可能な計算しか行いませんので、このオプションによって動作が変化することは基本的にありません。 .. attention:: 基本的に、単純集計演算子では部分集約を行うべきです。 初期値は ``PARTIAL`` になっています。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.flow.processor.PartialAggregation` .. [#] コンパイラオプションの設定については、以下の各コンパイラリファレンスを参照してください * :doc:`../spark/reference` - :ref:`spark-dsl-compiler-reference` * :doc:`../m3bp/reference` - :ref:`m3bp-dsl-compiler-reference` * :doc:`../mapreduce/reference` - :ref:`mapreduce-dsl-compiler-reference` .. _fold-operator: 畳み込み演算子 -------------- レコードをキーでグループ化し、グループ内のレコードを単一のレコードに畳み込む演算子です。 畳み込みの前後でレコードの型は一致していなければならず、また畳み込みの順序は規定されません。 .. list-table:: 畳み込み演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Fold`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Fold` * - 入力数 - 1 * - 出力数 - 1 * - ビュー引数 - 指定可能 [#]_ * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - .. list-table:: 畳み込み演算子の入出力 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - グループ - 任意 - グループ化を指定 * - 出力 - out - レコード - inと同様 - 畳みこみ結果 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Fold` .. [#] ただし、 `部分集約`_ に ``PARTIAL`` を指定している場合はビュー引数を指定できません。 畳み込み演算子の実装 ~~~~~~~~~~~~~~~~~~~~ 畳み込み演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: 畳み込み演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``void`` - 不可 - * - 引数1 - 入出力 - モデル - 必須 - 畳み込み結果 * - 引数2 - 入力 - モデル - 不可 - 引数1と同じ型を指定 * - ビュー引数 - 入力 - ビュー - 任意 - `部分集約`_ に ``PARTIAL`` を指定している場合は使用不可 * - 値引数 - なし - プリミティブ - 不可 - この演算子は、単一の入力を特定の項目でグループ化し、グループ内で畳みこんだ結果を出力します。 同じモデル型の二つの引数を取り、メソッドの本体で第一引数の内容を変更するプログラムを記述します。 第一引数には ``Key`` 注釈を指定し、``group`` でグループ化のためのプロパティ名を指定する必要があります。 なお、整列のためのプロパティ名および整列方向は無視されます。 この演算子は次のように動作します。 #. 引数1の ``Key`` 注釈に指定したグループ化条件で入力をグループ化 #. グループごとにそれぞれ以下の処理 #. グループ内の要素数が1になったら終了 #. そうでなければ、グループから要素を2つ取り除いて演算子メソッドを起動 #. 演算子メソッドの引数1に指定したモデルをグループに書き戻す #. グループ内の処理を繰り返す グループ内の折りたたみは、引数2を元に引数1を破壊的に変更します。 最後まで残った引数1の結果が、演算子の出力になります。 引数には同メソッドで宣言した型変数を利用できます。 畳み込み演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * ここまでの畳み込みの結果を表すデータモデルオブジェクト型の引数、 さらに ``Key`` 注釈でグループ化のための情報を指定する * 畳み込み対象のデータモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * レコードHogeを畳み込む。 * @param left ここまでの畳み込みの結果 * @param right 畳み込む対象 */ @Fold public void fold(@Key(group = "name") Hoge left, Hoge right) { // @Summarizeを手動で行うイメージで、leftに次々とrightを加える left.setValue(left.getValue() + right.getValue()); } .. ** 畳み込み演算子でも `部分集約`_ の指定が可能です。 集約の指定方法は `単純集計演算子`_ と同様です。 .. warning:: 畳み込み演算子で部分集約を利用する場合、演算子メソッドの本体でフレームワークAPIを利用できなくなります。 これはAsakusa Frameworkの実装上の制約で、今後解消されるかもしれません。 .. _group-sort-operator: グループ整列演算子 ------------------ レコードをキーでグループ化し、さらにグループを特定の条件で整列させて操作する演算子です。 この演算子は、 `グループ結合演算子`_ を単一の入力に対して行うものです。 .. list-table:: グループ整列演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``GroupSort`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `CoGroup` * - 入力数 - 1 * - 出力数 - 任意 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - .. list-table:: グループ整列演算子の入出力 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - グループ - 任意 - グループ化を指定 * - 出力 - (任意) - レコード - 任意 - 任意個数を指定可 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.GroupSort` グループ整列演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~~~ グループ整列演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: グループ整列演算子の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 出力 - ``void`` - 不可 - * - 引数1 - 入力 - リスト - 必須 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 以降の引数 - 各出力 - 結果 - 不可 - 任意の個数 * - 値引数 - なし - プリミティブ - 不可 - この演算子は、単一の入力をグループ化し、グループ内で整列したリストとして処理した結果を出力します。 一つのデータモデルオブジェクトを要素に取るリスト型の引数と、複数の結果オブジェクト型の引数を取り、リストの任意の要素について加工を行った後に、結果オブジェクトに出力を行うプログラムを記述します。 リスト型の引数には ``Key`` 注釈を指定し、グループ化のためのプロパティ名と整列のためのプロパティ名および整列方向を指定する必要があります。 引数には同メソッドで宣言した型変数を利用できますが、全ての結果オブジェクト型の出力に型変数を含める場合には、いずれかの入力に同様の型変数を指定してある必要があります。 グループ整列演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にvoidを指定する * 以下の引数を宣言する * データモデルオブジェクトを要素に取るリスト型の引数、 さらに ``Key`` 注釈でグループ化と整列のための情報を指定する * 一つ以上の結果型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract グループ整列演算子は、入力の個数が1つに制限されているという点を除き、 `グループ結合演算子`_ と同じ方法で記述できます。 以下は実装例です。 .. code-block:: java /** * レコードHogeを名前ごとに年齢の若い順に並べ、先頭と末尾だけをそれぞれ結果に流す。 * @param hogeList グループごとのリスト * @param first グループごとの先頭要素 * @param last グループごとの末尾要素 */ @GroupSort public void firstLast( @Key(group = "name", order = "+age") List hogeList, Result first, Result last) { first.add(hogeList.get(0)); last.add(hogeList.get(hogeList.size() - 1)); } .. ** ``Result`` インターフェースには複数件の結果を追加することもできます。 .. code-block:: java /** * レコードHogeを名前ごとに年齢の若い順に並べ、先頭の3件を結果に流す * @param hogeList グループごとのリスト * @param top3 グループごとの先頭3件の要素 */ @GroupSort public void topThree( @Key(group = "name", order = "+age") List hogeList, Result top3) { for (int i = 0; i < 3; i++) { top3.add(hogeList.get(i)); } } .. ** なお、グループ整列演算子で巨大な入力グループを取り扱いたい場合、 `グループ結合演算子`_ と同様に ``@Once`` や ``@Spill`` 注釈を指定します。 詳しくは `巨大な入力グループへの対応`_ を参照してください。 .. _special-operators: 特殊演算子 ========== 特殊系の演算子は、ここまでに紹介した分類に属さない特殊な演算子です。 .. _flow-operator: フロー演算子 ------------ Flow DSLで定義したフロー部品を演算子として利用します。 この演算子の入出力は、元となったフロー部品の入出力と一致します。 .. list-table:: フロー演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - 特殊 * - 導入 - 0.1 * - 入力数 - 任意 * - 出力数 - 任意 * - 値引数 - 指定可 * - 型引数 - 指定可 * - 備考 - フロー演算子の実装 ~~~~~~~~~~~~~~~~~~ フロー演算子はフロー部品を定義することで自動的に作成されます。 フロー部品の定義方法は :doc:`user-guide` を参照して下さい。 .. _checkpoint-operator: チェックポイント演算子 ---------------------- 再試行がサポートされている実行エンジンで、再試行の開始ポイントのヒントを指定するための演算子です。 .. attention:: チェックポイント演算子は、ジョブフローのトランザクションとは無関係です。 通常は明示的に指定する必要はありません。 .. list-table:: チェックポイント演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.1 * - メソッド - ``checkpoint`` * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 .. list-table:: チェックポイント演算子の入出力 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - inと同様 - チェックポイント演算子の実装 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ チェックポイント演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in; Out out; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); Checkpoint op = core.checkpoint(in); out.add(op); } .. _logging-operator: ロギング演算子 -------------- 通過したデータごとにアプリケーションログを出力する演算子です。 ログには以下のレベルがあります。 .. list-table:: ログのレベル :widths: 3 7 :header-rows: 1 * - レベル - 概要 * - ``ERROR`` - 重大な不具合 * - ``WARN`` - 注意を要する不具合 * - ``INFO`` - 分析のための情報 * - ``DEBUG`` - デバッグのための情報 このうち、 ``DEBUG`` はコンパイラの設定で有効または無効を切り替えられます。 .. list-table:: ロギング演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - ユーザー * - 導入 - 0.1 * - 演算子注釈 - ``Logging`` [#]_ * - 本体の実装 - 必要 * - 性能特性 - `Extract` * - 入力数 - 1 * - 出力数 - 1 * - ビュー引数 - 指定可能 * - 値引数 - 指定可能 * - 型引数 - 指定可能 * - 備考 - .. list-table:: ロギング演算子の入出力 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - * - 出力 - out - レコード - inと同様 - .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Logging` ロギング演算子の実装 ~~~~~~~~~~~~~~~~~~~~ ロギング演算子の演算子メソッドには次のような情報を指定します。 .. list-table:: ロギング演算子の実装 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - なし - ``String`` - 不可 - * - 引数1 - 入出力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 任意 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、入力されたデータをそのまま出力しますが、その際にロギングを行います。 メソッドの戻り値で返した文字列がログとして出力されます。 また、ログはシステムに規定された方法で処理されます [#]_ 。 なお、引数のオブジェクトの内容を変更してはいけません。 ロギング演算子の演算子注釈は、 ``Logging.Level`` [#]_ を指定することでログのレベルを指定できます。 この属性を指定しない場合は ``INFO`` レベルが使用されます。 レベルについては `ロギング演算子`_ を参照して下さい。 この演算子をフロー部品やジョブフローで使用する際に、出力 ``out`` は他の演算子等と結線されていなくてもエラーにならない、という特性を持ちます。 引数には同メソッドで宣言した型変数を利用できます。 例えば上限境界の無い型引数を定義して、引数の型として利用すると、 すべてのデータを受け取れるようなロギング演算子を定義することができます。 ロギング演算子の演算子メソッドは、一般的な演算子メソッドの要件の他に、 下記の要件をすべて満たす必要があります。 * 返戻型にStringを指定する * 以下の引数を宣言する * データモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * エラーログを出力する。 * @param hoge 更新するレコード */ @Logging(Logging.Level.ERROR) public String error(Hoge hoge) { return MessageFormat.format("hoge = {0}", hoge.getValueOption()); } .. ** .. [#] ログの処理方法は、内部的には :ref:`dsl-report-api` に処理を移譲しています。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Logging.Level` .. _empty-operator: 空演算子 -------- 「データを流さない入力」を表す演算子です。 `フロー演算子`_ の利用しない入力に接続することを想定しています。 .. list-table:: 空演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.1 * - メソッド - ``empty`` * - 性能特性 - N/A * - 入力数 - 0 * - 出力数 - 1 .. list-table:: 空演算子の入出力 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 出力 - out - レコード - 任意 - 空演算子の実装 ~~~~~~~~~~~~~~ 空演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); HogeOperatorFactory hoge = new HogeOperatorFactory(); ... Empty op = core.empty(Hoge.class); Something something = hoge.something(op); ... } .. _stop-operator: 停止演算子 ---------- 「データを流さない出力」を表す演算子です。 各種演算子の利用しない出力に接続することを想定しています。 .. list-table:: 停止演算子の概要 :widths: 5 5 :header-rows: 1 * - 項目 - 説明 * - 分類 - コア * - 導入 - 0.1 * - メソッド - ``stop`` * - 性能特性 - N/A * - 入力数 - 1 * - 出力数 - 0 .. list-table:: 停止演算子の入出力 :widths: 1 1 1 1 1 :header-rows: 1 * - 分類 - 名前 - 単位 - 型 - 備考 * - 入力 - in - レコード - 任意 - .. note:: Flow DSLではすべての演算子の出力が何らかに接続されていなければなりません。 これは接続漏れなどによる実装バグなどを検出するための措置です。 停止演算子の実装 ~~~~~~~~~~~~~~~~ 停止演算子はコア演算子に分類されるため、Operator DSLでの実装はありません。 Flow DSLからは次のように利用します。 .. code-block:: java In in; @Override protected void describe() { CoreOperatorFactory core = new CoreOperatorFactory(); HogeOperatorFactory hoge = new HogeOperatorFactory(); Something something = hoge.something(in); core.stop(something.unnecessary); } .. _support-operators: 補助演算子 ========== 補助演算子は単体で演算子としては機能せず、他の演算子と組み合わせて利用する注釈です。 .. _master-selection-support-operator: マスタ選択 ---------- マスタ選択は、以下の演算子において非等価結合を実現するための補助演算子です。 * `マスタ確認演算子`_ * `マスタ結合演算子`_ * `マスタ分岐演算子`_ * `マスタつき更新演算子`_ それぞれの演算子注釈には共通して ``selection`` という注釈要素を指定可能です。 この要素にメソッド名を指定し、同じ演算子クラス内に指定したメソッド名で、注釈 ``MasterSelection`` [#]_ を付与したパブリックメソッドを宣言します。 このメソッドは次のように宣言します。 .. list-table:: マスタ選択の実装 :widths: 2 2 2 2 3 :header-rows: 1 * - 分類 - 対応 - 型 - キー - 備考 * - 返戻 - 入力 - モデル - 不可 - 選択結果のマスタデータ * - 引数1 - 入力 - リスト - 不可 - 選択対象のマスタ一覧 * - 引数2 - 入力 - モデル - 不可 - * - ビュー引数 - 入力 - ビュー - 不可 - * - 値引数 - なし - プリミティブ - 不可 - この演算子は、トランザクションデータに対応する複数のマスタデータを引き当てたのち、実際に利用するマスタデータを選択します。 つまり、等価結合のみしか行えない環境において、重複するマスタデータから複雑な条件でマスタデータを選択するために利用することができます。 対象のメソッドは、マスタデータを表すデータモデルオブジェクトを要素に取るリスト型の引数と、トランザクションデータを表すデータモデルオブジェクト型の引数を順に取ります。 いずれも ``Key`` 注釈は不要です。 メソッドの本体ではマスタデータを選択し、一つだけ選んで戻り値として返します。 戻り値に ``null`` を返した場合にはマスタの引当に失敗した扱いになります。 なお、各引数の内容を変更した際の動作は規定されません。 返戻値の型と、引数1の要素型はマスタ選択演算子を利用する演算子の引数1と同じ型である必要があります。 また、引数2以降はそれぞれマスタ選択演算子を利用する演算子メソッドの引数と同じ型である必要があります。 なお、引数2以降は省略可能です。 マスタ選択演算子を指定した演算子メソッドが実行されると、その演算子の処理が実行される前に、マスタ選択演算子のメソッドが起動されます。 その際、マスタデータが指定したグループ化条件に従ってリストに詰められて、マスタ選択演算子メソッドの引数として与えられます。 そして、マスタ選択演算子メソッドの戻り値を引数として、元の演算子メソッドが実行されます。 この注釈を付与するメソッドは、下記の要件を満たす必要があります。 * 返戻型に結合対象のデータモデルオブジェクト型(マスタデータ)を指定する * 以下の引数を宣言する * 結合対象のデータモデルオブジェクト型の引数 (マスタデータ) * 結合対象のデータモデルオブジェクト型の引数 * 以下の修飾子を付与する * (特になし) * 以下の修飾子は付与しない * abstract 以下は実装例です。 .. code-block:: java /** * 有効なマスタを選択する。 * @param masters 選択対象のマスタデータ一覧 * @param tx トランザクションデータ * @return 実際に利用するマスタデータ、利用可能なものがない場合はnull */ @MasterSelection public ItemMst selectItemMst(List masters, HogeTrn tx) { for (ItemMst mst : masters) { if (mst.getStart() <= tx.getDate() && tx.getDate() <= mst.getEnd()) { return mst; } } return null; } .. ** マスタ選択演算子を利用する演算子は、かならず同じクラス内に宣言する必要があります。 また、その演算子は同一種類のマスタおよびトランザクションデータを取り扱う必要があります。 以下は、マスタ選択演算子を使用する演算子メソッドの実装例です。 .. code-block:: java /** * マスタの価格をトランザクションデータに設定する。 * @param master マスタデータ * @param tx 変更するトランザクションデータ */ @MasterJoinUpdate(selection = "selectItemMst") public void updateWithMaster( @Key(group = "id") ItemMst master, @Key(group = "itemId") HogeTrn tx) { tx.setPrice(master.getPrice()); } .. ** .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.MasterSelection` .. _volatile-support-operator: 多重化抑制 ---------- 多重化抑制は演算子メソッドに追加で指定する注釈で、コンパイラの多重化に関する最適化を抑止します。 コンパイラは最適化の過程で、単一の演算子を複数個に分解して並列性を確保します。 このため、同じデータに対して処理が複数回実行される場合があり、毎回異なる結果を出力するような演算子では期待した結果が得られない場合があります。 多重化を抑制する場合には、次のように注釈 ``Volatile`` [#]_ を演算子メソッドに指定します。 .. code-block:: java /** * ランダムに分岐する。 * @param hoge 対象のレコード * @return 分岐先を表すオブジェクト */ @Volatile @Branch public Status select(Hoge hoge) { if (Math.random() < 0.5) { return Status.SMALL; } else { return Status.BIG; } } .. ** 上記の場合、レコードはそれぞれ ``SMALL`` か ``BIG`` のいずれかのみに出力されるのが通常です。 しかし、多重化抑制の指定がない場合には、この演算子はレコードを両方に出力する場合や、いずれにも出力しない場合など予期せぬ動作をする場合があります。 その他、以下のケースなどでは多重化抑止が有効です。 * ユニークな値を採番する * 実行時の時刻などを利用する * 入力の個数を計測する .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Volatile` .. _sticky-support-operator: 除去抑制 -------- 除去抑制は演算子メソッドに追加で指定する注釈で、コンパイラの除去に関する最適化を抑止します。 コンパイラは最適化の過程で、最終的なジョブフローの出力に到達しない演算子の処理をすべて除去します。 このため、副作用のみを期待するような演算子を配置しても、期待した動作を行いません。 除去を抑制する場合には、次のように注釈 ``Sticky`` [#]_ を演算子メソッドに指定します。 .. code-block:: java /** * 例外をスローする。 * @param hoge 対象のレコード */ @Sticky @Update public void raise(Hoge hoge) { throw new IllegalStateException(); } .. ** 上記の更新演算子は直後に `停止演算子`_ によって出力を抑制され、ジョブフローの出力に接続されていないものとします。 除去抑制の指定がない場合、この演算子はコンパイラによって「不要な演算子」と判断され、例外がスローされることはありません。 上記のように除去抑制が指定されている場合にはこの演算子は消去されず、この演算子の入力にデータが流れた瞬間に例外がスローされます。 .. [#] :asakusafw-javadoc:`com.asakusafw.vocabulary.operator.Sticky`