Azure DevOps の OData Analytics クエリ ガイドライン

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

拡張機能開発者は、Azure DevOps 用の Analytics に対する効率的な OData クエリを設計するために、この記事に記載されているガイドラインに従うことでメリットを得ることができます。 これらのガイドラインに従うと、クエリの実行時間とリソース消費に対するパフォーマンスが向上します。 これらのガイドラインに準拠していないクエリでは、レポートの待機時間が長い、許可されたリソース消費を超えるクエリ、サービスのブロックなど、パフォーマンスが低下する可能性があります。

Note

Analytics サービスは、すべての Azure DevOps Services で自動的に有効になり、運用環境でサポートされます。 Power BI の統合 と、Analytics サービスの OData フィード へのアクセスが一般公開されています。 お使いいただき、フィードバックをお寄せください。 使用可能なデータはバージョンによって異なります。 サポートされている最新バージョンは v2.0、最新のプレビュー バージョンです v4.0-preview。 詳細については、OData API のバージョン管理に関するページを参照してください

Note

Analytics サービスは、Azure DevOps Server 2020 以降のすべての新しいプロジェクト コレクションに対して、運用環境で自動的にインストールされ、サポートされます。 Power BI の統合 と、Analytics サービスの OData フィード へのアクセスが一般公開されています。 お使いいただき、フィードバックをお寄せください。 Azure DevOps Server 2019 からアップグレードした場合は、アップグレード中に Analytics サービスをインストールできます。

使用可能なデータはバージョンによって異なります。 サポートされている最新バージョンは v2.0、最新のプレビュー バージョンです v4.0-preview。 詳細については、OData API のバージョン管理に関するページを参照してください

Note

Analytics サービスは、Azure DevOps Server 2019 のプレビュー段階です。 プロジェクト コレクションに対して有効またはインストールできますPower BI の統合 と Analytics サービスの OData フィード へのアクセスはプレビュー段階です。 お使いいただき、フィードバックをお寄せください。

使用可能なデータはバージョンによって異なります。 サポートされている最新バージョンは v2.0、最新のプレビュー バージョンです v4.0-preview。 詳細については、OData API のバージョン管理に関するページを参照してください

これらのガイドラインには、DO、CONSIDER、AVOID、DON'T という用語が付いた推奨事項があります。 Analytics によって適用される制限付きルールには、[BLOCKED] プレフィックスが含まれています。 異なるソリューション間のトレードオフを理解する必要があります。 特定の状況では、1 つ以上のガイドラインに違反することを強制するデータ要件が存在する可能性があります。 このようなケースはまれです。 このような決定を行う明確で説得力のある理由を持っていることをお勧めします。

ヒント

このドキュメントに示されている例は、Azure DevOps Services URL に基づいています。 オンプレミスのバージョンでは置換を使用します。

https://{servername}:{port}/tfs/{OrganizationName}/{ProjectName}/_odata/{version}/

エラーと警告メッセージ

✔️ OData 応答の警告を確認する

実行する各クエリは、一連の定義済みルールに対してチェックされます。 違反は、次の OData 応答を返します @vsts.warnings。 クエリを改善する方法に関する現在の情報と状況依存の情報が提供されるため、これらの警告を確認します。

{
  "@odata.context": "https://{OrganizationName}.tfsallin.net/_odata/v1.0/$metadata#WorkItems",
  "@vsts.warnings": [
    "The specified query does not include a $select or $apply clause which is recommended for all queries."
  ],
  ...
}

✔️ OData エラー メッセージを確認する

OData エラー ルールに違反するクエリでは、400 (無効な要求) 状態コードで応答が失敗します。 関連付けメッセージはプロパティ内に @vsts.warnings 表示されません。 代わりに、JSON 応答のプロパティに message エラー メッセージが生成されます。

{
  "error": {
  "code": "0",
  "message": "The query specified in the URI is not valid. The Snapshot tables in Analytics are intended to be used only in an aggregation."
  }
}

制限

推奨

次の例を考えてみましょう

ブロック

回避

✔️ アクセスできるプロジェクトにクエリを制限する

クエリがアクセス権を持たないプロジェクトのデータを対象とする場合、クエリは "Project access denied" メッセージを返します。 アクセス権を持っていることを確認するには、クエリを実行するすべてのプロジェクトに対して [分析の 表示] アクセス許可が [許可] に設定されていることを確認します。 詳細については、「Analytics にアクセスするために必要なアクセス許可」を参照してください

プロジェクトにアクセスできない場合は、次のメッセージが表示されます。

クエリ結果には、アクセス権のない 1 つ以上のプロジェクトのデータが含まれます。 1 つ以上のプロジェクト フィルターを追加して、'WorkItems' エンティティにアクセスできるプロジェクトを指定します。 $expandプロパティまたはナビゲーション プロパティを使用している場合は、それらのエンティティにプロジェクト フィルターが必要です。

この問題を回避するには、プロジェクト フィルターを明示的に追加するか、この記事で後述するようにプロジェクト スコープエンドポイントを使用します。

たとえば、次のクエリは、名前が付けられた {projectSK1} プロジェクトに属する作業項目をフェッチします {projectSK2}

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=ProjectSK eq {projectSK1} or ProjectSK eq {projectSK2}
  &$select=WorkItemId, Title

✔️ 拡張にアクセスできない可能性がある他のプロジェクトにデータが $expand 含まれる可能性がある場合は、句内でプロジェクト フィルターを指定します

ナビゲーション プロパティを展開すると、アクセスできない他のプロジェクトのデータを参照する可能性があります。 アクセスできないデータを参照している場合は、前に 一覧表示されているのと同じエラー メッセージが表示されます。"クエリ結果には、1 つ以上のプロジェクトのデータが含まれています..."。 同様に、展開されたデータを制御する明示的なプロジェクト フィルターを追加することで、この問題を解決できます。

単純なナビゲーション プロパティの場合は、通常 $filter の句でこれを行うことができます。 たとえば、次のクエリでは、リンクとそのターゲットの両方が同じプロジェクト内のどこに存在するのかを明示的に要求 WorkItemLinks します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
  $filter=ProjectSK eq {projectSK} and TargetWorkItem/ProjectSK eq {projectSK}
  &$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
  &$expand=TargetWorkItem($select=WorkItemId, Title)

代わりに、句でフィルターを展開オプションに$filter$expand移動できます。 ただし、クエリのセマンティックが変更されます。 たとえば、次のクエリは、特定のプロジェクトからすべてのリンクを取得し、条件に応じてターゲットが同じプロジェクトに存在する場合にのみ展開します。 有効ですが、この方法では、プロパティが展開されていないかどうか、または除外されたため null に展開されていないかどうかを判断するのが難しい場合があるため、混乱を招く可能性があります。この特定の動作が本当に必要な場合にのみ、このソリューションを使用してください。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemLinks?
  $filter=ProjectSK eq {projectSK}
  &$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
  &$expand=TargetWorkItem($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)

$filter展開オプションは、エンティティ セットなどのChildren展開コレクション プロパティを使用する場合にWorkItems便利です。 たとえば、次のクエリは、特定のプロジェクトのすべての作業項目と、同じプロジェクトに属するすべての子を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=ProjectSK eq {projectSK}
  &$select=WorkItemId, Title
  &$expand=Children($filter=ProjectSK eq {projectSK}; $select=WorkItemId, Title)

次のいずれかのプロパティを展開する場合は、フィルターを指定します。

  • WorkItems エンティティ セット: Parent, Children
  • WorkItemLinks エンティティ セット: TargetWorkItem.

✔️ プロジェクト スコープ エンドポイントを使用したクエリの検討

1 つのプロジェクトのデータに関心がある場合は、プロジェクト スコープの OData エンドポイント (/{ProjectName}/_odata/v1.0) を使用することをお勧めします。 前の 2 つのセクションで説明した問題を回避し、1 つのプロジェクト、参照されるエンティティ セット、およびすべての展開されたナビゲーション プロパティに対して暗黙的にデータをフィルター処理します。

この簡略化により、前のセクションのクエリを次の形式に書き直すことができます。 expand 句のフィルターが消えただけでなく、メイン エンティティ セットにフィルターを適用する必要もありません。

https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItemLinks?
  &$select=LinkTypeReferenceName, SourceWorkItemId, TargetWorkItemId
  &$expand=TargetWorkItem($select=WorkItemId, Title)

作業項目の子のクエリも、はるかに短く簡単です。

https://analytics.dev.azure.com/{OrganizationName}/{ProjectName}/_odata/{version}//WorkItems?
  &$select=WorkItemId, Title
  &$expand=Children($select=WorkItemId, Title)

このソリューションは、フォーカスが 1 つのプロジェクトのデータである場合にのみ適用できます。 プロジェクト間レポートの場合は、前のセクションで説明したフィルター戦略を使用する必要があります。

✔️ クエリが使用制限を超えた場合に操作を待機または停止する

多数のクエリを実行する場合、またはクエリの実行に多くのリソースが必要な場合は、サービスの制限を超えて一時的にブロックされる可能性があります。 サービスの制限を超えた場合は、送信する次のクエリが同じエラー メッセージで失敗する可能性が高いので、操作を停止します。

名前空間 '{namespace}' のリソース '{resource}' の使用量を超えたため、要求がブロックされました。

レート制限の詳細については、「レート制限」を参照してください。 効率的な OData クエリを設計する方法については、この記事で後述する パフォーマンス ガイドラインを 参照してください。

✔️ クエリがタイムアウトで失敗した場合に操作を待機または停止する

使用制限の超過と同様に、クエリがタイムアウトになった場合は、操作を待機または停止する必要があります。 一時的な問題を通知する可能性があるため、1 回再試行して問題が解決するかどうかを確認できます。 ただし、永続的なタイムアウトは、クエリの実行にコストがかかりすぎる可能性があることを示します。 再試行を行うと、使用制限を超えるだけでブロックされます。

TF400733: 要求が取り消されました:要求が要求のタイムアウトを超えました。もう一度やり直してください。

タイムアウトは、クエリに最適化が必要であることを示します。 効率的な OData クエリを設計する方法については、この記事で後述するパフォーマンス ガイドラインを参照してください

❌[ブロック]集計以外スナップショットエンティティを使用しない

サフィックスをSnapshot持つスナップショット エンティティ セットは、毎日のスナップショットとしてモデル化されているため、特別です。 それらを使用して、過去の各日の終わりに存在していたエンティティの状態を取得できます。 たとえば、クエリを実行 WorkItemSnapshot して 1 つの WorkItemIdフィルター処理を行うと、作業項目が作成されてから 1 日ごとに 1 つのレコードが取得されます。 このデータをすべて直接読み込むとコストが高くなり、使用制限を超え、ブロックされる可能性が最も高くなります。 ただし、これらのエンティティの集計はどちらも許可され、推奨されます。 実際、スナップショット エンティティ セットは集計シナリオを念頭に置いて設計されています。

たとえば、次のクエリでは、2020 年 1 月にどのように成長したかを確認するために、日付ごとの作業項目の数を取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
  $apply=
    filter(DateSK ge 20200101 and DateSK le 20200131)/
    groupby((DateSK), aggregate($count as Count))

集計の詳細については、「データの集計」を参照してください

✔️ スナップショット テーブルに集計する場合は、句に含めるかDateValue列をgroupby含めるDateSK

すべてのスナップショットエンティティは毎日のスナップショット テーブルとしてモデル化されるため、グループ化句には常に日のプロパティ (DateSKまたはDateValue) のいずれかを含める必要があります。 そうしないと、結果が正しく表示されない可能性があります。

たとえば、プロパティによってのみAssignedToグループ化WorkItemSnapshotし、カウントで集計した場合、ユーザーに割り当てられた作業項目のすべての数に、各割り当てがアクティブだった日数が乗算されます。 望む結果になる状況はありますが、そのようなケースはまれです。

❌ [ブロック]エンティティ アドレス指定にリソース パスでエンティティ キーを使用しない

OData 構文は、URL セグメントにキーを直接含めることで、特定のエンティティにアクセスする方法を提供します。 詳細については、OData バージョン 4.0 を参照してください 。パート 2: URL 規則 - 4.3 エンティティのアドレス指定。 OData ではこのようなアドレス指定が可能ですが、Analytics によってブロックされます。 クエリ内に含めると、次のエラーが発生します。

URI で指定されたクエリが無効です。 Analytics では、WorkItems(Id) や WorkItem(Id)/AssignedTo などのキーやプロパティのナビゲーションはサポートされていません。 PowerBI でそのエラーが発生した場合は、N+1 の問題を引き起こす正しくない折りたたみを避けるためにクエリを書き直してください。

エラー メッセージのヒントとして、特定のクライアント ツールが直接エンティティのアドレス指定を悪用する可能性があります。 このようなクライアントは、1 つの要求ですべてのデータを読み込む代わりに、エンティティごとに個別にクエリを実行することを選択できます。 要求の数が多くなる可能性があるため、この方法はお勧めしません。 代わりに、次のセクションで説明するように、明示的なエンティティ アドレス指定を使用することをお勧めします。

✔️ フィルター句を使用してエンティティを明示的にアドレス指定する

1 つのエンティティのデータをフェッチする場合は、エンティティのコレクションと同じ方法を使用し、句でフィルターを明示的に定義する $filter 必要があります。

たとえば、次のクエリは、識別子によって 1 つの作業項目を取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=WorkItemId eq {id}
  &$select=WorkItemId, Title

このようなフィルターに含める必要があるプロパティがわからない場合は、メタデータで検索できます。 メタデータのクエリを実行する URL コンポーネントについては、Analytics の OData クエリの構築に関するページを参照してください。 プロパティは 、 KeyEntityType. たとえば、 WorkItemIdRevision エンティティのキー列 WorkItemRevision です。

<EntityType Name="WorkItemRevision">
  <Key>
    <PropertyRef Name="WorkItemId"/>
    <PropertyRef Name="Revision"/>
  </Key>
  [...]
</EntityType>

❌[ブロック]エンティティをWorkItem展開Revisionsしない

Analytics データ モデルでは、特定の種類の拡張が許可されません。 そのうちの 1 つは、エンティティの Revisions コレクション プロパティ WorkItem です。 このプロパティを展開しようとすると、次のエラー メッセージが表示されます。

URI で指定されたクエリが無効です。 プロパティ 'Revisions' は、$expand クエリ オプションでは使用できません。

この制限は、次のセクションで説明するように、すべてのユーザーが推奨されるソリューションを使用することを奨励するために設定されました。このソリューションでは、次のセクションで説明するようにリビジョン WorkItemRevisions がフェッチされています。

✔️ エンティティ セットを使用 WorkItemRevisions して、特定の作業項目のすべてのリビジョンを読み込む

作業項目または作業項目のコレクションの完全な履歴をフェッチするたびに使用 WorkItemRevisions します。

たとえば、次のクエリは、識別子を持つ作業項目のすべてのリビジョンを {id} 返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
  $filter=WorkItemId eq {id}
  &$select=WorkItemId, Title

特定の条件に一致するすべての作業項目の完全な履歴を気にする場合は、ナビゲーション プロパティの WorkItem フィルターを使用して表します。 たとえば、次のクエリは、現在アクティブなすべての作業項目のすべてのリビジョンを取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemRevisions?
  $filter=WorkItem/State eq 'Active'
  &$select=WorkItemId, Title

❌ [ブロック]個別の列にグループ化しない

グループ化操作を使用して、レコードの数を減らします。 句で個別の列を groupby 使用すると問題が示され、クエリはすぐに失敗します。 この状況が誤って発生した場合は、次のエラー メッセージが表示されます。

このクエリの groupby 句で指定された 1 つ以上の列は推奨されません。

この問題を解決するには、句から個別の列を groupby 削除します。

❌ [ブロック]集計を使用 countdistinct しない

OData でも、Analytics は関数を countdistinct サポートしていません。 今後サポートを追加する予定ですが、現時点では利用できません。 この関数を含むクエリは、次のエラー メッセージを返します。

集計とは異なるカウントを適用するクエリはサポートされていません。

❌ 算術オーバーフローが発生する可能性がある AVOID 集計

まれに、集計クエリで算術オーバーフローの問題が発生することがあります。 たとえば、作業項目エンティティなど StackRank 、合計を意図していない数値プロパティを合計すると発生する可能性があります。 OData Extension for Data Aggregation 標準では、プロパティを別の型にキャストする方法は提供されないため、この問題を解決する唯一の方法は、問題のあるプロパティを集計から削除することです。

✔️ 長いクエリにバッチ エンドポイントを使用する

長いクエリで問題が発生する可能性があります。 特に、次の場合に問題が発生する可能性があります。

  • 多くのユーザー設定フィールドを使用してプロジェクトにクエリを実行します。
  • クエリはプログラムによって構築されます。

送信 HTTP GET される OData クエリの現在の制限は 3,000 文字です。 この値を超えると、"404 Not Found" という応答が返されます。

HTTP/1.1 404 Not Found
Content-Length: 0

この問題を解決するには、仕様 OData バージョン 4.0 で説明されているように、 OData バッチ エンドポイントを使用します。パート 1: プロトコル - 11.7 バッチ要求。 Batch 機能は、主に複数の操作を 1 つの HTTP 要求ペイロードにグループ化するように設計されましたが、クエリの長さの制限の回避策として使用することもできます。 要求を HTTP POST 送信することで、任意の長さのクエリを渡し、サービスがそれを正しく解釈できます。

❌ [ブロック]複数のクエリを送信するためにバッチ エンドポイントを使用しない

バッチ エンドポイントの使用は、複数の要求のバッチ処理から制限されます。 1 つの要求に含めることができるクエリは 1 つだけです。 複数のクエリのバッチを送信しようとすると、操作は失敗し、次のエラー メッセージが表示されます。 唯一の解決策は、クエリを複数の要求に分割することです。

Analytics では、現在のバッチ メッセージに含まれる複数の操作の処理はサポートされていません。 分析では、POST 要求をサポートするために OData バッチを使用しますが、操作を 1 つの要求に制限する必要があります。

❌ [ブロック]800 列を超えるクエリを使用しないでください

800 列を超えるクエリを制限します。 クエリで返される列が十分に選択されていない場合は、次のエラー メッセージが表示される可能性があります。

VS403670: 指定したクエリは、許容される 800 列の制限を超える 'N' 列を返します。 明示的な$select ($expand内を含む) オプションを使用して、列の数を制限してください。

$select句をクエリに追加し、クエリの操作を$expandして、この制限を超えないようにします。

❌ 長いクエリの作成を回避する

長いクエリを作成するたびに、アプローチを評価することをお勧めします。 長いクエリ (複雑なフィルターやプロパティの長いリストなど) が必要なシナリオは多数ありますが、通常は最適でない設計の早期インジケーターを提供します。

クエリに多数のエンティティ キー (たとえば) が含まれている場合は、 WorkItemId eq {id 1} or WorkItemId eq {id 2} or ...おそらくそれを書き換えることができます。 識別子を渡す代わりに、同じエンティティ セットを選択する他の条件を定義してみてください。 プロセスを変更する必要がある場合 (たとえば、新しいフィールドやタグを追加する)、通常は価値があります。 より抽象的なフィルターを使用するクエリはメインしやすく、より適切に機能する可能性が高くなります。

長いクエリを生成する傾向がある別のシナリオは、多数の個別の日付 (たとえば) DateSK eq {dateSK 1} or DateSK eq {dateSK 2} or ...を含めると発生します。 より抽象的なフィルターを作成するために使用できる別のパターンを探します。 たとえば、次のクエリは、月曜日に作成されたすべての作業項目を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedOn/DayOfWeek eq 2
  &$select=WorkItemId, Title, State

✔️ 日付列でフィルター処理するときにタイム ゾーンを指定する

タイム ゾーン (Edm.DateTimeOffset) は、組織のタイム ゾーン設定に一致するオフセットを持つすべての日付と時刻の情報を公開します。 このデータは正確で簡単に同時に解釈できます。 もう 1 つの悪い結果は、すべてのフィルターがタイム ゾーン情報も渡す必要があるということです。 スキップすると、次のエラー メッセージが表示されます。

URI で指定されたクエリが無効です。 datetime オフセットが指定されませんでした。 オフセットを指定するには、次のいずれかの形式の YYYY-MM-ddZ を使用して、午前 0 時以降のすべてを指定するか、yyyy-MM-ddThh:mm-hh:mm (ISO 8601 標準の日付と時刻の表記) を指定してください。

この問題を解決するには、タイム ゾーン情報を追加します。 たとえば、組織が "(UTC-08:00) 太平洋標準時 (米国およびカナダ)" タイム ゾーンでデータを表示するように構成されていると仮定すると、次のクエリでは、2020 年の初めから作成されたすべての作業項目が取得されます。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedDate ge 2020-01-01T00:00:00-08:00
  &$select=WorkItemId, Title, State

正のオフセットを持つタイム ゾーンでも同じソリューションが機能しますが、プラス文字 (+) は URI で特別な意味を持ち、正しく処理する必要があります。 開始点として (文字を+使用して) 指定2020-01-01T00:00:00+08:00すると、次のエラーが発生します。

URI で指定されたクエリが無効です。 'CreatedDate ge 2020-01-01T0000 08:00' の位置 31 の構文エラー。

これを解決するには、文字を + エンコードされたバージョン %2Bに置き換えます。 たとえば、組織が "(UTC+08:00) 北京、重慶、香港、ウルムチ" タイム ゾーンでデータを表示するように構成されていると仮定すると、次のクエリは、2020 年の初めから作成されたすべての作業項目を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedDate ge 2020-01-01T00:00:00%2B08:00
  &$select=WorkItemId, Title, State

別の方法として、タイム ゾーン情報を保持しないため、日付サロゲート キー プロパティを使用します。 たとえば、次のクエリは、組織の設定に関係なく、2020 年の初めから作成されたすべての作業項目を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedDateSK ge 20200101
  &$select=WorkItemId, Title, State

パフォーマンスに関するガイドライン

推奨

次の方法を使用しないでください。

次の例を考えてみましょう

回避

✔️ パフォーマンス ガイドラインの実装の効果を測定する

パフォーマンスに関する推奨事項と同様に、それらを盲目的に実装しないでください。 代わりに、常にベースラインをキャプチャし、 行った変更の効果を測定 します。 すべてのガイドラインは、特定の要件と課題を抱えている Analytics のクライアントとの対話に基づいて作成されました。 これらの推奨事項は一般的と見なされ、同様のクエリを設計するユーザーにとって役立つ可能性があります。 ただし、まれに、ガイドラインに従ってもパフォーマンスに影響を与えたり、悪影響を及ぼしたりする可能性があります。 それに気づくには、違いを測定する必要があります。 このような場合は、開発者コミュニティ ポータルでフィードバックを提供してください。

パフォーマンスを測定するオプションは多数あります。 最も簡単な方法は、2 つのバージョンの同じクエリをブラウザーで直接実行することです。 開発者ツールにかかった時間を確認します。 たとえば、Microsoft Edge F12 開発者ツール) の [ネットワーク] パネルを使用できます。 もう 1 つのオプションは、Fiddler Web デバッガー ツールを使用してこの情報を キャプチャすることです

方法が何であれ、両方のクエリを複数回実行します。 たとえば、クエリをそれぞれ 30 回実行して、十分に大きなサンプル セットを作成します。 次に、パフォーマンスの特性を把握します。 Analytics はマルチテナント アーキテクチャに従います。 そのため、同時に発生する他の操作は、クエリの期間に影響する可能性があります。

✔️ 集計拡張機能を使用する

クエリのパフォーマンスを向上させるために実行できる最善の方法は、集計拡張機能である OData Extension for Data Aggregation を使用することです。 集計拡張機能を使用して、サービスにデータ サーバー側を集計し、同じ関数クライアント側を適用してフェッチできる応答よりも小さい応答を返すように要求します。 最後に、Analytics はこの種類のクエリ用に最適化されているため、それを利用します。

詳細については、「データの集計」を参照してください

✔️ DO 句で列を $select 指定する

句で気にする列を $select 指定します。 Analytics は、列ストア インデックス テクノロジに基づいて構築されています。 つまり、データはストレージとクエリの両方の処理が列ベースです。 プロパティのセットを減らすことで、句で $select 参照することで、スキャンする必要がある列の数を減らし、クエリの全体的なパフォーマンスを向上させることができます。

たとえば、次のクエリでは、作業項目の列を指定します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $select=WorkItemId, Title, State

Note

Azure DevOps では、プロセスのカスタマイズがサポートされています。 一部の管理者はこの機能を使用し、何百ものユーザー設定フィールドを作成します。 句を $select 省略すると、クエリはユーザー設定フィールドを含むすべてのフィールドを返します。

✔️ 句内の展開オプションで列を$select$expand指定する

句の $select ガイドラインと同様に、句内の expand オプションで $select プロパティを $expand 指定します。 忘れるのは簡単ですが、省略した場合、応答には展開されたオブジェクトのすべてのプロパティが含まれます。

たとえば、次のクエリでは、作業項目とその親の両方の列を指定します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $select=WorkItemId, Title, State
  &$expand=Parent($select=WorkItemId, Title, State)

✔️ 作業項目の履歴データ (WorkItemRevisionsまたはWorkItemSnapshotエンティティ セット) に対してクエリを実行する場合にRevisedDateSKフィルターを定義する

履歴データのクエリを実行すると、最新の期間 (30 日、90 日など) に関心がある可能性があります。 作業項目エンティティの実装方法のため、このようなクエリを記述して優れたパフォーマンスを得るには便利な方法があります。 作業項目を更新するたびに、新しいリビジョンが作成され、このアクションがフィールドに System.RevisedDate 記録されるため、履歴フィルターに最適です。

Analytics では、変更された日付が () プロパティと RevisedDateSK (Edm.DateTimeOffsetEdm.Int32) プロパティにRevisedDate表示されます。 パフォーマンスを最大限に高めるには、後者を使用します。 これは日付 サロゲート キー であり、リビジョンが作成された日付、または null アクティブで不完全なリビジョンの日付を表します。 以降のすべての日付が必要な場合は {startDate} 、クエリに次のフィルターを追加します。

RevisedDateSK eq null or RevisedDateSK gt {startDateSK}

たとえば、次のクエリは、2020 年の初め以降の各日の作業項目の数を返します。 列の明確なフィルターとは別に DateSK 、2 番目のフィルターがオンになっていることに注意してください RevisedDateSK。 冗長に見えるかもしれませんが、クエリ エンジンがスコープ内にないリビジョンを除外し、クエリのパフォーマンスを大幅に向上させることができます。

https://analytics.dev.azure.com/{OrganizationName}/_odata/v1.0/WorkItemSnapshot?
  $apply=
    filter(DateSK gt 20200101)/
    filter(RevisedDateSK eq null or RevisedDateSK gt 20200101)/
    groupby(
      (DateValue), 
      aggregate($count as Count)
    )

Note

バーンダウン ウィジェットに取り組んでいたときに、この推奨事項を思い付きました。 最初はフィルターのみを DateSK 定義しましたが、大規模なデータセットを持つ組織に対してこのクエリを適切にスケーリングすることはできませんでした。 クエリ プロファイル中に、 DateSK リビジョンが適切にフィルター処理されないことがわかります。 フィルター RevisedDateSK を追加して初めて、大規模なパフォーマンスを得ることができました。
~ 製品チーム

✔️ 長期間にわたる傾向クエリには、週単位または月単位のスナップショットを使用してください

既定では、すべてのスナップショット テーブルは、毎日のスナップショットファクト テーブルとしてモデル化されます。 時間範囲に対してクエリを実行すると、各日の値が取得されます。 時間範囲が長い場合、多数のレコードが生成されます。 このような高精度が必要ない場合は、毎週または毎月のスナップショットを使用できます。

他のフィルター式でこれを行うと、特定の週または月が終了しない日を削除できます。 このシナリオを念頭に IsLastDayOfPeriod 置いて、Analytics に追加されたプロパティを使用します。 このプロパティは種類 Microsoft.VisualStudio.Services.Analytics.Model.Period が異なり、1 日が異なる期間 (週、月など) で終了するかどうかを判断できます。

<EnumType Name="Period" IsFlags="true">
  <Member Name="None" Value="0"/>
  <Member Name="Day" Value="1"/>
  <Member Name="WeekEndingOnSunday" Value="2"/>
  <Member Name="WeekEndingOnMonday" Value="4"/>
  <Member Name="WeekEndingOnTuesday" Value="8"/>
  <Member Name="WeekEndingOnWednesday" Value="16"/>
  <Member Name="WeekEndingOnThursday" Value="32"/>
  <Member Name="WeekEndingOnFriday" Value="64"/>
  <Member Name="WeekEndingOnSaturday" Value="128"/>
  <Member Name="Month" Value="256"/>
  <Member Name="Quarter" Value="512"/>
  <Member Name="Year" Value="1024"/>
  <Member Name="All" Value="2047"/>
</EnumType>

フラグを持つ列挙型として定義されているため Microsoft.VisualStudio.Services.Analytics.Model.Period 、OData has 演算子を使用し、ピリオド リテラルの完全な型を指定します。

IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month'

たとえば、次のクエリは、毎月の最終日に定義された作業項目の数を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItemSnapshot?
  $apply=
    filter(IsLastDayOfPeriod has Microsoft.VisualStudio.Services.Analytics.Model.Period'Month')/
    groupby(
      (DateValue), 
      aggregate($count as Count)
    )

✔️ タグでフィルター処理するときに作業項目にコレクション プロパティを使用 Tags する

このプロパティを TagNames 関数と共に contains 使用して、作業が特定のタグでマークされているかどうかを判断できます。 ただし、この方法では、特に複数のタグを同時にチェックする場合に、クエリが遅くなる可能性があります。 最適なパフォーマンスと結果を得るには、代わりにナビゲーション プロパティを Tags 使用します。

たとえば、次のクエリでは、タグ付けされたすべての {tag}作業項目を取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=Tags/any(t:t/TagName eq '{tag}')
  &$select=WorkItemId, Title, State

この方法は、複数のタグでフィルター処理する必要がある場合にも適しています。 たとえば、次のクエリは、タグ付けされたすべての {tag1}作業項目を返します。{tag2}

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=Tags/any(t:t/TagName eq {tag1} or t/TagName eq {tag2})
  &$select=WorkItemId, Title, State

これらのフィルターを "and" 演算子と組み合わせることもできます。 たとえば、次のクエリでは、両方 {tag1}と〘でタグ付けされたすべての作業項目が取得されます。{tag2}

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=Tags/any(t:t/TagName eq {tag1}) and Tags/any(t:t/TagName eq {tag2})
  &$select=WorkItemId, Title, State

✔️ 作業項目のすべてのタグをテキストとして表示する場合は、プロパティを使用 TagNames してください

前のセクションで説明したナビゲーション プロパティ Tagsは、フィルター処理に適しています。 ただし、クエリが入れ子になったコレクション内のタグを返すので、それらを操作するといくつかの課題が発生します。 データ モデルには、タグの使用シナリオを TagNames 簡略化するために追加したプリミティブ プロパティ (Edm.String) も含まれています。 セミコロン "; " 区切り記号と組み合わせたすべてのタグの一覧を含む 1 つのテキスト値です。 このプロパティは、タグをまとめて表示する場合に使用します。 これは、前に説明したタグ フィルターと組み合わせることができます。

たとえば、次のクエリでは、タグ付けされたすべての {tag}作業項目を取得します。 作業項目 ID、タイトル、状態、および結合されたタグのテキスト表現が返されます。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=Tags/any(t:t/TagName eq '{tag}')
  &$select=WorkItemId, Title, State, TagNames

重要

プロパティ TagNames の長さの制限は 1024 文字です。 その制限内に収まるタグのセットが含まれています。 作業項目に多数のタグがある場合、またはタグが非常に長い場合 TagNames は、完全なセットが含まれていないため、 Tag 代わりにナビゲーション プロパティを使用する必要があります。

❌大文字と小文字を区別しない比較を行うために使用およびtoupper関数を使用tolowerしない

他のシステムで作業したことがある場合は、大文字と小文字を tolower 区別しない比較に関数を toupper 使用することが想定される場合があります。 Analytics では、すべての文字列比較で既定では大文字と小文字が区別されないため、明示的に処理するために関数を適用する必要はありません。

たとえば、次のクエリは、"QUALITY"、"quality"、またはこの単語のその他のケースの組み合わせでタグ付けされたすべての作業項目を取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=Tags/any(t:t/TagName eq 'quality')
  &$select=WorkItemId, Title, State, TagNames

❌ 無制限の拡張を使用しない $levels=max

OData には、階層構造のすべてのレベルを拡張する機能があります。 たとえば、作業項目の追跡には、無制限の拡張を適用できるエンティティがいくつかあります。 この操作は、少量のデータを持つ組織でのみ機能します。 大規模なデータセットに対しては適切にスケーリングされません。 次の場合は、まったく使用しないでください。

  • 大規模なデータセットを操作しています。
  • ウィジェットを開発していて、ウィジェットがインストールされる場所を制御できない。

✔️ サーバー駆動型ページングを使用する

1 つの応答で送信するには大きすぎるセットを要求すると、Analytics によってページングが適用されます。 応答には、部分セットと、次の項目の部分的なセットを取得できるリンクのみが含まれます。 この戦略については、OData 仕様の OData バージョン 4.0 で説明されています。パート 1: プロトコル - サーバー駆動ページング。 サービスがページングを制御できるようにすることで、各エンティティが可能な限り skiptoken 効率的に設計されているため、最高のパフォーマンスが得られます。

プロパティには、次のページへのリンクが @odata.nextLink 含まれています。

{
  "@odata.context": "https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/$metadata#WorkItems(*)",
  "value": [
    ...
  ],
  "@odata.nextLink":"https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?$skiptoken=12345"}

Note

ほとんどの既存の OData クライアントは、サーバー駆動型ページングを自動的に処理できます。 たとえば、この戦略は、Power BI、SQL Server Integration Services、Azure Data Factory の各ツールで既に使用されています。

❌クライアント駆動型ページングを実装するためのオプションと$skipクエリ オプションを使用$topしない

他の REST API では、クライアント駆動型ページングと$skipクエリ オプションを$top実装している可能性があります。 Analytics では使用しないでください。 このアプローチにはいくつかの問題があり、パフォーマンスはその 1 つです。 代わりに、前のセクションで説明したサーバー駆動型ページング戦略を採用してください。

✔️ DO クエリ オプションを使用 $top してレコードの数を制限する

クエリ オプション $top は、一緒 $skipに使用する場合にのみ推奨されます。 レポートのシナリオで、レコードのサブセット (サンプルなど) のみが必要な場合は、クエリ オプションを使用 $top しても問題ありません。 さらに、いくつかの条件に従ってレコードをランク付けする必要がある場合は、常に組み合わせて$orderby使用$topして、上位のランク付けされたレコードで安定した結果を得る必要があります。

✔️ 少数のレコードを返すクエリを記述することを検討してください

少数のレコードを返すクエリを記述することは、最も直感的なガイドラインです。 常に、本当に関心があるデータのみをフェッチすることを目指してください。 これを実現するには、OData クエリ言語で強力なフィルター処理機能を利用できるようにします。

✔️ 選択したプロパティの数を最小限に制限することを検討してください

一部のプロジェクト管理者は、カスタム フィールドを追加してプロセスを大幅にカスタマイズします。 カスタマイズが多いと、ワイド エンティティで使用可能なすべての列 (たとえば) をフェッチするときにパフォーマンス WorkItemsの問題が発生する可能性があります。 Analytics は、列ストア インデックス テクノロジに基づいて構築されています。 つまり、データはストレージとクエリの両方の処理が列ベースです。 そのため、クエリが参照するプロパティが多いほど、処理コストが高くなります。 常に、クエリ内のプロパティのセットを、レポートシナリオで本当に気にしているものに制限することを目指してください。

✔️ 日付サロゲート キーのプロパティ (DateSK サフィックス) でのフィルター処理を検討する

日付フィルターを定義する方法は多数あります。 日付プロパティ (たとえば、)、対応するナビゲーション (たとえば)、CreatedDateCreatedOnDateまたは代理キー表現 (たとえば) CreatedDateでフィルター処理できます。 最後のオプションは、最適なパフォーマンスを実現し、レポート要件で可能な場合に推奨されます。

たとえば、次のクエリは、2020 年の初めから作成されたすべての作業項目を取得します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedDateSK ge 20200101

✔️ 代理キー列でのフィルター処理を検討する

関連オブジェクトの値 (プロジェクト名の作業項目のフィルター処理など) でデータをフィルター処理する場合は、常に 2 つの選択肢があります。 ナビゲーション プロパティ (たとえば) を使用するか、Project/ProjectNameサロゲート キーを事前にキャプチャして、クエリで直接使用できます (例: ProjectSK)。

ウィジェットを構築する場合は、後者のオプションを使用することをお勧めします。 キーがクエリの一部として渡されると、タッチする必要があるエンティティ セットの数が減り、パフォーマンスが向上します。

たとえば、次のクエリは、ナビゲーション プロパティではなくProject/ProjectNameプロパティを使用してProjectSKフィルター処理WorkItemsします。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=ProjectSK eq {projectSK}

❌or 句で 、ChildrenまたはRevisionsプロパティを$filter$expand使用Parentしないようにする

作業項目は、データ モデル全体で最もコストの高いエンティティです。 関連する作業項目にアクセスするために使用できるナビゲーション プロパティがいくつかあります。 ParentChildrenRevisions ただし、クエリ内でそれらを使用するたびに、パフォーマンスの低下が予想されます。 これらのプロパティのいずれかが本当に必要な場合は常に質問し、設計を更新する可能性があります。

たとえば、展開する代わりに、より多くの作業項目を Parentフェッチし、プロパティを使用 ParentWorkItemId して完全階層クライアント側を再構築できます。 このような最適化は、ケース バイ ケースで実行します。

✔️ ヘッダーに優先設定を渡すことを VSTS.Analytics.MaxSize 検討してください

クエリを実行すると、クエリから返されるレコードの数がわかりません。 集計を使用して別のクエリを送信するか、次のすべてのリンクに従ってデータセット全体をフェッチします。 Analytics では優先設定が考慮 VSTS.Analytics.MaxSize されるため、データセットがクライアントが受け入れ可能なものよりも大きいインスタンスで高速に失敗できます。

このオプションは、データエクスポートのシナリオで役立ちます。 これを使用するには、HTTP 要求にヘッダーを追加 Prefer し、負以外の値に設定 VSTS.Analytics.MaxSize する必要があります。 この値は VSTS.Analytics.MaxSize 、許容できるレコードの最大数を表します。 0 に設定すると、既定値の 200 K が使用されます。

たとえば、次のクエリは、データセットが 1000 レコード以下の場合に作業項目を返します。

GET https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems HTTP/1.1
User-Agent: {application}
Prefer: VSTS.Analytics.MaxSize=1000
OData-MaxVersion: 4.0
Accept: application/json;odata.metadata=minimal
Host: analytics.dev.azure.com/{OrganizationName}

データセットが 1000 レコードの制限を超えた場合、クエリはすぐに失敗し、次のエラーが発生します。

クエリ結果には 1,296 行が含まれており、許可される最大サイズが 1000 を超えています。 追加のフィルターを適用して、レコードの数を減らしてください

最大ページ サイズの設定については、「ODataPreferenceHeader.MaxPageSize プロパティ」を参照してください

クエリ スタイルのガイドライン

✔️ 集計メソッドで仮想プロパティを使用 $count する

一部のエンティティはプロパティを公開 Count します。 データが別のストレージにエクスポートされる場合、一部のレポート シナリオが簡単になります。 ただし、OData クエリの集計では、これらの列を使用しないでください。 代わりに仮想プロパティを $count 使用してください。

たとえば、次のクエリは、作業項目の合計数を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $apply=aggregate($count as Count)

❌ URL セグメントで仮想プロパティを使用 $count しないようにする

OData 標準ではエンティティ セット (たとえば) に仮想プロパティを使用 $count できますが、 _odata/v1.0/WorkItems/$countすべてのクライアントが応答を正しく解釈できるわけではありません。 そのため、代わりに集計を使用することをお勧めします。

たとえば、次のクエリは、作業項目の合計数を返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $apply=aggregate($count as Count)

✔️ パラメーター エイリアスを使用してクエリの揮発性部分を分離することを検討する

パラメーターエイリアスは、メインクエリ テキストからパラメーター値などの揮発性の部分を抽出するエレガントなソリューションを提供します。 これらを評価する式で使用できます。

  • プリミティブ値
  • 複合値
  • プリミティブ値または複合値のコレクション。

詳細については、OData バージョン 4.0 を参照してください 。パート 2: URL 規則 - 5.1.1.13 パラメーター エイリアス。 パラメーターは、ユーザーが指定した値でインスタンス化できるテンプレートとしてクエリ テキストを使用する場合に便利です。

たとえば、次のクエリでは、パラメーターを使用 @createdDateSK して値をフィルター式から分離します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=CreatedDateSK ge @createdDateSK
  &$select=WorkItemId, Title, State
  &@createdDateSK=20200101

❌ 1 つのクエリでの混在 $apply$filter 句の回避

クエリに追加 filter する場合は、2 つのオプションがあります。 句または$apply=filter()組み合わせを使用$filterして行うことができます。 これらのオプションはそれぞれ単独でうまく機能しますが、それらを組み合わせると予期しない結果が生じる可能性があります。

予想される場合でも、OData は評価の順序を明確に定義します。 また、句の $apply 優先度 $filterは . このため、1 つまたは複数を選択する必要がありますが、1 つのクエリではこれら 2 つのフィルター オプションは使用しないでください。 クエリが自動的に生成される場合は重要です。

たとえば、次のクエリでは、最初に作業項目をフィルター処理し StoryPoint gt 5、結果を集計してパスを指定し、最後に結果をフィルター処理します StoryPoints gt 2。 この評価順序では、クエリは常に空のセットを返します。

https://analytics.dev.azure.com/{OrganizationName}/_odata/{version}/WorkItems?
  $filter=StoryPoints gt 2
  $apply=
    filter(StoryPoints gt 5)/
    groupby(
      (Area/AreaPath),
      aggregate(StoryPoints with sum as StoryPoints)
    )

✔️ OData 評価順序に一致するようにクエリを構造化することを検討してください

1 つのクエリで句とfilter組み合$applyわせて使用すると混乱が生じる可能性があるため、評価順序に合わせてクエリ句を構成することをお勧めします。

  1. $apply
  2. $filter
  3. $orderby
  4. $expand
  5. $select
  6. $skip
  7. $top

✔️ メタデータ注釈で説明されている OData 機能の確認を検討する

Analytics でサポートされている OData 機能がわからない場合は、メタデータで注釈を検索できます。 TC GitHub リポジトリOASIS Open Data Protocol (OData) 技術委員会には、使用可能な注釈の一覧メイン含まれています。

たとえば、サポートされているフィルター関数の一覧は、エンティティ コンテナーの注釈で Org.OData.Capabilities.V1.FilterFunctions 使用できます。

<Annotation Term="Org.OData.Capabilities.V1.FilterFunctions">
  <Collection>
  <String>contains</String>
  <String>endswith</String>
  [...]
  </Collection>
</Annotation>

もう 1 つの便利な注釈は Org.OData.Capabilities.V1.ExpandRestrictions、句で使用できないナビゲーション プロパティについて $expand 説明します。 たとえば、次の注釈は、エンティティ セット内でWorkItems展開できないことを説明Revisionsしています。

<EntitySet Name="WorkItems" EntityType="Microsoft.VisualStudio.Services.Analytics.Model.WorkItem">
  [...]
  <Annotation Term="Org.OData.Capabilities.V1.ExpandRestrictions">
    <Record>
      <PropertyValue Property="Expandable" Bool="true"/>
      <PropertyValue Property="NonExpandableProperties">
        <Collection>
          <NavigationPropertyPath>Revisions</NavigationPropertyPath>
        </Collection>
      </PropertyValue>
    </Record>
  </Annotation>
</EntitySet>