SharePointの大規模なリストに対応するPower Appsを作成する

# はじめに
先日[YouTubeで下記の動画](https://youtu.be/rLsZoW8GH_k?si=mD1rwaN17nIA2bud)を拝見しました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/rLsZoW8GH_k" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

[SharePoint の HTTP 要求フローの送信アクション](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request)を実行することで、大規模なデータセット（リスト）に対応させている例です。

:::note warn
有識者の方からは、SharePointはデータベースではないと言われておりますので、
限られたシーンの際にご利用ください。
:::

記載した動画を解説すると
 - **Power Appsのデータ行の制限を2000（最大値）にする**
 - **[SharePoint の HTTP 要求フローの送信アクション](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request)で、「次ページへのリンク」と「取得したリストの行データ」をJSONで取得する**
 - **Power Appsで、[ParseJSON 関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-parsejson)を使い、値を表示する**

といった方法で成り立っています。

https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-parsejson

# 日本地図アプリを作成して、効果を体験してみる

今回は、[郵便番号データ](https://www.post.japanpost.jp/zipcode/download.html)をもとにSharePointリストとして作成し、アプリケーションを作ります。

https://www.post.japanpost.jp/zipcode/download.html

[124,675行の大規模データ](https://www.post.japanpost.jp/zipcode/dl/oogaki/zip/ken_all.zip)です。
※2024年 1月 31日更新分

:::note alert
あくまで実験目的です。
:::

## ■ 機能
1. 郵便番号データを、[SharePoint の HTTP 要求フローの送信アクション](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request)で取得する
1. 郵便番号から、[HeartRails Geo API](http://geoapi.heartrails.com/)を利用し、緯度・経度を取得する
1. Power Appsの[対話型マップ コントロール](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map)を使い、地図に郵便番号の場所を表示する
1. 都道府県を選択するコンボボックスを設けて、住所をフィルターする

http://geoapi.heartrails.com/

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map

:::note warn
[対話型マップ コントロール](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map)は、使用できるライセンスが制限されます。
詳しくは下記をご参照ください。
:::

https://learn.microsoft.com/ja-jp/power-platform/admin/pricing-billing-skus

# SharePoint リスト
かなり雑に作ってしまいました。
リストの中身は下記のようになっています。

|列名|内部名|データ型|インデックス
|---|---|---|---
|郵便番号|Title|1 行テキスト
|都道府県カナ|field_3|1 行テキスト
|市区町村カナ|field_4|1 行テキスト
|町名番地カナ|field_5|1 行テキスト
|都道府県|field_6|1 行テキスト|**インデックス付きの列**
|市区町村|field_7|1 行テキスト
|町名番地|field_8|1 行テキスト

内部名が滅茶苦茶なのは、インポートをミスったからです・・・。
またデータ型もテキトーです（苦笑

後述しますが、[SharePoint の HTTP 要求フローの送信アクション](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request)で、行を取得することに当たり、フィルターを実装するために**インデックス付きの列**に`都道府県`の列を設定しています。

大規模データセットの場合、インデックスが付与されていないと、正しく機能しません。

# Power Apps
モダン コントロールを、多く採用し、下記のようなレイアウトにしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/7930f5e7-716f-7ebb-ed2d-98bd8064f3fb.png)

このアプリケーションで、`フォーム`は、データを`表示のみ`実施する機能となっています。
SharePointリストを更新するためには、更新を実施するための **[SharePoint の HTTP 要求フローの送信アクション](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request)** が必要になります。

## 実装している関数

![data.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/be0fc3f4-011c-35c8-5840-80bf5afaadd3.jpeg)

```plaintext:OnVisible（操作する画面）①
UpdateContext({ListData:ParseJSON(GetSPListsItems.Run("Postcode","$top=20").response)});
ClearCollect(colM,Table(ListData.results));
```
- `ListData`は、Power Automateを経由して取得したSharePointのデータをParseJSON関数を経て、格納しています
- テーブルのデータは、ListDataの`results`

```plaintext:OnSelect（Homeボタン）②
If(CountRows(ComboboxCanvas1.SelectedItems) = 1,
    UpdateContext({ListData:ParseJSON(GetSPListsItems.Run("Postcode","$filter=field_6 eq '" & ComboboxCanvas1.Selected.Value & "'&$top=20").response)});,
    UpdateContext({ListData:ParseJSON(GetSPListsItems.Run("Postcode","$top=20").response)});  
);
ClearCollect(colM,Table(ListData.results));
```

- 都道府県を選択するコンボボックスに応じて、Power Automateに渡す値を変えています
    - コンボボックスに値がある場合、フィルター条件を文字列に追加し、上位20件の値を取得
    - コンボボックスに値がない場合、上位20件の値を取得
    - その他の挙動は、`OnVisible`と同様

```plaintext:OnSelect（Nextボタン）③
UpdateContext({ListUrl:Last(Split(Text(ListData.__next),"?")).Value});
UpdateContext({ListData:ParseJSON(GetSPListsItems.Run("Postcode",ListUrl).response)});
ClearCollect(colM,Table(ListData.results));
```
- JSONから`__next`を取得します。ここから、ページングされたことによる、次の検索クエリの結果を取得することができます（ **例:** 前回の検索結果からみて、次の上位20件のレコードの検索結果）
- `__next`を`?`で分割した最後の値を、検索用URLとしてPower Automateに渡します
- 戻り値を再度コレクションに新しく格納します(ClearCollect関数を使って、毎回リフレッシュしています)

```plaintext:OnSelect（郵便番号データ ギャラリー）④
UpdateContext({GeoInfo:GetGeoInfomation.Run(ThisItem.Value.Title)});
```
- ギャラリーの郵便番号データが選択されたときに、選択された郵便番号をもとに、[HeartRails Geo API](http://geoapi.heartrails.com/)を利用し、緯度・経度を取得します

# Power Automate
Power Automateの内容は、かなりエッセンシャルな内容になります。

## SharePointから行データを取得 - GetSPListsItems

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/5bef77a1-7fbd-45f2-8f5f-d4e6f1c4170e.png)

- メソッドは`GET`
- URI
```
_api/web/lists/GetByTitle('{-リスト名-}')/Items?{-検索条件-}
```

 - リスト名は`ListTitle`として、検索条件は`ListURL`として、Power Appsから値を受け取ります

:::note warn
[JSON ライトを使用](https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request#use-json-light)した場合、検索クエリで利用する`__next`の値が取得できません。
:::

https://learn.microsoft.com/ja-jp/sharepoint/dev/business-apps/power-automate/guidance/working-with-send-sp-http-request#use-json-light

ここで大切になることは、フィルターを実装するにあたり、
**評価の対象となる列が[インデックス付き列](https://support.microsoft.com/ja-jp/office/%E3%83%AA%E3%82%B9%E3%83%88%E3%81%BE%E3%81%9F%E3%81%AF%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E5%88%97%E3%81%AB%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B-f3f00554-b7dc-44d1-a2ed-d477eac463b0)に設定されていないと、検索クエリが機能しません。**

https://support.microsoft.com/ja-jp/office/%E3%83%AA%E3%82%B9%E3%83%88%E3%81%BE%E3%81%9F%E3%81%AF%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E5%88%97%E3%81%AB%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B-f3f00554-b7dc-44d1-a2ed-d477eac463b0

SharePoint側の設定が必要になるので、ご注意ください。

## 地理的な情報を取得 - GetGeoInfomation - [HeartRails Geo API](http://geoapi.heartrails.com/)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/9a09ffac-0970-ef0a-1091-5d1b5894f4df.png)

 - Power Appsから郵便番号を`ハイフンなし半角数字7桁`で受け取ります
 - メソッドは`GET`
- URI
```
http://geoapi.heartrails.com/api/json?method=getStations&postal={-郵便番号ハイフンなし半角数字7桁-}
```

郵便番号が`060-0941`の場合、APIの戻り値は下記のようになります。

```json
{
  "response": {
    "station": [
      {
        "name": "西線14条",
        "kana": "にっせんじゅうよじょう",
        "line": "札幌市電",
        "y": 43.040775,
        "x": 141.336429,
        "postal": "0640914",
        "prev": "西線11条",
        "next": "西線16条",
        "prefecture": "北海道",
        "distance": 1273.3884260023942
      }
    ]
  }
}
```

緯度や経度もあって嬉しいですね！
配列の最初の値のみ取得する形で、Power Automateを作りました。

```plaintext:JSONの解析アクションで使用する値
body('HTTP')?['response']?['station'][0]
```

```plaintext:スキーマ
{
    "type": "object",
    "properties": {
        "name": {
            "type": "string"
        },
        "kana": {
            "type": "string"
        },
        "line": {
            "type": "string"
        },
        "y": {
            "type": "number"
        },
        "x": {
            "type": "number"
        },
        "postal": {
            "type": "string"
        },
        "prev": {
            "type": "string"
        },
        "next": {
            "type": "string"
        },
        "prefecture": {
            "type": "string"
        },
        "distance": {
            "type": "number"
        }
    }
}
```

# かなりヌルヌルサクサク！
**非常にレスポンスが良く、大規模データセットでも機能します！**
`複数の行を取得する`アクションが、すでにPower Automateで存在しますが、速度の違いも0.3秒と数秒と段違いです。

**やっていることは全くローコードではないと思いますが、
Power Apps / Power Automate活用を更に尖らせる意味で良いですね！**

大規模データセットを採用する場合、`Dataverse`が望ましいです。用法用量を考えて、活用してください。


# 追加！[対話型マップ コントロール](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map)

さくっと、[対話型マップ コントロール](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map)を含めた赤枠の箇所を解説します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/a9586b99-b442-7a58-bb1d-b380c3af1da8.png)

具体的な要素はこちら

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/5c282401-fc17-e4c5-b4b5-a2b9244d1994.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/c80a7879-eb69-dbd6-ee6b-1c8092f8db1a.png)

 - テンプレートで配置されている`MainContainer`
     - conHeader - 水平コンテナー（ヘッダー用）
         - icoHeader - 地点のアイコン
         - lblHeader - Map!!という文字
     - conBody - 水平コンテナー（ボディ用）
         - フォームコントロール
         - マップコントロール

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/e77e1594-aeae-2b24-bd76-8724994b7dfd.png)

## ヘッダー部分
アプリ内で色を統一することが好きなので、`conHeader`の`Fill`を下記の通り設定しました。

```
App.Theme.Colors.Darker10
```

このように設定することで、Power Appsのテーマに合わせた色の統一ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/b9185d46-178e-2518-88f3-f870b6d31654.png)

:::note info
私のお気に入りは`Teams`です
:::

## ボディ部分
`conBody(Container)`には、上下左右に`パディング`を`10`設けました。

### Formコントロール
今回使用しているのは、モダンコントロールのFormです。

`Width（幅）`は`Parent.Width / 2.5`に設定しています。
`ParseJSON 関数`で評価した値を、どう表示するか解説すると、
フォームの`フィールド`から`三点リーダー`を選択し、`カスタム カード`を、表示したい要素分、追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/ed5c3ab8-1eb4-9464-2846-506ad5f7ed11.png)

`DataCard`が追加されるので、この要素の`DataField`プロパティに、Formのうち割り当てる`列`を設定します。

```plaintext:ParseJSON関数で取得したfield_6を表示する場合
ThisItem.Value.field_6
```

あとは必要に応じて、`テキストラベル`など、コントロールを追加しましょう。
`テキストラベル`に表示する値を反映するには、下記を設定することで、
データソースから値を反映することができます。

```
Parent.DataField
```

### [対話型マップ コントロール](https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/geospatial-component-map)

あまり工夫はできていません（苦笑
そもそもが優秀な機能なので、下記の部分のみ設定しております。

|項目|値|解説
|---|---|---
|CurrentLocation|true|現在地のオンオフ　ピコピコするマークとして使用
|CurrentLocationLatitude|GeoInfo.y|現在地の緯度を変数で決定、APIの戻り値を使用
|CurrentLocationLongitude|GeoInfo.x|現在地の経度を変数で決定、APIの戻り値を使用
|DefaultLatitude|GeoInfo.y|デフォルト（マップの中心地）の緯度を変数で決定、APIの戻り値を使用
|DefaultLongitude|GeoInfo.x|デフォルト（マップの中心地）の経度を変数で決定、APIの戻り値を使用
|DefaultZoomLevel|8|拡大、縮小を設定
|MapStyle|'Map.MapStyle'.SatelliteRoadLabels|

**私の地元だとこんな感じで映ります！**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/e904ab39-f21a-f5fc-02bb-8676e092636e.png)