Asakusa FrameworkのOperator DSLの更新演算子(@Update)のメモ。
|
更新演算子は、レコードの内容(データモデルのプロパティーの値)を変更する演算子。
性能特性はExtract(旧ドキュメントではMap)。[/2016-02-11]
入力 ポート数 |
入力データモデル の制約 |
イメージ | 出力 ポート数 |
出力データモデル の制約 |
入力1レコード に対する 出力レコード数 |
---|---|---|---|---|---|
1 |
![]() |
1 | inと同じ データモデル |
1レコード。 |
更新演算子は、データモデル内の値を変更するのに使う。
同一レコード内の他の値を使用(参照)することは出来るが、他のレコード(前後のレコードとか)を見ることは出来ない。
入力データモデルを別のデータモデルに変換して出力したい場合は変換演算子(@Convert)を使う。
複数レコードの集計をしたい場合は単純集計演算子(@Summarize)を使う。
(集計以外で)他のレコードを参照したい場合はグループ整列演算子(@GroupSort)を使う。
hogeというデータモデルの「value」項目に100をセットする例。
(この図はToad Editorを用いて作っています)
入力データ例 | 出力データ例 | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
in |
|
→ | out |
|
hoge = { value : INT; };
import com.asakusafw.vocabulary.operator.Update; import com.example.modelgen.dmdl.model.Hoge;
public abstract class ExampleOperator { /** * valueに100を設定する * * @param hoge * 更新するレコード */ @Update public void edit(Hoge hoge) { hoge.setValue(100); } }
引数に更新対象のデータモデルを指定する。
戻り型は無し。
import com.example.modelgen.dmdl.model.Hoge; import com.example.operator.ExampleOperatorFactory; import com.example.operator.ExampleOperatorFactory.Edit;
private final In<Hoge> in; private final Out<Hoge> out;
@Override public void describe() { ExampleOperatorFactory operators = new ExampleOperatorFactory(); // valueに100を設定する Edit edit = operators.edit(this.in); this.out.add(edit.out); }
Flow DSLでは、自分が作ったOperatorのFactoryクラス(AsakusaFWのコンパイラーによって生成される)を使用する。
メソッド名はOperatorクラスに書いたメソッド名と同じ。
戻り値の型はAsakusaFWのコンパイラーによって生成されたクラス。(メソッド名を先頭が大文字のキャメルケースに変換したもの)
出力ポートの名前のデフォルトはout。
出力ポート名を変えたい場合は@Updateアノテーションで指定できる。
@Update(outputPort = "out")
更新演算子の単体テストの実装例。
package com.example.operator;
import static org.hamcrest.CoreMatchers.is; import static org.junit.Assert.assertThat; import org.junit.Test; import com.example.modelgen.dmdl.model.Hoge;
/** * {@link ExampleOperator}のテスト. */ public class ExampleOperatorTest { @Test public void edit() { ExampleOperator operator = new ExampleOperatorImpl(); Hoge hoge = new Hoge(); operator.edit(hoge); assertThat(hoge.getValue(), is(100)); } }
Operatorのテストクラスは、通常のJavaのJUnitのテストケースクラスとして作成する。
テスト対象のOperatorクラス自身は抽象クラスだが、Operatorクラス名の末尾に「Impl」の付いた具象クラスがAsakusaFWによって生成されるので、それを使う。
更新演算子では、変更対象のデータモデルの他にも引数を指定することが出来る。
こうすると、引数の内容分だけ異なる同じ演算子(処理内容)をいくつかの場所で使用できるようになる。
Hoge2というデータモデルをErrorRecordデータモデルに変換し、「message」項目に引数で指定したメッセージを設定する例。
hoge2 = { "コード" code : TEXT; }; error_record = { "コード" code : TEXT; "メッセージ" message : TEXT; };
import com.asakusafw.vocabulary.operator.Update; import com.example.modelgen.dmdl.model.ErrorRecord;
public abstract class ExampleOperator { /** * メッセージを設定する * * @param errorRecord * 更新するレコード * @param message * 設定するメッセージ */ @Update public void setMessage(ErrorRecord errorRecord, String message) { errorRecord.setMessageAsString(message); } }
第1引数は通常の更新演算子と同じく更新対象のデータモデル。
第2引数以降を通常のJavaメソッドと同様に自由な引数とすることが出来る。(ただしプリミティブのみ)
import com.asakusafw.vocabulary.flow.util.CoreOperatorFactory; import com.asakusafw.vocabulary.flow.util.CoreOperatorFactory.Restructure; import com.example.modelgen.dmdl.model.ErrorRecord; import com.example.modelgen.dmdl.model.Hoge2; import com.example.operator.ExampleOperatorFactory; import com.example.operator.ExampleOperatorFactory.SetMessage;
@Override public void describe() { ExampleOperatorFactory operators = new ExampleOperatorFactory(); CoreOperatorFactory core = new CoreOperatorFactory(); // エラー情報への変換 Restructure<ErrorRecord> restructure = core.restructure(this.in, ErrorRecord.class); // メッセージを設定する SetMessage setMessage = operators.setMessage(restructure.out, "Hoge2エラー"); this.out.add(setMessage.out); }
演算子を使用するFlow DSLで、引数に具体的な値を指定する。
@Test public void setMessage() { ExampleOperator operator = new ExampleOperatorImpl(); ErrorRecord errorRecord = new ErrorRecord(); String message = "Hoge2エラー"; operator.setMessage(errorRecord, message); assertThat(errorRecord.getMessageAsString(), is(message)); }
更新演算子は、SQLのUPDATEに似ている。
UPDATE error_record SET message = 'メッセージ';
ただ、AsakusaFWでは入力データから出力データを作り出すという仕組み(同一テーブルへの更新ではない)なので、SELECT-INSERTの方が近いかもしれない。
INSERT out (code, message) SELECT code, 'メッセージ' FROM in;
case class Hoge(value: Int) def edit(hoge: Hoge) = hoge.copy(value = 100) val in : List[Hoge] = 〜 val out: List[Hoge] = in.map(edit)
SQLのUPDATE文のイメージで言うなら、foreachメソッドになるか。
case class ErrorMessage(code: String, var message: String) def setMessage(record: ErrorRecord, message: String) : Unit = { record.message = message } val list : List[ErrorRecord] = 〜 list.foreach(setMessage(_, "メッセージ"))