親子インデックス作成用のインデックス プロジェクションを定義する
チャンクされたドキュメントを含むインデックスの場合、インデックス プロジェクションでは、1 対多のインデックスを作成するために、親子コンテンツを検索インデックス内のフィールドにマップする方法を指定します。 インデックス プロジェクションにより、コンテンツを以下に送信できます。
単一のインデックス。ここで、親フィールドはチャンクごとに繰り返されますが、インデックスの詳細度はチャンク レベルです。 このアプローチの例として、RAG チュートリアルがあります。
2 つ以上のインデックス。ここで、親インデックスには親ドキュメントに関連するフィールドがあり、子インデックスはチャンクを中心に編成されています。 子インデックスはプライマリ検索コーパスですが、特定のチャンクの親フィールドを取得する場合の参照クエリや、独立したクエリには親インデックスを使用できます。
ほとんどの実装は、ドキュメント ファイル名などの親フィールドを持つチャンクを中心に編成され、チャンクごとに繰り返される単一のインデックスです。 ただし、要件である場合は、個別の複数の子インデックスをサポートするようにシステムは設計されています。 Azure AI 検索ではインデックス結合がサポートされていないため、使用するインデックスをアプリケーション コードで処理する必要があります。
インデックス プロジェクションは、スキルセットで定義されます。 各チャンクに関連付けられている親コンテンツと一緒に、コンテンツのチャンクを検索インデックスに送信するインデックス作成プロセスを調整する役割を担います。 これにより、親子コンテンツのインデックス作成方法を制御するためのオプションを増やすことで、ネイティブ データのチャンク分割の仕組みが改善されます。
この記事では、一対多のインデックス作成用のインデックス スキーマとインデクサー プロジェクション パターンを作成する方法について説明します。
前提条件
インデックス作成パイプラインの出力を受け入れるインデックス (1 つ以上)。
チャンクするコンテンツがある、サポートされているデータ ソース。 ベクトルまたはベクトル以外のコンテンツにすることができます。
コンテンツを複数のチャンクに分割するスキル (テキスト分割スキル、または同等の機能を提供するカスタム スキル)。
スキルセットには、一対多のインデックス作成のためにデータを整形するインデクサー プロジェクションが含まれています。 また、シナリオに統合ベクトル化が含まれている場合は、AzureOpenAIEmbedding のような埋め込みスキルなど、他のスキルをスキルセットが持つこともできます。
インデクサー処理への依存関係
一対多のインデックス作成は、スキルセットと次の 4 つのコンポーネントを含むインデクサーベースのインデックス作成に依存します。
- データ ソース
- 検索可能なコンテンツの 1 つ以上のインデックス
- インデックス プロジェクションを含むスキルセット*
- インデクサー
データは、サポートされている任意のデータ ソースから生成できますが、コンテンツがチャンクするのに十分な大きさであることを前提としています。また、チャンクの理由は、チャット モデルにグラウンディング データを提供する RAG パターンを実装しているためです。 または、ベクトル検索を実装しており、埋め込みモデルのより小さい入力サイズという要件を満たす必要があるためです。
インデクサーは、定義済みのインデックスにインデックス付きデータを読み込みます。 スキーマを定義する方法と、1 つ以上のインデックスを使用するかどうかが、1 対多のインデックス作成シナリオで行う最初の決定です。 次のセクションでは、インデックスの設計について説明します。
一対多のインデックス作成用のインデックスを作成する
親値を繰り返すチャンクに対して 1 つのインデックスを作成するか、親子フィールド配置用に別々のインデックスを作成するかにかかわらず、検索に使用されるプライマリ インデックスはデータ チャンクを中心に設計されています。 次のフィールドが必要です。
各ドキュメントを一意に識別するドキュメント キー フィールド。
keyword
アナライザーでEdm.String
型として定義されなければなりません。各チャンクを親に関連付けるフィールド。
Edm.String
型でなければなりません。 ドキュメント キー フィールドにすることはできません。filterable
が true に設定される必要があります。 例では parent_id と呼ばれ、この記事ではプロジェクションされるキーの値と呼ばれます。テキストやベクトル化されたチャンク フィールドなど、コンテンツのその他のフィールド。
スキルセットを作成またはインデクサーを実行する前に、検索サービスにインデックスが存在している必要があります。
親フィールドと子フィールドを含む単一インデックス スキーマ
チャンクごとに親コンテンツが繰り返される、チャンクを中心に設計された単一のインデックスは、RAG およびベクトル検索シナリオの主要なパターンです。 正しい親コンテンツを各チャンクに関連付ける機能は、インデックス プロジェクションによって有効になります。
次のスキーマは、インデックス プロジェクションの要件を満たす例です。 この例では、親フィールドは parent_id と title です。 子フィールドは、ベクトルおよび非ベクトル チャンクです。 chunk_id は、このインデックスのドキュメント ID です。 インデックス内のすべてのチャンクに対して、parent_id と title が繰り返されます。
Azure portal、REST API、または Azure SDK を使用してインデックスを作成できます。
{
"name": "my_consolidated_index",
"fields": [
{"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
{"name": "parent_id", "type": "Edm.String", "filterable": true},
{"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
{"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
{"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
],
"vectorSearch": {
"algorithms": [{"name": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
"profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
}
}
スキルセットにインデックス プロジェクションを追加する
インデックス プロジェクションはスキルセット定義内で定義され、そして主に selectors
の配列として定義されます。この配列では、各セレクターが検索サービス上の異なるターゲット インデックスに対応します。 このセクションでは、コンテキストの構文と例から始まり、パラメーターの参照が続きます。
さまざまな API 構文のタブを選択します。 現在、スキルセット JSON 定義の編集以外に、プロジェクションを設定するためのポータルのサポートはありません。 JSON の REST の例を参照してください。
インデックス プロジェクションは一般提供されています。 最新の安定版 API をお勧めします。
テキスト分割スキルによって出力された個々のページを、検索インデックス内のそれ自体のドキュメントとしてプロジェクションするのに使用する、インデックス プロジェクション定義のペイロードの例を次に示します。
"indexProjections": {
"selectors": [
{
"targetIndexName": "my_consolidated_index",
"parentKeyFieldName": "parent_id",
"sourceContext": "/document/pages/*",
"mappings": [
{
"name": "chunk",
"source": "/document/pages/*",
"sourceContext": null,
"inputs": []
},
{
"name": "chunk_vector",
"source": "/document/pages/*/chunk_vector",
"sourceContext": null,
"inputs": []
},
{
"name": "title",
"source": "/document/title",
"sourceContext": null,
"inputs": []
}
]
}
],
"parameters": {
"projectionMode": "skipIndexingParentDocuments"
}
}
パラメーター リファレンス
インデックス プロジェクション パラメーター | Definition |
---|---|
selectors |
メイン検索コーパスのパラメーター (通常はチャンクを中心に設計されたもの)。 |
projectionMode |
インデクサーに指示を提供する省略可能なパラメーター。 このパラメーターの有効な値は skipIndexingParentDocuments のみです。チャンク インデックスがプライマリ検索コーパスであり、親フィールドがチャンク インデックス内の追加の検索ドキュメントとしてインデックス付けされるかどうかを指定する必要がある場合に使用されます。 skipIndexingParentDocuments を設定しない場合は、インデックスで追加の検索ドキュメントが表示されます。これらのドキュメントは、チャンクには null ですが、親フィールドのみに設定されます。 たとえば、5 つのドキュメントがインデックスに 100 個のチャンクを提供する場合、インデックス内のドキュメントの数は 105 になります。 作成された 5 つのドキュメントすなわち親フィールドには、チャンク (子) フィールドの null ががあり、インデックス内のドキュメントの大部分とは大きく異なります。 projectionMode を skipIndexingParentDocument に設定することをお勧めします。 |
セレクターには、定義の一部として次のパラメーターがあります。
セレクター パラメーター | Definition |
---|---|
targetIndexName |
インデックス データがプロジェクションされるインデックスの名前。 これは、親フィールドを繰り返す単一のチャンク インデックスであるか、親子コンテンツに別々のインデックスを使用している場合は子インデックスです。 |
parentKeyFieldName |
親ドキュメントのキーを提供するフィールドの名前。 |
sourceContext |
個々の検索ドキュメントにデータをマッピングする細分性を定義するエンリッチメント注釈。 詳細については、「スキル コンテキストと入力注釈言語」を参照してください。 |
mappings |
検索インデックス内のフィールドへのエンリッチされたデータのマッピングの配列。 各マッピングは次で構成されます。name : データのインデックスを付ける検索インデックス内のフィールドの名前。 source : データをプルするエンリッチメント注釈パス。 各 mapping では、ナレッジ ストアや Shaper スキルと同様に、オプションの sourceContext および inputs フィールドを使用してデータを再帰的に定義することもできます。 アプリケーションによっては、これらのパラメーターを使用すると、検索インデックス内の Edm.ComplexType 型のフィールドにデータを整形できます。 一部の LLM は検索結果で複合型を受け入れないので、使用している LLM によって、複合型マッピングが役に立つかどうかが決まります。 |
mappings
パラメーターは重要です。 ドキュメント キーや親 ID などの ID フィールドを除き、子インデックス内のすべてのフィールドを明示的にマップする必要があります。
この要件は、Azure AI 検索の他のフィールド マッピング規則とは異なります。 一部のデータ ソースの種類では、インデクサーは、同様の名前または既知の特性に基づいてフィールドを暗黙的にマップできます (たとえば、BLOB インデクサーは、既定のドキュメント キーとして一意のメタデータ ストレージ パスを使用します)。 ただし、インデクサー プロジェクションの場合は、リレーションシップの "多" の側にすべてのフィールド マッピングを明示的に指定する必要があります。
親キー フィールドの、フィールドのマッピングは作成しないでください。 これを行うと、変更履歴と同期されたデータ更新が中断されます。
親ドキュメントの処理
一対多のインデックス作成のいくつかのパターンを見てきたので、各オプションに関する主な違いを比較してみましょう。 インデックス プロジェクションでは、スキルセットを介して実行される "親" ドキュメントごとに "子" ドキュメントが効果的に生成されます。 "親" ドキュメントを処理するには、いくつかの選択肢があります。
親ドキュメントと子ドキュメントを個別のインデックスに送信するには、インデクサー定義の
targetIndexName
を親インデックスに設定し、インデックス プロジェクション セレクターのtargetIndexName
を子インデックスに設定します。親ドキュメントと子ドキュメントを同じインデックスに保持するには、インデクサーの
targetIndexName
とインデックス プロジェクションのtargetIndexName
を同じインデックスに設定します。親検索ドキュメントの作成と、インデックスに均一の詳細度の子ドキュメントのみが含まれていることの確認を避けるには、インデクサー定義とセレクターの両方の
targetIndexName
を同じインデックスに設定します。ただし、次に示すようにselectors
の後に追加のparameters
オブジェクトを追加し、projectionMode
キーをskipIndexingParentDocuments
に設定します。"indexProjections": { "selectors": [ ... ], "parameters": { "projectionMode": "skipIndexingParentDocuments" } }
フィールド マッピングを確認する
インデクサーは、3 種類のフィールド マッピングに関連付けられます。 インデクサーを実行する前に、フィールド マッピングを確認し、各種類を使用するタイミングを把握してください。
フィールド マッピングはインデクサーで定義され、ソース フィールドをインデックス フィールドにマップするために使用されます。 フィールド マッピングは、中間のスキル処理手順なしで、ソースからデータをリフトし、インデックス作成のために渡すデータ パスに使用されます。 通常、インデクサーは、名前と種類が同じフィールドを自動的にマップできます。 不一致がある場合にのみ、明示的なフィールド マッピングが必要です。 一対多のインデックス作成とこれまでに説明したパターンでは、フィールド マッピングが必要ない場合があります。
出力フィールド マッピングはインデクサーで定義され、スキルセットによって生成されたエンリッチ済みコンテンツをメイン インデックスのフィールドにマップするために使用されます。 この記事で取り上げる一対多のパターンでは、これは 2 つのインデックスによるソリューションにおける親インデックスです。 この記事で示す例では、親インデックスは title フィールドのみを含むスパースであり、そのフィールドにはスキルセット処理のコンテンツが取り込まれていないため、出力フィールドマッピングは行われません。
インデクサー プロジェクションのフィールド マッピングは、スキルセットによって生成されたコンテンツを子インデックス内のフィールドにマップするために使用されます。 子インデックスに親フィールドも含まれている場合 (統合インデックス ソリューションのように)、コンテンツを持つすべてのフィールド (親レベルの title フィールドを含む) に対してフィールド マッピングを設定する必要があります。これは、タイトルがチャンクされた各ドキュメントに表示されることを前提とします。 個別の親インデックスと子インデックスを使用している場合、インデクサー プロジェクションには、子レベルのフィールドのみに対するフィールド マッピングが必要です。
Note
出力フィールド マッピングとインデクサー プロジェクションのフィールド マッピングはどちらも、エンリッチ済みドキュメント ツリー ノードをソース入力として受け入れます。 各ノードへのパスを指定する方法を知ることが、データ パスの設定に不可欠です。 パス構文の詳細を確認するには、例について、エンリッチメントされたノードへのパスを参照するに関するページと「スキルセットの定義」を参照してください。
インデクサーを実行する
データ ソース、インデックス、スキルセットを作成したら、インデクサーを作成して実行する準備ができました。 この手順では、パイプラインを実行します。
処理の完了後に検索インデックスに対してクエリを実行して、ソリューションをテストできます。
コンテンツのライフサイクル
基になるデータ ソースに応じて、インデクサーは通常、継続的な変更の追跡と削除の検出を提供できます。 このセクションでは、データ更新に関連する一対多インデックス作成のコンテンツのライフサイクルについて説明します。
変更の追跡と削除の検出を提供するデータ ソースの場合、インデクサー プロセスはソース データの変更を取得できます。 インデクサーとスキルセットを実行するたび、スキルセットまたは基になるソース データが変更された場合には、インデックス プロジェクションが更新されます。 インデクサーによって取得された変更は、エンリッチメント プロセスを通じてインデックス内のプロジェクションに反映され、プロジェクションされたデータが元のデータ ソース内のコンテンツの現在の表現になります。 データ更新アクティビティは、チャンクごとにプロジェクションされるキーの値でキャプチャされます。 基になるデータが変更されると、この値が更新されます。
Note
インデックス プッシュ API を使用して、プロジェクションされたドキュメント内のデータを手動で編集できますが、これは避けてください。 ソース データ内のドキュメントが更新され、データ ソースで変更の追跡または削除の検出が有効になっている場合、インデックスの手動更新は、次のパイプライン呼び出しで上書きされます。
変更内容
データ ソースに新しいコンテンツを追加すると、次のインデクサーの実行時に新しいチャンクまたは子ドキュメントがインデックスに追加されます。
データ ソース内の既存のコンテンツを変更すると、使用しているデータ ソースが変更の追跡と削除の検出をサポートしている場合、チャンクは検索インデックス内で段階的に更新されます。 たとえば、ドキュメント内で語または文が変更されると、その語または文を含むターゲット インデックス内のチャンクは、次のインデクサー実行時に更新されます。 フィールドの種類や属性の変更など、その他の種類の更新は、既存のフィールドではサポートされていません。 許可される更新の詳細については、「インデックス スキーマを変更する」を参照してください。
Azure Storage などの一部のデータ ソースでは、タイムスタンプに基づく変更と削除の追跡が既定でサポートされています。 OneLake、Azure SQL、Azure Cosmos DB などのその他のデータ ソースは、変更の追跡用に構成する必要があります。
削除されたコンテンツ
ソース コンテンツが存在しなくなった場合 (たとえば、チャンク数を減らすためにテキストが短縮された場合)、検索インデックス内の対応する子ドキュメントが削除されます。 残りの子ドキュメントも、そのコンテンツが他に変更されていなくても、新しいハッシュ値を含むようにキーが更新されます。
親ドキュメントがデータソースから完全に削除された場合、対応する子ドキュメントは、データソース定義で定義された dataDeletionDetectionPolicy
によって削除が検出された場合にのみ削除されます。 dataDeletionDetectionPolicy
が構成されていなくて、データソースから親ドキュメントを削除する必要がある場合は、不要であれば子ドキュメントを手動で削除する必要があります。
プロジェクションされるキーの値
更新および削除されたコンテンツのデータ整合性を確保するために、一対多のインデックス作成でのデータ更新は、"多" 側のプロジェクションされるキーの値に依存します。 統合ベクトル化またはデータのインポートとベクトル化ウィザードを使用している場合、プロジェクションされるキーの値は、インデックスのチャンクされた側すなわち "多" 側の parent_id
フィールドです。
プロジェクションされるキーの値は、インデクサーがドキュメントごとに生成する一意の識別子です。 これにより、一意性が確保され、変更と削除の追跡が正しく機能できるようになります。 このキーには、次のセグメントが含まれています。
- 一意性を保証するランダム ハッシュ。 このハッシュは、親ドキュメントが後続のインデクサーの実行時に更新された場合に変更されます。
- 親ドキュメントのキー。
- ドキュメントの生成元のコンテキストを識別するエンリッチメント注釈パス。
たとえば、キー値が "aa1b22c33" の親ドキュメントを 4 ページに分割し、それらの各ページがインデックス プロジェクションによりそれ自体のドキュメントとしてプロジェクションされる場合、次のようになります。
- aa1b22c33
- aa1b22c33_pages_0
- aa1b22c33_pages_1
- aa1b22c33_pages_2
ソース データで親ドキュメントが更新され、おそらくチャンクされたページが増えると、ランダム ハッシュが変更され、追加されるページが増え、各チャンクのコンテンツがソース ドキュメント内の内容と一致するように更新されます。
個別の親子インデックスの例
このセクションでは、個別の親インデックスと子インデックスの例を示します。 これは一般的ではないパターンですが、アプリケーション要件がこのアプローチを使用して最もよく満たされる可能性があります。 このシナリオでは、親子コンテンツを 2 つの個別のインデックスにプロジェクションします。
各スキーマには特定の詳細度のフィールドがあり、親 ID フィールドは、参照クエリで使用するために両方のインデックスに共通です。 プライマリ検索コーパスは子インデックスですが、参照クエリを発行して、結果内の一致ごとに親フィールドを取得してください。 Azure AI 検索はクエリ時の結合をサポートしないため、アプリケーション コードまたはオーケストレーション レイヤーは、アプリまたはプロセスに渡すことができる結果をマージまたは照合する必要があります。
親インデックスには、parent_id フィールドと title があります。 parent_id はドキュメント キーです。 親ドキュメント レベルでフィールドをベクトル化しない限り、ベクトル検索構成は必要ありません。
{
"name": "my-parent-index",
"fields": [
{"name": "parent_id", "type": "Edm.String", "filterable": true},
{"name": "title", "type": "Edm.String", "searchable": true, "filterable": true, "sortable": true, "retrievable": true},
]
}
子インデックスには、チャンク フィールドとparent_id フィールドがあります。 統合ベクトル化、スコア付けプロファイル、セマンティック ランカー、またはアナライザーを使用している場合は、子インデックスでこれらを設定します。
{
"name": "my-child-index",
"fields": [
{"name": "chunk_id", "type": "Edm.String", "key": true, "filterable": true, "analyzer": "keyword"},
{"name": "parent_id", "type": "Edm.String", "filterable": true},
{"name": "chunk", "type": "Edm.String","searchable": true,"retrievable": true},
{"name": "chunk_vector", "type": "Collection(Edm.Single)", "searchable": true, "retrievable": false, "stored": false, "dimensions": 1536, "vectorSearchProfile": "hnsw"}
],
"vectorSearch": {
"algorithms": [{"name": "hsnw", "kind": "hnsw", "hnswParameters": {}}],
"profiles": [{"name": "hsnw", "algorithm": "hnsw"}]
},
"scoringProfiles": [],
"semanticConfiguration": [],
"analyzers": []
}
インデクサーがコンテンツのインデックス作成に使用するデータ パスを指定するインデックス プロジェクション定義の例を次に示します。 インデックス プロジェクション定義で子インデックス名を指定し、すべての子またはチャンク レベル フィールドのマッピングを指定します。 これは、子インデックス名が指定される唯一の場所です。
"indexProjections": {
"selectors": [
{
"targetIndexName": "my-child-index",
"parentKeyFieldName": "parent_id",
"sourceContext": "/document/pages/*",
"mappings": [
{
"name": "chunk",
"source": "/document/pages/*",
"sourceContext": null,
"inputs": []
},
{
"name": "chunk_vector",
"source": "/document/pages/*/chunk_vector",
"sourceContext": null,
"inputs": []
}
]
}
]
}
インデクサー定義では、パイプラインのコンポーネントを指定します。 インデクサー定義では、指定するインデックス名は親インデックスです。 親レベルのフィールドのフィールド マッピングが必要な場合は、outputFieldMappings で定義します。 個別のインデックスを使用する一対多のインデックス作成の場合、インデクサー定義は次の例のようになります。
{
"name": "my-indexer",
"dataSourceName": "my-ds",
"targetIndexName": "my-parent-index",
"skillsetName" : "my-skillset"
"parameters": { },
"fieldMappings": (optional) Maps fields in the underlying data source to fields in an index,
"outputFieldMappings" : (required) Maps skill outputs to fields in an index,
}
次のステップ
データ チャンクと一対多のインデックス作成は、Azure AI 検索の RAG パターンの一部です。 詳細については、次のチュートリアルとコード サンプルに進んでください。