S-JIS[2013-11-09/2016-02-11] 変更履歴

Asakusa Framework 拡張演算子

Asakusa FrameworkOperator DSLの拡張演算子(extend)のメモ。


概要

拡張演算子は、新しいプロパティーを追加した別のデータモデルに変換する演算子。
性能特性はExtract(旧ドキュメントではMap)。[/2016-02-11]

入力
ポート数
入力データモデル
の制約
イメージ 出力
ポート数
出力データモデル
の制約
入力1レコード
に対する
出力レコード数
1   1 inのプロパティーは
全て存在している
必要がある。
1レコード。

「プロパティーが追加されているデータモデル」とは、例えば以下のような状態のことを言っている。

  入力データモデル 出力データモデル 説明
プロパティー value1 value1  
value2 value2  
value3 value3  
  value4 value4が追加されている。

拡張演算子の入力データモデルと出力データモデルに同一プロパティー名のプロパティーがある場合はそのまま値が移送される。
出力データモデルにしか無いプロパティー(追加されたプロパティー)は初期値。(更新演算子(@Update)等で値をセットする)

なお、値の移送方法に関しては、getter/setterを使って移送するコードが生成される。
リフレクションを使うわけではないので、性能劣化については気にする必要は無い)


プロパティー名が一致しないデータモデルへの変換を行いたい場合は変換演算子(@Convert)を使う。

プロパティーを減らす場合は射影演算子(project)を使う。
プロパティーが増減する場合は再構築演算子(restructure)を使う。


hogeデータモデルをプロパティーの多いfooデータモデルへ変換する例。
(この図はToad Editorを用いて作っています)

入力データ例   出力データ例
in
hoge
value1 value2 value3
1 1000 aaa
12 12000 bbb
3 3333 ccc
out
foo
value1 value2 value3 value4
1 1000 aaa  
12 12000 bbb  
3 3333 ccc  

example.dmdl(DMDL):

hoge = {

    value1 : INT;

    value2 : LONG;

    value3 : TEXT;
};

foo = hoge + {

    value4 : TEXT;
};

fooには、全てのプロパティーをいちいち書いてもよいのだが、
「hogeにプロパティーを追加する」のであれば、「hoge + 〜」で追加するプロパティーだけを書くことが出来る。

ExampleJob.java(Flow DSL):

import com.asakusafw.vocabulary.flow.util.CoreOperatorFactory;
import com.asakusafw.vocabulary.flow.util.CoreOperatorFactory.Extend;

import com.example.modelgen.dmdl.model.Foo;
import com.example.modelgen.dmdl.model.Hoge;
	private final In<ExHoge> in;

	private final Out<ExFoo> out;
	@Override
	public void describe() {
		CoreOperatorFactory core = new CoreOperatorFactory();

		// HogeをFooに変換する
		Extend<Foo> ex = core.extend(this.in, Foo.class);
		this.out.add(ex.out);
	}

拡張演算子はコア演算子なので、Operatorクラスにプログラマーが何かを実装する必要は無い。

Flow DSLでは、extendメソッドの第2引数に変換先のデータモデル(のクラス)を指定する。
(変換元のデータモデルは第1引数(接続元)の情報から分かる)

なお、出力データの指定方法は、「out.add(ex)」でも「out.add(ex.out)」でも同じ。見た目が違うだけ。


asの例

AsakusaFW 0.7.3から、新しい表記方法が出来るようになった。[2015-04-23]
メソッドの引数にデータモデルクラスを渡すのではなく、In・SourceやOutのデータソースを指定し、それと同じデータモデルであることを表す。

	private final In<Hoge> in;

	private final In<HogeExtend> extendIn;

	private final Out<HogeExtend> out;
		// 従来の書き方
		this.out.add(core.extend(this.in, HogeExtend.class));

		// AsakusaFW 0.7.3 で書けるようになった書き方
		this.out.add(core.extend(this.in).as(HogeExtend.class));
		this.out.add(core.extend(this.in).as(this.extendIn));
		this.out.add(core.extend(this.in).as(this.out));

この表記方法は、多相データフロー(ジェネリクス(型引数)を使ってフローパートを定義する)で便利。
(Javaでは、型引数から「T.class」のようにしてクラスを指定することは出来ない為)

@FlowPart
public class ExtendFlowPart<T extends ProjHoge> extends FlowDescription {
	private final In<Hoge> hogeIn;

	private final In<T> in;

	private final Out<T> out;

	public ExtendFlowPart(In<Hoge> hogeIn, In<T> in, Out<T> out) {
		this.hogeIn = hogeIn;
		this.in = in;
		this.out = out;
	}
	@Override
	public void describe() {
		CoreOperatorFactory core = new CoreOperatorFactory();

		core.stop(this.in);

//×		this.out.add(core.extend(this.hogeIn, T.class));
//		this.out.add(core.extend(this.hogeIn).as(this.in));
		this.out.add(core.extend(this.hogeIn).as(this.out));
	}
}

単体テスト

拡張演算子はコア演算子なので、(Operatorクラスにプログラマーが何も実装していないので)Operatorの単体テストを実装する必要は無い。


類似

拡張演算子は、SQLのSELECT-INSERTに似ている。

INSERT foo
SELECT value1, value2, value3, '' FROM hoge;

あるいは、Scalamapメソッドに似ている。

  case class Hoge(value1: Int, value2: Long, value3: String)
  case class Foo(value1: Int, value2: Long, value3: String, value4: String)

  def extend(hoge: Hoge) = Foo(hoge.value1, hoge.value2, hoge.value3, null)

  val in : List[Hoge] = 〜
  val out: List[Foo]  = in.map(extend)

Operator DSLへ戻る / AsakusaFW目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま