次の方法で共有


Cosmos DB のインデックス作成ポリシー (Azure と Fabric)

Cosmos DB (Azure と Fabric) は、スキーマに依存しないデータベースであり、スキーマまたはインデックス管理を処理することなく、アプリケーションを反復処理できます。 Cosmos DB 内のインデックス作成は、データの進化に関係なく、高速で柔軟なクエリ パフォーマンスを実現するように設計されています。 Cosmos DB では、すべてのコンテナーに、コンテナーの項目のインデックス作成方法を指定するインデックス作成ポリシーがあります。 新しく作成したコンテナーの既定のインデックス作成ポリシーでは、あらゆる項目のあらゆるプロパティのインデックスが作成され、任意の文字列または数値に範囲インデックスが適用されます。 この既定の構成では、インデックス作成とインデックス管理を事前に考慮しなくても、クエリのパフォーマンスを向上させることができます。

状況によっては、この自動動作を、自分の要件にさらに適合するようにオーバーライドできます。 "インデックス作成モード" を設定し、"プロパティ パス" を含めるか除外することで、コンテナーのインデックス作成ポリシーをカスタマイズできます。

インデックス作成モード

Cosmos DB では、次の 2 つのインデックス作成モードがサポートされています。

  • 一貫性: 項目を作成、更新、削除すると、それに同期してインデックスが更新されます。

  • None:コンテナーでインデックス作成が無効になっています。 このモードは、コンテナーがセカンダリ インデックスを必要としない純粋なキー値ストアとして使用される場合に一般的に使用されます。 一括操作のパフォーマンスを改善する目的で使用することもできます。 一括操作が完了したら、インデックス モードを Consistent に設定し、完了するまで SDK の IndexTransformationProgress 機能を使用して監視できます。

Cosmos DB では、遅延インデックス作成モードもサポートされています。 遅延インデックス作成では、エンジンが他の作業を行っていない場合に、優先順位の低いレベルでインデックスの更新が実行されます。 この動作により、クエリ結果に 一貫性がない 、または 不完全になる 可能性があります。 Cosmos DB コンテナーのクエリを実行する場合は、遅延インデックス作成を選択しないでください。 新しいコンテナーでは、遅延インデックス作成を選択できません。

インデックス サイズ

Cosmos DB では、消費されるストレージの合計は、データ サイズとインデックス サイズの両方の組み合わせです。 インデックス サイズのいくつかの機能を次に示します。

  • インデックス サイズは、インデックス作成ポリシーによって異なります。 すべてのプロパティのインデックスが作成されている場合、インデックス サイズはデータ サイズよりも大きくなることがあります。

  • データが削除されると、インデックスはほぼ連続して圧縮されます。 ただし、少量のデータを削除する場合は、インデックス サイズの減少をすぐに確認できないことがあります。

  • 物理パーティションが分割されると、インデックス サイズが一時的に増大する可能性があります。 インデックス領域は、パーティション分割が完了した後に解放されます。

  • コンテナーのインデックス作成モードが一貫性がある場合、 id_ts のシステム プロパティは常にインデックスが作成されます。

  • システム プロパティ id_ts は、コンテナー ポリシーのインデックス付きパスの説明には含まれません。 これらのシステム プロパティは常に既定でインデックスが作成され、この動作を無効にできないため、この除外は仕様に従っています。

パーティション キーは ( /idされていない限り) インデックスが作成されないため、インデックスに含める必要があります。

プロパティ パスを含めるか除外する

カスタム インデックス作成ポリシーには、明示的にインデックス作成に含めるかインデックス作成から除外するプロパティ パスを指定できます。 インデックスが作成されるパスの数を最適化することによって、書き込み操作の待ち時間と RU 料金を大幅に削減できます。

この JSON 項目をもう一度検討してください。

{
  "id": "00000000-0000-0000-0000-000000004368",
  "name": "Cofaz Jacket",
  "tags": [
    { "category": "clothing", "type": "jacket" },
    { "category": "outdoor", "type": "winter" }
  ],
  "inventory": { "warehouse": "Seattle", "quantity": 50 },
  "distributors": [
    { "name": "Contoso" },
    { "name": "AdventureWorks" }
  ]
}

このインデックス作成ポリシーでは、次のインデックス作成パスが作成されます。

経路 価値
/id "00000000-0000-0000-0000-000000004368"
/name "Cofaz Jacket"
/tags/0/category "clothing"
/tags/0/type "jacket"
/tags/1/category "outdoor"
/tags/1/type "winter"
/inventory/warehouse "Seattle"
/inventory/quantity 50
/distributors/0/name "Contoso"
/distributors/1/name "AdventureWorks"

これらのパスは、次の追加で定義されます。

  • スカラー値 (文字列または数値) へのパスは /? で終わる

  • 配列の要素は、(/[]/0などではなく) /1表記によって一緒にアドレス指定されます。

  • /* ワイルドカードを使用してノードの下の任意の要素を一致させることができる

サンプル 項目のベースライン インデックス作成ポリシーには、次の最適化が含まれる場合があります。

  • inventoryquantity パスは /inventory/quantity/?

  • tagscategory パスは /tags/[]/category/?

  • inventory の下にあるもののパスは /inventory/*

たとえば、/inventory/quantity/? パスを含めることができます。 このパスにより quantity プロパティのインデックスが確実に作成されますが、このプロパティ内で入れ子になっている追加の JSON のインデックスは作成されません。

包含/除外戦略

すべてのインデックス作成ポリシーには、含まれるパスまたは除外されるパスのいずれかとしてルート パス /* を指定する必要があります。

  • インデックスを作成する必要がないパスを選択的に除外するためにルート パスを指定する。 Cosmos DB では、モデルに追加される可能性のある新しいプロパティのインデックスを事前に作成できるため、この方法をお勧めします。

  • インデックスを作成する必要があるパスを選択的に含めるためにルート パスを除外する。 パーティション キーのプロパティ パスは、既定では除外された戦略でインデックスが作成されないため、必要に応じて明示的に含める必要があります。

  • 英数字や _ (アンダースコア) を含む通常文字から成るパスの場合は、パス文字列を二重引用符で囲んでエスケープする必要はありません ("/path/?" など)。 他の特殊文字を含むパスの場合は、パス文字列を二重引用符で囲んでエスケープする必要があります ("/"path-abc"/?" など)。 パスの中に特殊文字が予測される場合は、安全のために、すべてのパスをエスケープすることができます。 機能的には、すべてのパスをエスケープしても、特殊文字を含むパスだけをエスケープしても、違いはまったくありません。

  • システム プロパティ _etag は、etag がインデックス作成対象パスに追加されていない限り、既定でインデックス作成から除外されます。

  • インデックス作成モードが [consistent](同期) に設定されている場合、システム プロパティ id_ts には自動的にインデックスが作成されます。

  • 明示的にインデックスされたパスが項目に存在しない場合は、パスが未定義であることを示す値がインデックスに追加されます。

明示的に含まれるすべてのパスには、特定の項目についてパスが未定義の場合でも、コンテナー内の各項目についてインデックスに値が追加されます。

詳細については、 インデックス作成ポリシーのサンプルを参照してください。

優先順位を含める、または除外する

含まれるパスと除外されるパスに競合がある場合は、より正確なパスが優先されます。

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

  • 含まれるパス: /model/manufacturer/*

  • 除外されるパス: /model/*

この場合、含まれるパスはより正確であるため、除外されるパスよりも優先されます。 これらのパスに基づいて、/model パス内やそのパスに入れ子になったデータは、インデックスから除外されます。 例外は、含まれるパス /model/manufacturer/* 内のデータで、インデックスが作成されます。

Cosmos DB に含まれるパスと除外されたパスの優先順位に関するいくつかの規則を次に示します。

  • より深いパスは、浅いパスよりも正確です。 たとえば、/a/b/?/a/? よりも正確です。

  • /?/* よりも正確です。 たとえば、 /a/?/a/* よりも正確であるため、 /a/? が優先されます。

  • パス /* には、含まれるパスまたは除外されるパスを指定する必要があります。

フルテキスト インデックス

フルテキスト インデックスを使用すると、インデックスを使用してフルテキスト検索とスコアリングを効率的に行うことができます。 インデックスを作成するすべてのテキスト パスを含む、インデックス作成ポリシーの fullTextIndexes セクションを含めることで、インデックス作成ポリシーでフルテキスト パスを簡単に定義することができます。 例えば次が挙げられます。

{
  "indexingMode": "consistent",
  "automatic": true,
  "includedPaths": [
    {
      "path": "/*"
    }
  ],
  "excludedPaths": [
    {
      "path": "/\"_etag\"/?"
    },
  ],
  "fullTextIndexes": [
    {
      "path": "/name"
    }
  ]
}

Important

フルテキスト インデックス作成ポリシーは、コンテナーのフルテキスト ポリシーで定義されているパス上に存在する必要があります。 詳細については、 フルテキスト検索を参照してください。

ベクトル インデックス

ベクトル インデックス作成では、VECTORDISTANCE システム関数を使用して、ベクトル検索の実行効率を向上させます。 ベクター検索では、ベクター インデックスを適用するときの待機時間が短く、スループットが高く、要求ユニット (RU) の消費量が少なくなります。 次の種類のベクトル インデックス ポリシーを指定できます。

タイプ Description 最大ディメンション
flat 他のインデックス プロパティと同じインデックスにベクトルを格納します。 505
quantizedFlat インデックスに格納する前にベクトルを量子化 (圧縮) します。 この種類では、少量の精度を犠牲にして待機時間とスループットを向上させることができます。 4096
diskANN 高速かつ効率的な概算検索のために、DiskANN に基づいてインデックスを作成します。 4096

Important

ベクター ポリシーとベクター インデックスは、作成後に変更できません。 変更するには、新しいコレクションを作成します。

注意すべき点をいくつか次に示します。

  • flatおよびquantizedFlatインデックスの種類では、Cosmos DB のインデックスを使用して、ベクター検索中に各ベクターを格納および読み取ります。 flat インデックスを持つベクター検索はブルート フォース検索であり、100% の精度または再現率を提供します。 この精度は、検索が常にデータセット内の最も類似したベクトルを見つけることを意味します。 ただし、 flat インデックスでは、最大 505 次元のベクトルがサポートされます。

    • quantizedFlat インデックスには、量子化された(圧縮された)ベクトルが格納されます。 quantizedFlat インデックスを使用したベクトル検索もブルート フォース検索ですが、インデックスに追加する前にベクトルが量子化されるため、精度は 100% をわずかに下回る可能性があります。 ただし、quantized flat を使用したベクトル検索は flat インデックスでのベクトル検索よりも待機時間が短く、スループットが高く、RU コストが低くなります。 この型は、クエリ フィルターを使用してベクター検索を比較的小さなベクター セットに絞り込むシナリオに適しています。 このシナリオでは、高い精度が必要です。

    • diskANN インデックスは、Microsoft Research によって開発されたハイ パフォーマンス ベクトル インデックス作成アルゴリズム スイートである DiskANN を適用する、ベクトル専用に定義された個別のインデックスです。 DiskANN インデックスでは、高い精度を維持しながら、最短の待機時間、最高のスループット、および最小の RU コスト クエリが提供されます。 ただし、DiskANN は近似ニアレスト ネイバー (ANN) インデックスであるため、精度は quantizedFlatflat よりも低くなる可能性があります。

    • diskANN インデックスと quantizedFlat インデックスは、すべての近似最近傍ベクトル インデックスに適用される精度と待機時間のトレードオフを調整するために使用できる、オプションのインデックス構築パラメーターを取ることができます。

  • quantizationByteSize: 直積量子化のサイズ (バイト単位) を設定します。 (Min=1Default=dynamic (システムが決定)、 Max=512)。 このサイズを大きく設定すると、RU コストが高くなり、待機時間が長くなる代わりに、ベクター検索の精度が高くなる可能性があります。 このトレードオフは、 quantizedFlatDiskANN の両方のインデックスの種類に適用されます。

    • indexingSearchListSize: インデックスの構築中に検索するベクトルの数を設定します。 最小値 = 10、規定値 = 100、最大値 = 500。 このサイズを大きく設定すると、インデックスのビルド時間が長くなり、ベクターの取り込みの待機時間が長くなる代わりに、ベクター検索の精度が高くなる可能性があります。 この特性は、 DiskANN インデックスにのみ適用されます。

ベクトル インデックスを使用したインデックス作成ポリシーの例を次に示します。

{
  "indexingMode": "consistent",
  "automatic": true,
  "includedPaths": [
    {
      "path": "/*"
    }
  ],
  "excludedPaths": [
    {
      "path": "/_etag/?",
    },
    {
      "path": "/vector/*"
    }
  ],
  "vectorIndexes": [
    {
      "path": "/vector",
      "type": "diskANN"
    }
  ]
}

Important

挿入のパフォーマンスを最適化するには、インデックス作成ポリシーの excludedPaths セクションにベクター パスを追加する必要があります。 ベクター パスを excludedPaths に追加しないと、ベクター挿入の RU 料金と待機時間が長くなります。

ベクター インデックス作成ポリシーは、コンテナーのベクター ポリシーで定義されているパス上にも存在する必要があります。 詳細については、 ベクター ポリシーを参照してください。

空間インデックス

インデックス作成ポリシーで空間パスを定義する場合は、そのパスに適用するインデックスの type を定義する必要があります。

空間インデックスには、次のような種類があります。

  • Point

  • Polygon

  • MultiPolygon

  • LineString

既定では、Cosmos DB では空間インデックスは作成されません。 空間 SQL 組み込み関数を使用するには、必要なプロパティに空間インデックスを作成します。 詳細については、「 空間インデックス」を参照してください。

タプル インデックス

タプル インデックスは、配列要素内の複数のフィールドに対してフィルター処理を実行する場合に便利です。 タプル インデックスは、タプル指定子 []を使用して、インデックス作成ポリシーの includedPaths セクションで定義されます。

含まれるパスまたは除外されるパスとは異なり、/* ワイルドカードを使用してパスを作成することはできません。 すべてのタプル パスは、 /?で終わる必要があります。 タプル パス内のタプルが項目に存在しない場合は、タプルが未定義であることを示す値がインデックスに追加されます。

配列タプル パスは、次の表記を使用して、 includedPaths セクションで定義されます。 <path prefix>/[]/{<tuple 1>, <tuple 2>, …, <tuple n>}/?

配列タプルの次の重要な規則を考慮してください。

  • タプルの各部分はコンマで区切られます。

  • 最初の部分であるパス プレフィックスは、タプル間で共通するパスです。 ルートから配列へのパスです。 この例では、 /tagsです。

  • 最初の部分の後に、タプルには配列ワイルドカード指定子 []を含める必要があります。 すべての配列タプル パスには、タプル指定子 {}の前に配列ワイルドカード指定子が必要です。

  • 次の部分では、タプル指定子 {}を使用してタプルを指定します。

  • タプルは、以下のいくつかの例外を除き、他のインデックス パスと同じパス仕様を使用する必要があります。

    • タプルは先頭の /で始めることはできません。

    • タプルには配列ワイルドカードを含めることはできません。

    • タプルは、 ? または *で終わるべきではありません。

    • ? はタプル パスの最後のセグメントであり、タプル指定子セグメントの直後に指定する必要があります。

たとえば、この仕様は有効なタプル パスです。 /tags/[]/{category, type}/?

配列タプル パスの有効な例をいくつか次に示します。

[
  { "path": "/tags/[]/{category, type}/?" },
  { "path": "/distributors/[]/{name}/?" },
  { "path": "/tags/[]/{category}/?" },
  { "path": "/tags/[]/{{category, type}}/?" },
  { "path": "/tags/[]/{[0], [1]}/?" },
  { "path": "/inventory/items/[]/tags/[]/{category, type}/?" }
]

説明を含む 無効な 配列タプル パスの例をいくつか次に示します。

無効なパス Explanation
/tags/[]/{category/[]/type}/? タプルの 1 つに配列ワイルドカードが使用されています
/tags/[]/{category, type}/* 配列タプルパスの最後のセグメントは?であり、*ではありません。
/tags/[]/{{category, type}}/? タプル指定子が入れ子になっています
/tags/{category, type}/? 配列ワイルドカードがタプル指定子の前にありません
/tags/[]/{/category,/type}/? タプルは/から始まる必要があります
/tags/[]/{category/?,type/?}/? タプルは、次の値で終わる必要があります。 ?
/inventory/[]/tags/[]/{category, type}/? 2 つの配列ワイルドカードとしてのパス プレフィックス

複合インデックス

2 つ以上のプロパティを使用する ORDER BY 句が含まれるクエリには、複合インデックスが必要です。 また、複合インデックスを定義して、多くの等値クエリと範囲クエリのパフォーマンスを向上させることもできます。 既定では、複合インデックスは定義されないため、必要に応じて複合インデックスを追加する必要があります。

複合インデックス パスで /* ワイルドカードを使用することはできません。 各複合パスは自動的に /?で終わるので、追加する必要はありません。 複合パスは、複合インデックスに含まれる唯一の値であるスカラー値を指す必要があります。 複合インデックス パスが項目に存在しない場合、または非スカラー値を指している場合、Cosmos DB はインデックスに値を追加して、パスが未定義であることを示します。

複合インデックスを定義するときは、次の 2 つのコンポーネントを指定します。

  • 複合インデックスの使用方法に影響する特定の順序で定義された 2 つ以上のプロパティ パス。

  • 昇順または降順として定義される順序。

複合インデックスを追加すると、新しい複合インデックスの追加が完了するまで、クエリは既存の範囲インデックスを使用します。 そのため、複合インデックスを追加しても、すぐにパフォーマンス向上が見られないことがあります。

ヒント

いずれかのソフトウェア開発キット (SDK) を使用して、インデックス変換の進行状況を追跡できます。

複数のプロパティに対する ORDER BY クエリ:

2 つ以上のプロパティを使用する ORDER BY 句が含まれるクエリに複合インデックスを使用する場合は、次の考慮事項を確認します。

  • 複合インデックスのパスが ORDER BY 句の中のプロパティのシーケンスに一致しない場合、その複合インデックスはクエリをサポートできません。

  • 複合インデックスのパスの順序 (昇順または降順) は、order 句の中の ORDER BY とも一致する必要があります。

  • 複合インデックスはまた、すべてのパスで反対の順序を持つ ORDER BY 句もサポートします。

プロパティ名、数量、および_tsで複合インデックスが定義されている次の例を考えてみます。

複合インデックス サンプル ORDER BY クエリ 複合インデックスでサポートされていますか?
(name ASC, quantity ASC) SELECT * FROM c ORDER BY c.name ASC, c.inventory.quantity asc Yes
(name ASC, quantity ASC) SELECT * FROM c ORDER BY c.inventory.quantity ASC, c.name asc No
(name ASC, quantity ASC) SELECT * FROM c ORDER BY c.name DESC, c.inventory.quantity DESC Yes
(name ASC, quantity ASC) SELECT * FROM c ORDER BY c.name ASC, c.inventory.quantity DESC No
(name ASC, quantity ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.inventory.quantity ASC, timestamp ASC Yes
(name ASC, quantity ASC, timestamp ASC) SELECT * FROM c ORDER BY c.name ASC, c.inventory.quantity ASC No

すべての必要な ORDER BY クエリに対応できるように、インデックス作成ポリシーをカスタマイズする必要があります。

複数のプロパティに対するフィルターを含むクエリ

クエリに 2 つ以上のプロパティに対するフィルターが含まれている場合は、これらのプロパティの複合インデックスを作成すると便利な場合があります。

たとえば、等値および範囲の両方のフィルターが含まれる次のクエリについて考えてみます。

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND c.inventory.quantity > 18

このクエリは、 (name ASC, quantity ASC)に複合インデックスを適用できる場合、より効率的で、時間がかかり、要求ユニット (RU) が少なくなります。

範囲フィルターが複数含まれるクエリを複合インデックスを使用して最適化することもできます。 ただし、個々の複合インデックスで最適化できる範囲フィルターはそれぞれ 1 つだけです。 範囲フィルターは、><<=>=!= です。 範囲フィルターは、複合インデックス内で最後に定義する必要があります。

等値フィルターが 1 つと範囲フィルターが 2 つ含まれる次のクエリについて考えてみます。

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND
  c.inventory.quantity > 18 AND
  c._ts > 1612212188

このクエリは、(name ASC, quantity ASC)(name ASC, _ts ASC) の複合インデックスを使用すると、より効率的に実行できます。 ただし、等値フィルターが含まれるプロパティを複合インデックス内で最初に定義する必要があるため、クエリでは (quantity ASC, name ASC) の複合インデックスが使用されません。 各複合インデックスで最適化できる範囲フィルターは 1 つだけであるため、(name ASC, quantity ASC, _ts ASC) の複合インデックスを 1 つだけではなく、個別の複合インデックスを 2 つ必要とします。

複数のプロパティに対するフィルターを使用してクエリの複合インデックスを作成する場合は、次の考慮事項が使用されます。

  • フィルター式では、複数の複合インデックスを使用できます。

  • クエリのフィルターのプロパティは、複合インデックスのプロパティと一致する必要があります。 プロパティが複合インデックス内にあり、フィルターとしてクエリに含まれていない場合、クエリでは複合インデックスは使用されません。

  • 複合インデックスに含まれていないプロパティをクエリでフィルター処理する場合、複合インデックスと範囲インデックスの組み合わせを使用してクエリを評価します。 この方法では、範囲インデックスのみに依存するよりも消費する RU が少なくなります。

  • プロパティに範囲フィルター (><<=>=、または !=) が含まれている場合、このプロパティは複合インデックス内で最後に定義する必要があります。 クエリに複数の範囲フィルターが含まれている場合、複数の複合インデックスを利用すると効果的になる可能性があります。

  • 複合インデックスを作成して複数のフィルターを使用してクエリを最適化する場合、複合インデックスの ORDER は結果に影響しません。 このプロパティは省略可能です。

プロパティ名、数量、およびタイムスタンプに複合インデックスが定義されている次の例を考えてみましょう。

複合インデックス サンプル クエリ 複合インデックスでサポートされていますか?
(name ASC, quantity ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity = 18 Yes
(name ASC, quantity ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity > 18 Yes
(name ASC, quantity ASC) SELECT COUNT(1) FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity > 18 Yes
(name DESC, quantity ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity > 18 Yes
(name ASC, quantity ASC) SELECT * FROM c WHERE c.name != "Cofaz Jacket" AND c.inventory.quantity > 18 No
(name ASC, quantity ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity = 18 AND c.timestamp > 123049923 Yes
(name ASC, quantity ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity < 18 AND c.timestamp = 123049923 No
(name ASC, quantity ASC) and (name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity < 18 AND c.timestamp > 123049923 Yes

フィルターと ORDER BY を使用したクエリ

クエリで 1 つ以上のプロパティがフィルター処理され、ORDER BY 句に異なるプロパティが含まれている場合は、フィルター内のプロパティを ORDER BY 句に追加すると便利な場合があります。

たとえば、フィルター内のプロパティを ORDER BY 句に追加すると、複合インデックスを適用するために次のクエリを書き直すことができます。

範囲インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket"
ORDER BY
  c.timestamp

複合インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket"
ORDER BY
  c.name,
  c.timestamp

フィルターを使用した任意の ORDER BY クエリに対しても同じクエリ最適化を一般化できます。このとき、個々の複合インデックスでサポートできる範囲フィルターは最大で 1 つだけであることに注意してください。

範囲インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND
  c.inventory.quantity = 18 AND
  c.timestamp > 1611947901
ORDER BY
  c.timestamp

複合インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND
  c.inventory.quantity = 18 AND
  c.timestamp > 1611947901
ORDER BY
  c.name,
  c.inventory.quantity,
  c.timestamp

さらに、複合インデックスを使用して、システム関数と ORDER BYを使用してクエリを最適化できます。

範囲インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND
  CONTAINS(c.distributors[0].name, "Contoso", true)
ORDER BY
  c.distributors[0].name

複合インデックスを使用するクエリ:

SELECT
  *
FROM
  container c
WHERE
  c.name = "Cofaz Jacket" AND
  CONTAINS(c.distributors[0].name, "Contoso", true)
ORDER BY
  c.name, c.distributors[0].name

次の考慮事項は、フィルターと ORDER BY 句が含まれるクエリを最適化するために複合インデックスを作成する場合に適用されます。

  • 1 つのプロパティに対するフィルターと、別のプロパティを使用する別の ORDER BY 句を使用してクエリに複合インデックスを定義しない場合でも、クエリは成功します。 しかし、複合インデックスを使用すると、特に ORDER BY 句内のプロパティのカーディナリティが高い場合、クエリの RU コストを削減できます。

  • クエリでプロパティをフィルター処理する場合は、最初にこれらのプロパティを ORDER BY 句に含める必要があります。

  • クエリで複数のプロパティをフィルター処理する場合は、等値フィルターを ORDER BY 句内で最初のプロパティとする必要があります。

  • クエリで複数のプロパティをフィルター処理する場合、複合インデックスごとに使用できる範囲フィルターまたはシステム関数の数は最大で 1 つとなります。 範囲フィルターまたはシステム関数で使用するプロパティは、複合インデックス内で最後に定義する必要があります。

  • 複合インデックスを、複数のプロパティが含まれる ORDER BY クエリ、および複数のプロパティに対するフィルターが含まれるクエリに対して作成する際の考慮事項もすべて、適用されます。

複合インデックス サンプル ORDER BY クエリ 複合インデックスでサポートされていますか?
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" ORDER BY c.name ASC, c.timestamp ASC Yes
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" AND c.timestamp > 1589840355 ORDER BY c.name ASC, c.timestamp ASC Yes
(timestamp ASC, name ASC) SELECT * FROM c WHERE c.timestamp > 1589840355 AND c.name = "Cofaz Jacket" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" ORDER BY c.timestamp ASC, c.name ASC No
(name ASC, timestamp ASC) SELECT * FROM c WHERE c.name = "Cofaz Jacket" ORDER BY c.timestamp ASC No
(quantity ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.inventory.quantity = 18 and c.name = "Cofaz Jacket" ORDER BY c.inventory.quantity ASC, c.name ASC,c.timestamp ASC Yes
(quantity ASC, name ASC, timestamp ASC) SELECT * FROM c WHERE c.inventory.quantity = 18 and c.name = "Cofaz Jacket" ORDER BY c.timestamp ASC No

フィルターと集計を使用したクエリ

クエリが 1 つ以上のプロパティをフィルター処理し、集計システム関数を持つ場合は、フィルターおよび集計システム関数のプロパティの複合インデックスを作成すると便利な場合があります。 この最適化は、 SUM および AVG システム関数に適用されます。

次の考慮事項は、フィルターと集計システム関数が含まれるクエリを最適化するために複合インデックスを作成する場合に適用されます。

  • 複合インデックスは、集計を使用したクエリを実行するときは省略可能です。 しかし、複合インデックスを使用すると、クエリの RU コストを削減できる場合がよくあります。

  • クエリで複数のプロパティをフィルター処理する場合、等値フィルターを複合インデックスの最初のプロパティとする必要があります。

  • 範囲フィルターを複合インデックスごとに最大で 1 つ設定でき、それを集計システム関数のプロパティに指定する必要があります。

  • 集計システム関数のプロパティは、複合インデックス内で最後に定義する必要があります。

  • order (ASC または DESC) は関係ありません。

複合インデックス サンプル クエリ 複合インデックスでサポートされていますか?
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "Cofaz Jacket" Yes
(timestamp ASC, name ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "Cofaz Jacket" No
(name ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name > "Cofaz Jacket" No
(name ASC, quantity ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity = 25 Yes
(quantity ASC, timestamp ASC) SELECT AVG(c.timestamp) FROM c WHERE c.name = "Cofaz Jacket" AND c.inventory.quantity > 25 No

配列ワイルドカードを使用した複合インデックス

配列ワイルドカードを含む複合インデックスの例を次に示します。

{
  "automatic": true,
  "indexingMode": "Consistent",
  "includedPaths": [
    {
      "path": "/*"
    }
  ],
  "excludedPaths": [],
  "compositeIndexes": [
    [
      {
        "path": "/name",
        "order": "ascending"
      },
      {
        "path": "/distributors/[]/name",
        "order": "descending"
      }
    ]
  ]
}

この複合インデックスを利用できるクエリの例を次に示します。

SELECT VALUE
  p.id
FROM
  products p
JOIN
  d IN p.distributors
WHERE
  p.name = "Cofaz Jacket" AND
  d.name > "Contoso"

インデックス作成ポリシーの変更

インデックス作成ポリシーを更新すると、古いインデックスから新しいインデックスへの変換がトリガーされます。 この変換は、インプレースおよびオンラインで実行されます。 操作中に余分なストレージ領域は消費されません。 古いインデックス作成ポリシーは、新しいポリシーに効率的に変換され、コンテナー上での書き込み可用性、読み取り可用性、またはプロビジョニングされたスループットが影響を受けることはありません。 インデックス変換は非同期操作であり、完了までにかかる時間は、プロビジョニングされたスループット、項目の数、およびそれらのサイズによって決まります。 複数のインデックス作成ポリシーを更新する必要がある場合は、1 回の操作ですべての変更を実行します。 この方法により、インデックス変換をより迅速に完了できます。

Important

インデックス変換は要求ユニットを消費する操作です。 いずれかの SDK を使用して、インデックス変換操作の進行状況と消費量を追跡できます。

インデックス変換中の書き込み可用性には影響しません。 インデックス変換にはプロビジョニングされた RU が使用されますが、CRUD 操作やクエリよりも低い優先順位になります。

新しいインデックス付きパスを追加しても、読み取り可用性に影響はありません。 クエリでは、インデックス変換が完了した後にのみ、新しいインデックス付きパスが使用されます。 つまり新しいインデックス付きパスを追加しているとき、そのインデックス付きパスの恩恵を受けるクエリのパフォーマンスは、インデックス変換の前と途中では同じです。 インデックス変換が完了すると、クエリ エンジンは新しいインデックス付きパスを使用し始めます。

インデックス付きパスを削除しているときは、すべての変更を 1 つのインデックス作成ポリシー変換にまとめる必要があります。 1 回のインデックス作成ポリシーの変更で、複数のインデックスを削除する場合は、クエリ エンジンによって、インデックス変換全体で結果の整合性と完全性が提供されます。 ただし、複数のインデックス作成ポリシーの変更によってインデックスを削除した場合、クエリ エンジンは、すべてのインデックス変換が完了するまで一貫性のある結果や完全な結果を提供しません。 ほとんどの開発者はインデックスを削除せず、すぐにこれらのインデックスを使用するクエリを実行しようとします。 実際には、このような状況はほとんどありません。

インデックス付きパスを削除すると、クエリ エンジンはすぐに使用を停止し、代わりにフル スキャンを実行します。 可能であれば、常に複数のインデックスの削除を 1 つのインデックス作成ポリシー変更にグループ化します。

インデックスの削除は直ちに反映されますが、新しいインデックスの追加は、インデックス作成変換が必要であるため、しばらく時間がかかります。 最初に新しいインデックスを追加してから、インデックス変換 が完了するまで 待ってから、インデックス作成ポリシーから前のインデックスを削除して、1 つのインデックスを別のインデックスに置き換えます。 たとえば、1 つのプロパティ インデックスを複合インデックスに置き換える場合は、この方法に従います。 それ以外の場合、この誤りは、前のインデックスに対してクエリを実行する機能に悪影響を及ぼし、前のインデックスを参照するアクティブなワークロードが中断される可能性があります。

インデックス作成ポリシーと TTL

Time-to-Live (TTL) 機能を使用するには、インデックス作成が必要です。

これは、次のことを意味します。

  • インデックス作成モードが noneに設定されているコンテナーで TTL をアクティブにすることはできません。

  • TTL がアクティブ化されているコンテナーでは、インデックス作成モードを None に設定することはできません。

インデックス作成が必要なプロパティ パスはないものの、TTL が必要なシナリオでは、インデックス作成モードが consistent に設定され、パスが含まれておらず、/* が除外された唯一のパスとなっているインデックス作成ポリシーを使用できます。