次の方法で共有


TripPin パート 2 - REST サービスのデータ コネクタ

このマルチパート チュートリアルでは、Power Query 用の新しいデータ ソース拡張機能の作成について説明します。 このチュートリアルは順番に行う予定です。各レッスンは、前のレッスンで作成したコネクタに基づいて構築され、コネクタに新しい機能を段階的に追加します。

このレッスンでは、次の操作を行います。

  • Web.Contents を使用して REST API を呼び出す基本関数を作成する
  • 要求ヘッダーを設定し、JSON 応答を処理する方法について説明します
  • Power BI Desktop を使用して応答をユーザー フレンドリな形式にラングリングする

このレッスンでは、 TripPin サービス の OData ベースのコネクタ ( 前のレッスンで作成) を、RESTful API 用に作成したコネクタに変換します。 OData は RESTful API ですが、一連の規則が固定されています。 OData の利点は、スキーマ、データ取得プロトコル、標準クエリ言語を提供することです。 OData.Feed の使用をやめると、これらの機能をコネクタに自分で組み込む必要があります。

OData コネクタの要約

コネクタから OData 関数を削除する前に、サービスからデータを取得するために、現在 (主にバックグラウンドで) 行っていることを簡単に確認しましょう。

Visual Studio Code の パート 1 から TripPin 拡張機能プロジェクトを開きます。 クエリ ファイルを開き、次のクエリを貼り付けます。

TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")

Fiddler を開き、Visual Studio Code で現在の Power Query ファイルを評価します。

Fiddler では、サーバーに対して次の 3 つの要求があります。

3 つの OData 要求が表示された Fiddler 出力画面のスクリーンショット。

  • /Me: 要求している実際の URL。
  • /$metadata: 応答に関するスキーマと型の情報を決定するために、 OData.Feed 関数によって自動的に呼び出されます。
  • /Me/BestFriend: /Me シングルトンを一覧表示したときに (熱心に) プルされたフィールドの 1 つ。 この場合、呼び出しの結果、 204 No Content 状態が発生しました。

M の評価はほとんど遅延です。 ほとんどの場合、データ値は必要なときにのみ取得/プルされます。 値が積極的に取得されるシナリオ (/Me/BestFriend の場合など) があります。 この動作は、メンバーの型情報が必要な場合に発生する傾向があり、エンジンは値を取得して検査する以外に型を決定する方法はありません。 M コネクタのパフォーマンスを向上させるための重要な要素のひとつは、処理を遅延させること(つまり、頻繁にプルすることを避ける)です。

要求と共に送信された要求ヘッダーと、/Me 要求の応答の JSON 形式を確認します。

{
  "@odata.context": "https://services.odata.org/v4/TripPinService/$metadata#Me",
  "UserName": "aprilcline",
  "FirstName": "April",
  "LastName": "Cline",
  "MiddleName": null,
  "Gender": "Female",
  "Age": null,
  "Emails": [ "April@example.com", "April@contoso.com" ],
  "FavoriteFeature": "Feature1",
  "Features": [ ],
  "AddressInfo": [
    {
      "Address": "P.O. Box 555",
      "City": {
        "Name": "Lander",
        "CountryRegion": "United States",
        "Region": "WY"
      }
    }
  ],
  "HomeAddress": null
}

クエリの評価が完了すると、PQTest の結果ウィンドウに Me シングルトンのレコード値が表示されます。

Me シングルトンの Record 値を示す PQTest 結果のスクリーンショット。

出力ウィンドウのフィールドと生の JSON 応答で返されるフィールドを比較すると、不一致が発生します。 クエリ結果には、JSON 応答のどこにも表示されない他のフィールド (FriendsTripsGetFriendsTrips) があります。 OData.Feed 関数は、$metadataによって返されたスキーマに基づいて、これらのフィールドをレコードに自動的に追加しました。 この違いは、より優れたユーザー エクスペリエンスを提供するために、コネクタがサービスからの応答を拡張または再フォーマットする方法の良い例です。

基本的な REST コネクタの作成

次に、 Web.Contents を呼び出す新しいエクスポートされた関数をコネクタに追加します。

ただし、OData サービスに対して Web 要求を成功させるには、 いくつかの標準 OData ヘッダーを設定する必要があります。 これを行うには、コネクタで共通のヘッダーセットを新しい変数として定義します。

DefaultRequestHeaders = [
    #"Accept" = "application/json;odata.metadata=minimal",  // column name and values only
    #"OData-MaxVersion" = "4.0"                             // we only support v4
];

TripPin.Feedを使用するのではなく、OData.Feed を使用して Web 要求を行い、結果を JSON ドキュメントとして解析するように、関数の実装を変更します。

TripPinImpl = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source)
    in
        json;

コネクタ ファイルに変更を加えたので、必ずコネクタをビルドしてください。 その後、クエリ ファイル (TripPin.query.pq) を評価できます。 /Me レコードの結果は、Fiddler 要求にあった生の JSON に似ています。

新しい関数の実行時に Fiddler を監視すると、評価によって 3 つではなく 1 つの Web 要求が行われるようになったことがわかります。 おめでとうございます。300% パフォーマンスの向上を達成しました。 これですべての型とスキーマの情報が失われましたが、その部分に焦点を当てる必要はまだありません。

次のような一部の TripPin エンティティ/テーブルにアクセスするようにクエリを更新します。

  • https://services.odata.org/v4/TripPinService/Airlines
  • https://services.odata.org/v4/TripPinService/Airports
  • https://services.odata.org/v4/TripPinService/Me/Trips

適切に書式設定されたテーブルを返すために使用されたパスは、[リスト] が埋め込まれた最上位レベルの "値" フィールドを返すようになったことがわかります。 エンド ユーザーの使用シナリオで使用できるようにするには、結果にいくつかの変換を行う必要があります。

埋め込みリストを含む値フィールドを含む PQTest 結果のスクリーンショット。

Power Query での変換の作成

M 変換を手動で作成することは可能ですが、ほとんどのユーザーは Power Query を使用してデータを整形することを好みます。 Power BI Desktop で拡張機能を開き、それを使用してクエリを設計し、出力をよりわかりやすい形式に変換します。 ソリューションをリビルドし、新しい拡張機能ファイルを Custom Data Connectors ディレクトリにコピーして、Power BI Desktop を再起動します。

新しい空のクエリを開始し、数式バーに次の行を貼り付けます。

= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")

必ず = 記号を含めます。

元の OData フィード (AirlineCode と Name の 2 つの列を含むテーブル) のように見えるまで出力を操作します。

AirlineCode 列と Name 列を含む Power Query テーブルのスクリーンショット。

結果のクエリは次のようになります。

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines"),
    value = Source[value],
    toTable = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expand = Table.ExpandRecordColumn(toTable, "Column1", {"AirlineCode", "Name"}, {"AirlineCode", "Name"})
in
    expand

クエリに名前を付けます ("Airlines")。

新しい空のクエリを作成します。 今回は、 TripPin.Feed 関数を使用して /Airports エンティティにアクセスします。 次の表のような結果が得られるまで変換を適用します。 一致するクエリは次の表に従います。このクエリには名前 ("Airports") も付けます。

変換されたテーブルを含む Power Query テーブルのスクリーンショット。

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airports"),
    value = Source[value],
    #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"Name", "IcaoCode", "IataCode", "Location"}, {"Name", "IcaoCode", "IataCode", "Location"}),
    #"Expanded Location" = Table.ExpandRecordColumn(#"Expanded Column1", "Location", {"Address", "Loc", "City"}, {"Address", "Loc", "City"}),
    #"Expanded City" = Table.ExpandRecordColumn(#"Expanded Location", "City", {"Name", "CountryRegion", "Region"}, {"Name.1", "CountryRegion", "Region"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded City",{{"Name.1", "City"}}),
    #"Expanded Loc" = Table.ExpandRecordColumn(#"Renamed Columns", "Loc", {"coordinates"}, {"coordinates"}),
    #"Added Custom" = Table.AddColumn(#"Expanded Loc", "Latitude", each [coordinates]{1}),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "Longitude", each [coordinates]{0}),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"coordinates"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Name", type text}, {"IcaoCode", type text}, {"IataCode", type text}, {"Address", type text}, {"City", type text}, {"CountryRegion", type text}, {"Region", type text}, {"Latitude", type number}, {"Longitude", type number}})
in
    #"Changed Type"

このプロセスは、サービスの下の他のパスに対して繰り返すことができます。 準備ができたら、(モック) ナビゲーション テーブルを作成する次の手順に進みます。

ナビゲーション テーブルのシミュレート

ここでは、適切に書式設定された TripPin エンティティを表示するテーブル (M コードを使用) を作成します。

新しい空のクエリを開始し、詳細エディターを表示します。

次のクエリを貼り付けます。

let
    source = #table({"Name", "Data"}, {
        { "Airlines", Airlines },
        { "Airports", Airports }
    })
in
    source

[プライバシー レベル] 設定を [常にプライバシー レベルの設定を無視する] ("高速結合" とも呼ばれます) に設定していない場合は、プライバシー プロンプトが表示されます。

Power Query のプライバシー プロンプトのスクリーンショット。

複数のソースのデータを結合していて、1 つ以上のソースのプライバシー レベルを指定しなかった場合、プライバシープロンプトが表示されます。 [ 続行 ] ボタンを選択し、トップ ソースのプライバシー レベルを [パブリック] に設定します。

トップ ソースのプライバシー レベルを [パブリック] に設定する [プライバシー レベル] ダイアログのスクリーンショット。

[ 保存] を 選択すると、テーブルが表示されます。 このテーブルはまだナビゲーション テーブルではありませんが、後続のレッスンで 1 つに変換するために必要な基本的な機能を提供します。

TripPin データを含むテーブルのスクリーンショット。

拡張機能内から複数のデータ ソースにアクセスする場合、データの組み合わせチェックは行われません。 拡張機能内から行われたすべてのデータ ソース呼び出しは同じ承認コンテキストを継承するため、組み合わせることは "安全" であると想定されます。 データの組み合わせルールに関しては、拡張機能は常に 1 つのデータ ソースとして扱われます。 ソースを他の M ソースと組み合わせると、ユーザーは通常のプライバシー プロンプトを受け取ります。

Fiddler を実行し、Power Query エディターで [プレビューの更新 ] ボタンを選択した場合は、ナビゲーション テーブル内の各項目に対する個別の Web 要求に注意してください。 これらの個別の要求は、一括評価が行われていることを示しています。これは、多くの要素を含むナビゲーション テーブルを作成する場合には理想的ではありません。 以降のレッスンでは、遅延評価をサポートする適切なナビゲーション テーブルを作成する方法について説明します。

Conclusion

このレッスンでは、REST サービス用の単純なコネクタを構築する方法について説明しました。 この場合、既存の OData 拡張機能を ( Web.Contents を使用して) 標準の REST 拡張機能に変えましたが、新しい拡張機能を最初から作成する場合も同じ概念が適用されます。

次のレッスンでは、Power BI Desktop を使用してこのレッスンで作成したクエリを、拡張機能内の真のナビゲーション テーブルに変換します。

次のステップ

TripPin パート 3 - ナビゲーション テーブル