diff --git a/Directory.Build.props b/Directory.Build.props index 23c19fd851..1a01927fc0 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,7 +3,7 @@ 3.56.0 3.57.0 preview.0 - 3.41.2 + 3.41.3 1.0.0 beta.0 2.0.5 diff --git a/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs b/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs index 8bf1310187..d12f3e8f44 100644 --- a/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs +++ b/Microsoft.Azure.Cosmos/src/Fluent/Settings/VectorIndexDefinition.cs @@ -50,6 +50,26 @@ public VectorIndexDefinition Path( return this; } + /// + /// Configures the quantizer type for the current definition. + /// + /// + /// The quantizer type to be used for vector quantization. This is an optional parameter and applies to index + /// types DiskANN and quantizedFlat. Allowed values are Product and Spherical. + /// + /// An instance of the current . +#if PREVIEW + public +#else + internal +#endif + VectorIndexDefinition WithQuantizerType( + QuantizerType quantizerType) + { + this.vectorIndexPath.QuantizerType = quantizerType; + return this; + } + /// /// Configures the quantization byte size for the current definition. /// diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs index acc18cf140..7587f88732 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/IndexingPolicy.cs @@ -591,6 +591,114 @@ public int GetHashCode(IndexingPolicy indexingPolicy) return hashCode; } } + + internal sealed class VectorIndexPathEqualityComparer : IEqualityComparer + { + public static readonly VectorIndexPathEqualityComparer Singleton = new VectorIndexPathEqualityComparer(); + + public bool Equals(VectorIndexPath vectorIndexPath1, VectorIndexPath vectorIndexPath2) + { + if (Object.ReferenceEquals(vectorIndexPath1, vectorIndexPath2)) + { + return true; + } + + if (vectorIndexPath1 == null || vectorIndexPath2 == null) + { + return false; + } + + if (vectorIndexPath1.Path != vectorIndexPath2.Path || + vectorIndexPath1.Type != vectorIndexPath2.Type || + vectorIndexPath1.QuantizerType != vectorIndexPath2.QuantizerType || + vectorIndexPath1.QuantizationByteSize != vectorIndexPath2.QuantizationByteSize || + vectorIndexPath1.IndexingSearchListSize != vectorIndexPath2.IndexingSearchListSize || + !vectorIndexPath1.AdditionalProperties.EqualsTo(vectorIndexPath2.AdditionalProperties)) + { + return false; + } + + // Compare VectorIndexShardKey arrays + if (vectorIndexPath1.VectorIndexShardKey == null && vectorIndexPath2.VectorIndexShardKey == null) + { + return true; + } + + if (vectorIndexPath1.VectorIndexShardKey == null || vectorIndexPath2.VectorIndexShardKey == null) + { + return false; + } + + if (vectorIndexPath1.VectorIndexShardKey.Length != vectorIndexPath2.VectorIndexShardKey.Length) + { + return false; + } + + HashSet shardKeys1 = new HashSet(vectorIndexPath1.VectorIndexShardKey); + HashSet shardKeys2 = new HashSet(vectorIndexPath2.VectorIndexShardKey); + + return shardKeys1.SetEquals(shardKeys2); + } + + public int GetHashCode(VectorIndexPath vectorIndexPath) + { + if (vectorIndexPath == null) + { + return 0; + } + + int hashCode = 0; + hashCode ^= vectorIndexPath.Path?.GetHashCode() ?? 0; + hashCode ^= vectorIndexPath.Type.GetHashCode(); + hashCode ^= vectorIndexPath.QuantizerType?.GetHashCode() ?? 0; + hashCode ^= vectorIndexPath.QuantizationByteSize.GetHashCode(); + hashCode ^= vectorIndexPath.IndexingSearchListSize.GetHashCode(); + + if (vectorIndexPath.VectorIndexShardKey != null) + { + foreach (string shardKey in vectorIndexPath.VectorIndexShardKey) + { + hashCode ^= shardKey?.GetHashCode() ?? 0; + } + } + + return hashCode; + } + } + + internal sealed class VectorIndexesEqualityComparer : IEqualityComparer> + { + private static readonly VectorIndexPathEqualityComparer vectorIndexPathEqualityComparer = new VectorIndexPathEqualityComparer(); + + public bool Equals(Collection vectorIndexes1, Collection vectorIndexes2) + { + if (Object.ReferenceEquals(vectorIndexes1, vectorIndexes2)) + { + return true; + } + + if (vectorIndexes1 == null || vectorIndexes2 == null) + { + return false; + } + + HashSet hashedVectorIndexes1 = new HashSet(vectorIndexes1, vectorIndexPathEqualityComparer); + HashSet hashedVectorIndexes2 = new HashSet(vectorIndexes2, vectorIndexPathEqualityComparer); + + return hashedVectorIndexes1.SetEquals(hashedVectorIndexes2); + } + + public int GetHashCode(Collection vectorIndexes) + { + int hashCode = 0; + foreach (VectorIndexPath vectorIndexPath in vectorIndexes) + { + hashCode ^= vectorIndexPathEqualityComparer.GetHashCode(vectorIndexPath); + } + + return hashCode; + } + } #endregion } } diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs new file mode 100644 index 0000000000..416fbbaa91 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/QuantizerType.cs @@ -0,0 +1,33 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ +namespace Microsoft.Azure.Cosmos +{ + using System.Runtime.Serialization; + using Newtonsoft.Json; + using Newtonsoft.Json.Converters; + + /// + /// Defines the quantizer type of a vector index path specification in the Azure Cosmos DB service. + /// + [JsonConverter(typeof(StringEnumConverter))] +#if PREVIEW + public +#else + internal +#endif + enum QuantizerType + { + /// + /// Represents a product quantizer type. + /// + [EnumMember(Value = "product")] + Product, + + /// + /// Represents a spherical quantizer type. + /// + [EnumMember(Value = "spherical")] + Spherical + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs index 642f66d430..6e0f2f845b 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/Settings/VectorIndexPath.cs @@ -39,6 +39,7 @@ namespace Microsoft.Azure.Cosmos /// { /// "path": "/embeddings/vector", /// "type": "DiskANN", + /// "quantizerType": "product", // or "spherical" /// "quantizationByteSize": 2, /// "indexingSearchListSize": 100, /// "vectorIndexShardKey": ["/Country"] @@ -68,6 +69,20 @@ public sealed class VectorIndexPath [JsonConverter(typeof(StringEnumConverter))] public VectorIndexType Type { get; set; } + /// + /// Gets or sets the quantizer type for the vector index path. This is only applicable for the quantizedFlat and diskann vector index types. + /// Allowed values are "product" and "spherical". + /// + [JsonProperty(PropertyName = Constants.Properties.QuantizerType, NullValueHandling = NullValueHandling.Ignore)] + [JsonConverter(typeof(StringEnumConverter))] +#if PREVIEW + public +#else + internal +#endif + QuantizerType? QuantizerType + { get; set; } + /// /// Gets or sets the quantization byte size for the vector index path. This is only applicable for the quantizedFlat and diskann vector index types. /// The allowed range for this parameter is between 1 and min(dimensions, 512). diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json index dfaa8667db..59663795dd 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetPreviewSDKAPI.net6.json @@ -1213,6 +1213,11 @@ "Attributes": [], "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizerType(Microsoft.Azure.Cosmos.QuantizerType)": { + "Type": "Method", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithQuantizerType(Microsoft.Azure.Cosmos.QuantizerType);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Microsoft.Azure.Cosmos.Fluent.VectorIndexDefinition`1[T] WithVectorIndexShardKey(System.String[])": { "Type": "Method", "Attributes": [], @@ -1332,6 +1337,31 @@ }, "NestedTypes": {} }, + "Microsoft.Azure.Cosmos.QuantizerType;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Int32 value__": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" + }, + "Microsoft.Azure.Cosmos.QuantizerType Product[System.Runtime.Serialization.EnumMemberAttribute(Value = \"product\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QuantizerType Product;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.QuantizerType Spherical[System.Runtime.Serialization.EnumMemberAttribute(Value = \"spherical\")]": { + "Type": "Field", + "Attributes": [ + "EnumMemberAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QuantizerType Spherical;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, "Microsoft.Azure.Cosmos.RequestOptions;System.Object;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { @@ -1511,10 +1541,32 @@ ], "MethodInfo": "Int32 QuantizationByteSize;CanRead:True;CanWrite:True;Int32 get_QuantizationByteSize();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] QuantizerType[Newtonsoft.Json.JsonPropertyAttribute(NullValueHandling = NullValueHandling.Ignore = 1, PropertyName = \"quantizerType\")]-[Newtonsoft.Json.JsonConverterAttribute(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]": { + "Type": "Property", + "Attributes": [ + "JsonConverterAttribute", + "JsonPropertyAttribute" + ], + "MethodInfo": "System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] QuantizerType;CanRead:True;CanWrite:True;System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType] get_QuantizerType();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_QuantizationByteSize(Int32)": { "Type": "Method", "Attributes": [], "MethodInfo": "Void set_QuantizationByteSize(Int32);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_QuantizerType(System.Nullable`1[Microsoft.Azure.Cosmos.QuantizerType]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs index 65f14d567c..d4d9f711a2 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosContainerSettingsTests.cs @@ -212,11 +212,11 @@ public void ValidateVectorEmbeddingsAndIndexes() }; Collection embeddings = new Collection() - { - embedding1, - embedding2, - embedding3, - }; + { + embedding1, + embedding2, + embedding3, + }; ContainerProperties containerSettings = new ContainerProperties(id: "TestContainer", partitionKeyPath: "/partitionKey") { @@ -224,28 +224,30 @@ public void ValidateVectorEmbeddingsAndIndexes() IndexingPolicy = new Cosmos.IndexingPolicy() { VectorIndexes = new() - { - new Cosmos.VectorIndexPath() - { - Path = "/vector1", - Type = Cosmos.VectorIndexType.Flat, - }, - new Cosmos.VectorIndexPath() - { - Path = "/vector2", - Type = Cosmos.VectorIndexType.QuantizedFlat, - VectorIndexShardKey = new[] { "/Country" }, - QuantizationByteSize = 3, - }, - new Cosmos.VectorIndexPath() - { - Path = "/vector3", - Type = Cosmos.VectorIndexType.DiskANN, - VectorIndexShardKey = new[] { "/ZipCode" }, - QuantizationByteSize = 2, - IndexingSearchListSize = 5, - } - }, + { + new Cosmos.VectorIndexPath() + { + Path = "/vector1", + Type = Cosmos.VectorIndexType.Flat, + }, + new Cosmos.VectorIndexPath() + { + Path = "/vector2", + Type = Cosmos.VectorIndexType.QuantizedFlat, + QuantizerType = Cosmos.QuantizerType.Product, + VectorIndexShardKey = new[] { "/Country" }, + QuantizationByteSize = 3, + }, + new Cosmos.VectorIndexPath() + { + Path = "/vector3", + Type = Cosmos.VectorIndexType.DiskANN, + QuantizerType = Cosmos.QuantizerType.Spherical, + VectorIndexShardKey = new[] { "/ZipCode" }, + QuantizationByteSize = 2, + IndexingSearchListSize = 5, + } + }, }, }; @@ -261,18 +263,99 @@ public void ValidateVectorEmbeddingsAndIndexes() Collection vectorIndexes = containerSettings.IndexingPolicy.VectorIndexes; Assert.AreEqual("/vector1", vectorIndexes[0].Path); Assert.AreEqual(Cosmos.VectorIndexType.Flat, vectorIndexes[0].Type); + Assert.IsNull(vectorIndexes[0].QuantizerType); // Flat type doesn't use quantizer + Assert.AreEqual("/vector2", vectorIndexes[1].Path); Assert.AreEqual(Cosmos.VectorIndexType.QuantizedFlat, vectorIndexes[1].Type); + Assert.AreEqual(Cosmos.QuantizerType.Product, vectorIndexes[1].QuantizerType); Assert.AreEqual(3, vectorIndexes[1].QuantizationByteSize); CollectionAssert.AreEqual(new string[] { "/Country" }, vectorIndexes[1].VectorIndexShardKey); Assert.AreEqual("/vector3", vectorIndexes[2].Path); Assert.AreEqual(Cosmos.VectorIndexType.DiskANN, vectorIndexes[2].Type); + Assert.AreEqual(Cosmos.QuantizerType.Spherical, vectorIndexes[2].QuantizerType); Assert.AreEqual(2, vectorIndexes[2].QuantizationByteSize); Assert.AreEqual(5, vectorIndexes[2].IndexingSearchListSize); CollectionAssert.AreEqual(new string[] { "/ZipCode" }, vectorIndexes[2].VectorIndexShardKey); } + [TestMethod] + public void ValidateVectorIndexQuantizerTypeSerialization() + { + // Test with Product quantizer + Cosmos.VectorIndexPath vectorIndexProduct = new Cosmos.VectorIndexPath() + { + Path = "/vector", + Type = Cosmos.VectorIndexType.DiskANN, + QuantizerType = Cosmos.QuantizerType.Product, + QuantizationByteSize = 2, + IndexingSearchListSize = 100 + }; + + // Serialize + string serializedProduct = JsonConvert.SerializeObject(vectorIndexProduct); + + // Verify the JSON contains quantizerType + Assert.IsTrue(serializedProduct.Contains("\"quantizerType\":\"product\"")); + + // Deserialize + Cosmos.VectorIndexPath deserializedProduct = JsonConvert.DeserializeObject(serializedProduct); + + // Verify round-trip + Assert.AreEqual(vectorIndexProduct.Path, deserializedProduct.Path); + Assert.AreEqual(vectorIndexProduct.Type, deserializedProduct.Type); + Assert.AreEqual(Cosmos.QuantizerType.Product, deserializedProduct.QuantizerType); + Assert.AreEqual(vectorIndexProduct.QuantizationByteSize, deserializedProduct.QuantizationByteSize); + Assert.AreEqual(vectorIndexProduct.IndexingSearchListSize, deserializedProduct.IndexingSearchListSize); + + // Test with Spherical quantizer + Cosmos.VectorIndexPath vectorIndexSpherical = new Cosmos.VectorIndexPath() + { + Path = "/embedding", + Type = Cosmos.VectorIndexType.QuantizedFlat, + QuantizerType = Cosmos.QuantizerType.Spherical, + QuantizationByteSize = 3, + VectorIndexShardKey = new[] { "/region" } + }; + + // Serialize + string serializedSpherical = JsonConvert.SerializeObject(vectorIndexSpherical); + + // Verify the JSON contains quantizerType + Assert.IsTrue(serializedSpherical.Contains("\"quantizerType\":\"spherical\"")); + + // Deserialize + Cosmos.VectorIndexPath deserializedSpherical = JsonConvert.DeserializeObject(serializedSpherical); + + // Verify round-trip + Assert.AreEqual(vectorIndexSpherical.Path, deserializedSpherical.Path); + Assert.AreEqual(vectorIndexSpherical.Type, deserializedSpherical.Type); + Assert.AreEqual(Cosmos.QuantizerType.Spherical, deserializedSpherical.QuantizerType); + Assert.AreEqual(vectorIndexSpherical.QuantizationByteSize, deserializedSpherical.QuantizationByteSize); + CollectionAssert.AreEqual(vectorIndexSpherical.VectorIndexShardKey, deserializedSpherical.VectorIndexShardKey); + + // Test with null quantizer type (for Flat index) + Cosmos.VectorIndexPath vectorIndexFlat = new Cosmos.VectorIndexPath() + { + Path = "/flatVector", + Type = Cosmos.VectorIndexType.Flat + }; + + // Serialize + string serializedFlat = JsonConvert.SerializeObject(vectorIndexFlat); + + // Verify the JSON doesn't contain quantizerType (null value handling) + Assert.IsFalse(serializedFlat.Contains("quantizerType")); + + // Deserialize + Cosmos.VectorIndexPath deserializedFlat = JsonConvert.DeserializeObject(serializedFlat); + + // Verify round-trip + Assert.AreEqual(vectorIndexFlat.Path, deserializedFlat.Path); + Assert.AreEqual(vectorIndexFlat.Type, deserializedFlat.Type); + Assert.IsNull(deserializedFlat.QuantizerType); + } + [TestMethod] public void ValidateFullTextPathsAndIndexes() { diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs index 98562625ef..251bc1f4df 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Fluent/ContainerDefinitionForCreateTests.cs @@ -543,11 +543,13 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder .Attach() .WithVectorIndex() .Path(vector2Path, VectorIndexType.QuantizedFlat) + .WithQuantizerType(QuantizerType.Product) .WithQuantizationByteSize(3) .WithVectorIndexShardKey(new string[] { "/Country" }) .Attach() .WithVectorIndex() .Path(vector3Path, VectorIndexType.DiskANN) + .WithQuantizerType(QuantizerType.Spherical) .WithQuantizationByteSize(2) .WithIndexingSearchListSize(35) .WithVectorIndexShardKey(new string[] { "/ZipCode" }) @@ -557,7 +559,8 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder Assert.AreEqual(HttpStatusCode.Created, response.StatusCode); mockContainers.Verify(c => c.CreateContainerAsync( - It.Is((settings) => settings.VectorEmbeddingPolicy.Embeddings.Count == 3 + It.Is((settings) => + settings.VectorEmbeddingPolicy.Embeddings.Count == 3 && vector1Path.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].Path) && VectorDataType.Int8.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].DataType) && DistanceFunction.DotProduct.Equals(settings.VectorEmbeddingPolicy.Embeddings[0].DistanceFunction) @@ -569,7 +572,23 @@ public async Task ValidateVectorEmbeddingsAndIndexingPolicyUsingContainerBuilder && vector3Path.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Path) && VectorDataType.Float32.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].DataType) && DistanceFunction.Euclidean.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].DistanceFunction) - && 400.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Dimensions)), + && 400.Equals(settings.VectorEmbeddingPolicy.Embeddings[2].Dimensions) + && settings.IndexingPolicy.VectorIndexes.Count == 3 + && vector1Path.Equals(settings.IndexingPolicy.VectorIndexes[0].Path) + && VectorIndexType.Flat.Equals(settings.IndexingPolicy.VectorIndexes[0].Type) + && settings.IndexingPolicy.VectorIndexes[0].QuantizerType == null + && vector2Path.Equals(settings.IndexingPolicy.VectorIndexes[1].Path) + && VectorIndexType.QuantizedFlat.Equals(settings.IndexingPolicy.VectorIndexes[1].Type) + && QuantizerType.Product.Equals(settings.IndexingPolicy.VectorIndexes[1].QuantizerType) + && 3.Equals(settings.IndexingPolicy.VectorIndexes[1].QuantizationByteSize) + && settings.IndexingPolicy.VectorIndexes[1].VectorIndexShardKey.SequenceEqual(new string[] { "/Country" }) + && vector3Path.Equals(settings.IndexingPolicy.VectorIndexes[2].Path) + && VectorIndexType.DiskANN.Equals(settings.IndexingPolicy.VectorIndexes[2].Type) + && QuantizerType.Spherical.Equals(settings.IndexingPolicy.VectorIndexes[2].QuantizerType) + && 2.Equals(settings.IndexingPolicy.VectorIndexes[2].QuantizationByteSize) + && 35.Equals(settings.IndexingPolicy.VectorIndexes[2].IndexingSearchListSize) + && settings.IndexingPolicy.VectorIndexes[2].VectorIndexShardKey.SequenceEqual(new string[] { "/ZipCode" }) + ), It.IsAny(), It.IsAny(), It.IsAny()), Times.Once);