LUISで出力されるJSONの二次元配列のEntityをFlowで抽出する

#背景
この記事読んでみて、え！！私もやりたい！って思ったけどなかなか簡単にできなかったので思い出としてここに書いておきます。
「AI」すごいぜ！　メールから自動で請求書作成にノンプログラミングでチャレンジした話
https://internet.watch.impress.co.jp/docs/column/shimizu/1110337.html

#やりたいこと
メール受信したら本文をLUISで判定して特定のメールアドレスだけ抽出してBoxにあるEXCELのテーブルに出力したい
こことか
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/9359bde4-5956-226e-07a4-6d14cf2bfadc.png)
こことか
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/de145e2e-32a5-fac4-71f0-ea12a8c5089e.png)
こんなとか
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/24915e18-de17-8232-f3f3-2380b1a6e7b4.png)
様式だったりエラーコードだったり微妙に異なるフォーマットだったりするのでFunctionsで作るにしてもパターンごとで判定させたりするの手間なのでLUISくんでうまい感じで抽出できるんじゃ？って思ったってのがきっかけ。

#前提
使うのは以下
- Microsoft Flow（Office365）
- Box（の中のEXCEL）
- Azure Cognitive ServiceのLUIS

#事象（ハマった所）
##LUISのCulture"ja-jp"で作ってType「Simple」でEntityを作るとメールアドレスの中にスペースが入ってしまう
こんな感じ。プルプル…おしい。LUISサポートに聞いてみた所現状は仕様っぽい。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/d6591fa1-2e86-887e-c1f2-7306d76bff33.png)
これ抽出してからFlowかFunctionsでスペース抜くかとも思ったけど。もう少しLUIS側で頑張る方法あるか調べてみた。
###解決方法
Culture"en-us"で作って[Manage prebuilt entities]から規定の"email"を作って、さらにそれをCompositeタイプのEntity作って
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/44a5ae07-2869-3a6c-9dbe-f58348817244.png)
抽出したい箇所のemail部分をCompositタイプのEntityでラップする
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/3eaeceaf-9554-cbdc-9ffd-1da745fa7e33.png)
そうすると…
ぉお！！compositeEntitiesの中のchidrenの中に欲しい情報が入っているではないか！これ取り出したい！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/356079f2-75d3-23f0-7591-7eac746bda5b.png)

```:json
"compositeEntities": [ { "parentType": "FlowEmail", "value": "piyo . piyo @ example . ne . jp", "children": [ { "type": "builtin.email", "value": "piyo.piyo@example.ne.jp" } ] } ] }
```

##二次元配列のデータをFlowで抽出する
###FlowでLUISとつなぐ
Flowの「Get prediction」コネクタ追加してメール本文を入力にします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/414ce70a-1dcf-bae2-df1a-4d5179d97171.png)

###開始位置を特定
データ操作コネクタで[データ操作]の中の[作成]というアクションコネクタ追加します
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/f9e56312-c0ce-a499-5bdc-a0e9a5f96c35.png)
入力フォームで関数`add(indexof(body('Get_prediction')?['luisPrediciton'],'children'),12)`入力して追加します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/11270bd6-ce93-cdc5-599b-4d783da0bb12.png)

>[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)を読みます。
文字列検索するためのindexOf関数でchildrenの"c"の開始位置の取得ができます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/703dc9c1-c822-4094-b16a-16b95f5a3eef.png)
その開始位置に欲しい情報の開始位置までの12を加算したいのでadd関数を使います
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/ea79ea25-6082-9e10-b10b-bce198b65bbb.png)
`body('Get_prediction')?['luisPrediciton']`は手動で入れなくても動的コンテンツで[LUIS Prediction]を選択すれば簡単に入力できます。

###終了位置を特定
ここまでで`"type": "builtin.email", "value": "piyo.piyo@example.ne.jp" } ] } ] }`の最初の「"」の位置が特定できました。次は終了位置を特定させます。
最後の`} ] } ] }`が固定なのでその位置を探せばよいので`lastIndexOf(body('Get_prediction')?['luisPrediciton'],'}]}]}')`を追加します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/438f78a8-843b-a51b-7a48-19ab5cbc2144.png)

>これは[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)にあるlastindexof記述箇所参考です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/c8968d44-3c08-ad09-add7-9faab3e5b0d4.png)

###開始から終了までの文字を抜き出す
はい。ここで開始と終了位置がわかったのでその間の文字を抽出します。`substring(body('Get_prediction')?['luisPrediciton'],outputs('作成'),sub(outputs('作成_2'),outputs('作成')))`カッコの中は全て動的コンテンツをクリックして追加します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/1636a81b-f512-da43-7ac7-7aa2de59ef0a.png)

>これは[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)にあるsubstring関数記述箇所参考です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/0be7e43b-2d09-8f64-77c2-ef76780292be.png)
さらにsubstringの第3パラメータとして何文字分をとるかを指定する必要があるので作成_2から作成を引いた数を出すためのsub関数を使います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/efd091de-d2be-599f-0810-76425907d0b0.png)

同じコネクタ使ってるんでどれがどれだか混乱する…あとからわかるようにコネクタの名前を変えておけばよかったかも。
ここまでで`"type":"builtin.email","value":"piyo.piyo@example.ne.jp"`が抽出できました。

###「,」で分割
次にsplit 関数で「 , (カンマ) 」を基準に配列化します。`split(outputs('作成_3'),',')`を追加します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/ae7dfa41-c9a2-fa18-a4b4-d4e67d9d9294.png)

>これは[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)にあるsplit関数記述箇所参考です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/da4fd270-a58f-7c05-569f-94f073920378.png)

ここまでで以下のような配列データが返されてます。

```:json
[
  "\"type\":\"builtin.email\"",
  "\"value\":\"piyo.piyo@example.ne.jp\""
]
```

###最後の配列データを「:」で分割
typeとvalueという2つの配列のうちvalueというレコードに必要な値が含まれるので、split関数で「:(コロン)」を基準に配列化します。`split(last(outputs('作成_4')),':')`を追加します。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/8bed8551-9e1f-8f9d-378c-f08b13109d05.png)

>これは[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)にあるlast関数記述箇所参考です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/2dc1a625-64c7-d7d1-b9da-477d4f6080f4.png)

ここまでで以下のような配列データが返されています。この最後の配列`"\"piyo.piyo@example.ne.jp\""`から不要な記号を2段階で除去します。

```:json
[
  "\"value\"",
  "\"piyo.piyo@example.ne.jp\""
]
```

###不要なデータを除去
最初に「\"」を抜き、さらに「"」を抜きます。`replace(replace(last(outputs('作成_5')),'\"',''),'"','')`
>これは[定義言語スキーマドキュメント](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)にあるreplace関数記述箇所参考です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/461ad7be-3ec9-093b-8e5c-e54a9aa0cd1b.png)

##保存してテスト
保存し
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/5f60489f-c687-254a-6e22-cf8f0039f829.png)
右上のテストをクリック
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/b1a41c16-cb31-360c-72b8-b454d2f1b479.png)
テスト実行
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/705a8c54-8693-2fb6-dc81-847ffa42edb1.png)
すべてのフローで正常終了すればOK
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/997fb76b-bded-1a76-9fc5-3d13ad309719.png)

それぞれのコネクタクリックすると実行結果が見えます。エラーになったらその結果ごとに修正します。実際私何回もエラーになってデバッグしながら作った感じ。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/527153cb-1391-2499-5dae-f91c2c3cfee8.png)
BoxのEXCELテーブルに出力するようコネクタ追加して
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/e03fe965-5b1f-5e2e-cdf3-92034a94833b.png)

>この手順は[ここ](https://qiita.com/taketakekaho/items/d208dcb8851349b342ea)参考

メール送ってみると
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/8cd4b27d-57f8-80a0-bd7c-e43801378999.png)

でけた～～！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178016/9d0e27a8-7c4f-34e7-7b5d-225c73637542.png)


データ操作コネクタを1個にすることもできるけどデバッグしやすいように複数で作りました。
もっといい方法知ってる！って方いたら是非教えてください。
とりあえずLUISとJSON操作の勉強にはなったので満足です。


#参考にしたもの
- [「AI」すごいぜ！　メールから自動で請求書作成にノンプログラミングでチャレンジした話](https://internet.watch.impress.co.jp/docs/column/shimizu/1110337.html)
- [LUIS composite entities](https://docs.microsoft.com/en-us/azure/cognitive-services/LUIS/add-entities#add-composite-entities)
- [ワークフロー定義言語スキーマ-](https://docs.microsoft.com/ja-jp/azure/logic-apps/logic-apps-workflow-definition-language)