Asakusa Framework0.9.1のOperator DSLのGroupViewについて。→最新版GroupView
|
AsakusaFW 0.9.1で、Operator DSLの各ユーザー演算子でGroupViewが使えるようになった。
ただし、AsakusaFW 0.9.1時点ではincubating機能扱いで、0.10.0で正式機能となった。[/2017-12-02]
例えば更新演算子(Update)で別のデータモデルを参照して更新元データとして使うことが出来る。
つまり、マスター更新演算子(MasterJoinUpdate)と同様のことが出来ることになる。
ただし、マスター側(GroupView)のデータはメモリーに乗り切るほどの小規模データ、いわゆる「定数表」が想定されている。
Direct I/O APIでも同様のことが出来たが、たぶんGroupViewの方が正式な機能になると思われる。
Direct I/O APIはどの演算子からでもファイルを読むことが出来た(他の演算子の処理結果を使う事は出来なかった)が、GroupViewは他の演算子の処理結果を入力とすることが出来る。
また、Direct I/O APIと異なり、Flow DSL上でGroupViewへの接続を記述することになる。
AsakusaFW 0.9.1ではGroupViewはincubatingの機能なので、そのままでは使うことが出来ない。
build.gradleでsdk.incubatingをtrueにする必要がある。
また、GroupViewを使っているフローのテストを行う際は、従来のスモールジョブ実行エンジンを使った環境(AsakusaFW 0.9.1のデフォルト)では実行できない。
Vanillaをテストキットとして使用する必要がある。
〜 apply plugin: 'asakusafw-sdk' apply plugin: 'asakusafw-organizer' apply plugin: 'asakusafw-m3bp' apply plugin: 'asakusafw-vanilla' apply plugin: 'eclipse' asakusafw { 〜 sdk.incubating true sdk.testkit 'vanilla' }
定義を追加したら、Eclipseのプロジェクトの定義を作り直しておく。
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のテスト用に特に何かが必要になるわけではない。
ただし、従来のスモールジョブ実行エンジンを使った環境でGroupViewを含むフローのテストを実行するとエラーになる。
04:21:13 ERROR View codeConvertView (in ConstantExampleOperator#updateCode(@Update)) is not supported in this platform (at com.example.flowpart.ConstantExampleFlowPart)
build.gradleでVanillaをテストキットとして指定する必要がある。