Azure AI Searchの検索インデックスに登録されているドキュメントを更新する場合のハマりそうな挙動についてです。
結論:ベクター埋め込み用に stored: false を使っている場合、ドキュメント更新するときは再度ベクター埋め込みしてMergeするときに一緒に渡さないとダメそう。
まず前提条件としてベクター埋め込み用のフィールド(Collection(Edm.Single)
などなフィールド)が含まれてて且つ stored プロパティが falseに設定されているものとします。
最近のアップデートでベクター埋め込み用のフィールドに対して stored プロパティおよび retrievable プロパティを false に設定することでベクター埋め込み用のストレージ容量を削減することができるようになっています。この状態だとベクター検索はできるけど、結果のドキュメントにはベクター埋め込みされた値は見えない感じになります。
さてそんな状態のドキュメントに対して、他のフィールドの値を変更しないといけない場合、通常であればmergeなどで変更対象のフィールドのみ変更することができます。なぜこんなことを言っているかというと、上記のベクター埋め込み用フィールドがある場合、変更対象に指定していないにも関わらずどうも空にされるような挙動をしてしまっているからです。(確認はしていないけど、おそらくベクター埋め込み云々は関係がない)
ちょっとどういった挙動か確認してみましょう。下記のようなドキュメントが登録されているものとします。1ドキュメントだけ存在していて、ベクターインデックスサイズも少しありますね。
検索結果には含まれていませんが、上記のドキュメントにはベクター埋め込み用のフィールドが含まれています(が、stored と retrievable がfalseなので検索結果には含まれません)。
検索文字列は空にしてベクター検索すると正しく結果が得られます。
次に上記のドキュメントを更新してみましょう。例として product フィールドのみ更新します。REST APIでmergeを実行してみます。(C# SDKのMergeDocumentsAsync() でも同じ結果になります)
var client = new HttpClient();
var data = new
{
value = new List<object>{
new Dictionary<string, object> {
{ "@search.action", "merge" },
{ "chunk_id", "chunk1" },
{ "parent_id", "content1" },
{ "product", "product_restAPI" }
}
}
};
data.Dump();
var req = new HttpRequestMessage()
{
Method = HttpMethod.Post,
RequestUri = new Uri($"{endpoint}/indexes('{indexName}')/docs/search.index?api-version=2024-05-01-preview"),
Content = new StringContent(data.ToJson(), Encoding.UTF8, "application/json")
};
req.Headers.Add("api-key", managementKey);
var ret = await client.SendAsync(req);
結果は指定したフィールドの値だけ更新されています、が、ベクターインデックスサイズは0になります。
この状態でドキュメントを検索してみるとベクター埋め込みだけのベクター検索だとHitしません。ベクター検索をしなければHitします。という感じで、現状Mergeするとstored: falseなベクター埋め込み用フィールドが無くなるっぽいです。保存して検索結果に入れることができる場合はクライアント側で取得できるので必要なら再度Merge時に渡すこともできますし、そもそも大丈夫なはず。
この挙動自体気に入らないのですが、現状ベクター埋め込み用に stored: false を使っている場合、ドキュメント更新するときは再度ベクター埋め込みしてMergeするときに一緒に渡さないとダメそうです。