Asakusa FrameworkのOperator DSLのビューAPI(GroupView)について。
|
|
|
AsakusaFW 0.10.0でビューAPIが使えるようになった。
(AsakusaFW 0.9.1でincubating機能として導入されたのが、0.10.0で正式機能となった)
ビューAPIは、Operator DSLの各ユーザー演算子で任意のデータモデルを入力できるようにするもの。
ユーザー演算子のメソッドの引数にViewあるいはGroupViewを指定する。
Viewは全データの取得、GroupViewはキーを指定してそのキーのデータのみ取得できる。普通はGroupViewを使用することになるだろう。
例えば更新演算子(Update)で別のデータモデルを参照して更新元データとして使うことが出来る。
つまり、マスター更新演算子(MasterJoinUpdate)と同様のことが出来ることになる。
ただし、マスター側(GroupView)のデータはメモリーに乗り切るほどの小規模データ、いわゆる「定数表」が想定されている。
Direct I/O APIでも同様のことが出来たが、GroupViewの方が正式な機能 となった。
Direct I/O APIはどの演算子からでもファイルを読むことが出来た(他の演算子の処理結果を使う事は出来なかった)が、GroupViewは他の演算子の処理結果を入力とすることが出来る。
また、Direct I/O APIと異なり、Flow DSL上でGroupViewへの接続を記述することになる。
code_convertというデータモデルをGroupViewで読み込む例。
変換対象コードと変換後コードを持っておき、それを使ってUpdate演算子の中でコードを変換する。
"コード変換データ"
code_convert = {
"変換対象コード"
code : TEXT;
"変換後コード"
converted_code : TEXT;
};
package com.example.operator;
import java.util.List; import com.asakusafw.runtime.core.GroupView; import com.asakusafw.vocabulary.model.Key; import com.asakusafw.vocabulary.operator.Update; import com.example.modelgen.dmdl.model.CodeConvert; import com.example.modelgen.dmdl.model.SalesDetail;
public abstract class ConstantExampleOperator {
@Update
public void updateCode(SalesDetail detail, @Key(group = { "code" }, order = { "converted_code" }) GroupView<CodeConvert> codeConvertView) {
List<CodeConvert> codeConvertList = codeConvertView.find(detail.getStoreCodeOption());
if (!codeConvertList.isEmpty()) {
CodeConvert codeConvert = codeConvertList.get(0);
detail.setStoreCodeOption(codeConvert.getConvertedCodeOption());
}
}
}
GroupViewは、各演算子メソッドの通常の引数の後、追加パラメーター(プリミティブやStringの引数)の前に記述する。GroupViewを複数記述することも可能。
@Keyでキーとなる項目を指定する。(orderでソート順を指定することも可)
そして、GroupView(上記のcodeConvertView)のfindメソッドにキーの値(StringOptionやIntOption等、キー項目のデータ型のOptionクラス)を指定することで、該当するキーの値一覧が取得できる。
該当する値が無い場合は空のListが返る。
もし複数のレコードが取得されうる場合は、@Keyのorderでソート順を指定することも可能。
キーが複数項目ある(複合キー)場合は、@Keyのgroupに複数の項目を指定し、findメソッドにその個数分の値を指定する。
@Keyのgroupの項目数とfindの引数の数(およびデータ型)が一致していないと、実行時に例外が発生する。
| キーの 項目数 |
@Keyの例 | findの例 | 備考 |
|---|---|---|---|
| 0 | @Key(group = {}, order = { "sort1" }) GroupView<T> view |
List<T> list = view.find(); |
全件取得される。 |
| 1 | @Key(group = { "key1" }, order = { "sort1" }) GroupView<T>
view |
List<T> list = view.find(in.getId1Option()); |
|
| 2 | @Key(group = { "key1", "key2" }, order = { "sort1" })
GroupView<T> view |
List<T> list = view.find(in.getId1Option(),
in.getId2Option()); |
package com.example.flowpart;
import com.asakusafw.vocabulary.flow.FlowDescription; import com.asakusafw.vocabulary.flow.FlowPart; import com.asakusafw.vocabulary.flow.In; import com.asakusafw.vocabulary.flow.Out; import com.asakusafw.vocabulary.flow.Source; import com.example.modelgen.dmdl.model.CodeConvert; import com.example.modelgen.dmdl.model.SalesDetail; import com.example.operator.ConstantExampleOperatorFactory;
/**
* GroupViewの実験
*/
@FlowPart
public class ConstantExampleFlowPart extends FlowDescription {
/** 売上明細 */
private final In<SalesDetail> salesDetail;
/** コード変換データ */
private final In<CodeConvert> codeConvert;
/** 変換後売上明細 */
private final Out<SalesDetail> convertedSalesDetail;
/**
* GroupViewの実験
*
* @param salesDetail 売上明細
* @param codeConvert コード変換データ
* @param convertedSalesDetail 変換後売上明細
*/
public ConstantExampleFlowPart(In<SalesDetail> salesDetail, In<CodeConvert> codeConvert, Out<SalesDetail> convertedSalesDetail) {
this.salesDetail = salesDetail;
this.codeConvert = codeConvert;
this.convertedSalesDetail = convertedSalesDetail;
}
@Override
public void describe() {
ConstantExampleOperatorFactory operator = new ConstantExampleOperatorFactory();
Source<SalesDetail> c = operator.updateCode(salesDetail, codeConvert).out;
convertedSalesDetail.add(c);
}
}
GroupViewを使った演算子をFlow DSLで記述する際は、GroupViewの分を引数に入れる必要がある。
GroupViewを使ったOperatorをテストする方法。
package com.example.operator;
import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import java.util.ArrayList; import java.util.List; import org.junit.Rule; import org.junit.Test; import com.asakusafw.runtime.core.GroupView; import com.asakusafw.testdriver.OperatorTestEnvironment; import com.example.modelgen.dmdl.model.CodeConvert; import com.example.modelgen.dmdl.model.SalesDetail;
public class ConstantExampleOperatorTest {
@Rule
public final OperatorTestEnvironment resource = new OperatorTestEnvironment();
@Test
public void updateCode() {
GroupView<CodeConvert> codeConvertView = getCodeConvertView();
// テスト実行
ConstantExampleOperator operator = new ConstantExampleOperatorImpl();
SalesDetail detail = new SalesDetail();
detail.setStoreCodeAsString("123");
operator.updateCode(detail, codeConvertView);
assertThat(detail.getStoreCodeAsString(), is("abc"));
}
private GroupView<CodeConvert> getCodeConvertView() {
List<CodeConvert> objects = new ArrayList<>();
objects.add(create("123", "abc"));
objects.add(create("456", "def"));
return resource.loader(CodeConvert.class, objects).group("code").order("converted_code").asView();
}
private CodeConvert create(String code, String convertedCode) {
CodeConvert model = new CodeConvert();
model.setCodeAsString(code);
model.setConvertedCodeAsString(convertedCode);
return model;
}
}
OperatorTestEnvironmentのloaderメソッドを使ってGroupViewを生成する。
上記の例ではloaderメソッドにデータモデルのListを渡しているが、ファイルのパスを指定してそのファイルを使ったりすることも出来るようだ。
groupとorderメソッドには、テスト対象のGroupViewの@Keyに指定したgroup, orderと同じものを指定する。
最後にasViewメソッドを呼び出すと、GroupViewが生成される。
GroupViewを使った場合はFlow DSL上で普通のデータと同じく結線されているので、通常のフローのテストの方法でデータを用意すればよい。
GroupViewのテスト用に特に何かが必要になるわけではない。
GgoupViewのfindメソッドの引数の型はObject...である。[2018-11-12]
つまり、コンパイル時の型チェックが効かない。
(GroupViewのfindメソッドの引数の型は、@Keyのgroupで指定したプロパティーの型と合わせる必要がある。Javaではそのようなチェックが出来ない為、findメソッドはObject...になっているのだと思う)
実行時には、デフォルトでは引数の個数のチェック(@Keyのgroupのプロパティーの個数とfindメソッドの引数の個数が一致している事のチェック)はされるが、型のチェックは行われない。
(たぶんJavaのMapを使った実装だろうから、キーがObjectであればエラーにならない。しかしキーは一致しないので、常に空リストが返る)
設定com.asakusafw.dag.view.validateをTYPEにすると、実行時に型チェックが行われるようになる。(デフォルトはCOUNT(
値はValidationLevel列挙型に定義されている))
(Vanillaの場合、環境変数ASAKUSA_VANILLA_ARGSに「--engine-conf
com.asakusafw.dag.view.validate=TYPE」を設定するか、ASAKUSA_HOME/vanilla/conf/vanilla.propertiesに「com.asakusafw.dag.view.validate=TYPE」を記述する)
(実行速度のことを考えて、デフォルトでは型チェックまでは行わないようになっているのだと思う)
AsakusaFW 0.10.1以前のGroupViewにはバグがあって、MasterJoinでGroupViewを使うと「「マスター結合演算子」の演算子メソッドの引数は2つのデータモデル型である必要があります」というエラーになる。[2018-11-10]