diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props index 3c5217f13345..f16128c50b63 100644 --- a/dotnet/Directory.Packages.props +++ b/dotnet/Directory.Packages.props @@ -161,7 +161,7 @@ - + diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/CosmosMongoDB/CosmosMongoVectorStoreFixture.cs b/dotnet/src/IntegrationTests/Connectors/Memory/CosmosMongoDB/CosmosMongoVectorStoreFixture.cs index 2aa0511e9289..f4427b6d5bef 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/CosmosMongoDB/CosmosMongoVectorStoreFixture.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/CosmosMongoDB/CosmosMongoVectorStoreFixture.cs @@ -40,7 +40,9 @@ public CosmosMongoVectorStoreFixture() .Build(); var connectionString = GetConnectionString(configuration); +#pragma warning disable CA2000 // Dispose objects before losing scope var client = new MongoClient(connectionString); +#pragma warning restore CA2000 this.MongoDatabase = client.GetDatabase("test"); diff --git a/dotnet/src/IntegrationTests/Connectors/Memory/MongoDB/MongoDBVectorStoreFixture.cs b/dotnet/src/IntegrationTests/Connectors/Memory/MongoDB/MongoDBVectorStoreFixture.cs index 51ca1e3565da..5a17920cff6f 100644 --- a/dotnet/src/IntegrationTests/Connectors/Memory/MongoDB/MongoDBVectorStoreFixture.cs +++ b/dotnet/src/IntegrationTests/Connectors/Memory/MongoDB/MongoDBVectorStoreFixture.cs @@ -37,11 +37,13 @@ public async Task InitializeAsync() cts.CancelAfter(TimeSpan.FromSeconds(60)); await this._container.StartAsync(cts.Token); +#pragma warning disable CA2000 // Dispose objects before losing scope var mongoClient = new MongoClient(new MongoClientSettings { Server = new MongoServerAddress(this._container.Hostname, this._container.GetMappedPublicPort(MongoDbBuilder.MongoDbPort)), DirectConnection = true, }); +#pragma warning restore CA2000 this.MongoDatabase = mongoClient.GetDatabase("test"); diff --git a/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/BsonValueFactory.cs b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/BsonValueFactory.cs new file mode 100644 index 000000000000..6787d9e07166 --- /dev/null +++ b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/BsonValueFactory.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using MongoDB.Bson; + +namespace Microsoft.SemanticKernel.Connectors.MongoDB; + +/// +/// A class that constructs the correct BsonValue for a given CLR type. +/// +internal static class BsonValueFactory +{ + /// + /// Create a BsonValue for the given CLR type. + /// + /// The CLR object to create a BSON value for. + /// The appropriate for that CLR type. + public static BsonValue Create(object? value) + => value switch + { + null => BsonNull.Value, + Guid guid => new BsonBinaryData(guid, GuidRepresentation.Standard), + object[] array => new BsonArray(Array.ConvertAll(array, Create)), + Array array => new BsonArray(array), + IEnumerable enumerable => new BsonArray(enumerable.Select(Create)), + _ => BsonValue.Create(value) + }; +} diff --git a/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoDynamicMapper.cs b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoDynamicMapper.cs index b301df567520..4ee72ae2ac8f 100644 --- a/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoDynamicMapper.cs +++ b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoDynamicMapper.cs @@ -30,13 +30,13 @@ public BsonDocument MapFromDataToStorageModel(Dictionary dataMo : keyValue switch { string s => s, - Guid g => BsonValue.Create(g), + Guid g => new BsonBinaryData(g, GuidRepresentation.Standard), ObjectId o => o, long i => i, int i => i, null => throw new InvalidOperationException($"Key property '{model.KeyProperty.ModelName}' is null."), - _ => throw new InvalidCastException($"Key property '{model.KeyProperty.ModelName}' must be a string.") + _ => throw new InvalidCastException($"Key property '{model.KeyProperty.ModelName}' must be a string, Guid, ObjectID, long or int.") } }; @@ -44,7 +44,7 @@ public BsonDocument MapFromDataToStorageModel(Dictionary dataMo { if (dataModel.TryGetValue(property.ModelName, out var dataValue)) { - document[property.StorageName] = BsonValue.Create(dataValue); + document[property.StorageName] = BsonValueFactory.Create(dataValue); } } diff --git a/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoMapper.cs b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoMapper.cs index 85a5d0462bb0..5f42b66a3849 100644 --- a/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoMapper.cs +++ b/dotnet/src/InternalUtilities/connectors/Memory/MongoDB/MongoMapper.cs @@ -11,6 +11,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Conventions; +using MongoDB.Bson.Serialization.Serializers; namespace Microsoft.SemanticKernel.Connectors.MongoDB; @@ -40,7 +41,8 @@ public MongoMapper(CollectionModel model) var conventionPack = new ConventionPack { - new IgnoreExtraElementsConvention(ignoreExtraElements: true) + new IgnoreExtraElementsConvention(ignoreExtraElements: true), + new GuidStandardRepresentationConvention() }; ConventionRegistry.Register( @@ -139,4 +141,15 @@ public TRecord MapFromStorageToDataModel(BsonDocument storageModel, bool include return BsonSerializer.Deserialize(storageModel); } + + private class GuidStandardRepresentationConvention : ConventionBase, IMemberMapConvention + { + public void Apply(BsonMemberMap memberMap) + { + if (memberMap.MemberType == typeof(Guid) && memberMap.MemberInfo.GetCustomAttribute() is null) + { + memberMap.SetSerializer(new GuidSerializer(GuidRepresentation.Standard)); + } + } + } } diff --git a/dotnet/src/InternalUtilities/src/Diagnostics/NullableAttributes.cs b/dotnet/src/InternalUtilities/src/Diagnostics/NullableAttributes.cs index 91d716132ced..7996d2b292bd 100644 --- a/dotnet/src/InternalUtilities/src/Diagnostics/NullableAttributes.cs +++ b/dotnet/src/InternalUtilities/src/Diagnostics/NullableAttributes.cs @@ -7,9 +7,10 @@ // This was copied from https://github.com/dotnet/runtime/blob/39b9607807f29e48cae4652cd74735182b31182e/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs // and updated to have the scope of the attributes be internal. -#if !NETCOREAPP namespace System.Diagnostics.CodeAnalysis; +#if !NETCOREAPP && !NETSTANDARD2_1 + /// Specifies that null is allowed as an input even if the corresponding type disallows it. [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] internal sealed class AllowNullAttribute : Attribute diff --git a/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs b/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs index 621025f1e39a..3a6b5b9fe111 100644 --- a/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs +++ b/dotnet/src/InternalUtilities/src/Http/HttpClientProvider.cs @@ -88,7 +88,7 @@ private static SocketsHttpHandler CreateHandler() }, }; } -#elif NETSTANDARD2_0 +#elif NETSTANDARD2_0_OR_GREATER private static HttpClientHandler CreateHandler() { var handler = new HttpClientHandler(); @@ -99,7 +99,7 @@ private static HttpClientHandler CreateHandler() catch (PlatformNotSupportedException) { } // not supported on older frameworks return handler; } -#elif NET462 +#elif NETFRAMEWORK private static HttpClientHandler CreateHandler() => new(); #endif diff --git a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollection.cs b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollection.cs index 16dba308f78f..6131f439f624 100644 --- a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollection.cs +++ b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollection.cs @@ -15,6 +15,7 @@ using Microsoft.Extensions.VectorData.ProviderServices; using Microsoft.SemanticKernel.Connectors.MongoDB; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver; using MEVD = Microsoft.Extensions.VectorData; @@ -67,6 +68,9 @@ public class CosmosMongoCollection : VectorStoreCollectionThe size of the dynamic candidate list for search. private readonly int _efSearch; + /// to use for serializing key values. + private readonly BsonSerializationInfo? _keySerializationInfo; + private static readonly Type[] s_validKeyTypes = [typeof(string), typeof(Guid), typeof(ObjectId), typeof(int), typeof(long)]; /// @@ -123,6 +127,11 @@ internal CosmosMongoCollection(IMongoDatabase mongoDatabase, string name, Func @@ -142,9 +151,9 @@ await this.RunOperationAsync("CreateIndexes", /// public override async Task DeleteAsync(TKey key, CancellationToken cancellationToken = default) { - var stringKey = this.GetStringKey(key); + Verify.NotNull(key); - await this.RunOperationAsync("DeleteOne", () => this._mongoCollection.DeleteOneAsync(this.GetFilterById(stringKey), cancellationToken)) + await this.RunOperationAsync("DeleteOne", () => this._mongoCollection.DeleteOneAsync(this.GetFilterById(key), cancellationToken)) .ConfigureAwait(false); } @@ -153,9 +162,7 @@ public override async Task DeleteAsync(IEnumerable keys, CancellationToken { Verify.NotNull(keys); - var stringKeys = keys is IEnumerable k ? k : keys.Cast(); - - await this.RunOperationAsync("DeleteMany", () => this._mongoCollection.DeleteManyAsync(this.GetFilterByIds(stringKeys), cancellationToken)) + await this.RunOperationAsync("DeleteMany", () => this._mongoCollection.DeleteManyAsync(this.GetFilterByIds(keys), cancellationToken)) .ConfigureAwait(false); } @@ -166,7 +173,7 @@ public override Task EnsureCollectionDeletedAsync(CancellationToken cancellation /// public override async Task GetAsync(TKey key, RecordRetrievalOptions? options = null, CancellationToken cancellationToken = default) { - var stringKey = this.GetStringKey(key); + Verify.NotNull(key); var includeVectors = options?.IncludeVectors ?? false; if (includeVectors && this._model.EmbeddingGenerationRequired) @@ -175,7 +182,7 @@ public override Task EnsureCollectionDeletedAsync(CancellationToken cancellation } using var cursor = await this - .FindAsync(this.GetFilterById(stringKey), top: 1, skip: null, includeVectors, sortDefinition: null, cancellationToken) + .FindAsync(this.GetFilterById(key), top: 1, skip: null, includeVectors, sortDefinition: null, cancellationToken) .ConfigureAwait(false); var record = await cursor.SingleOrDefaultAsync(cancellationToken).ConfigureAwait(false); @@ -202,10 +209,8 @@ public override async IAsyncEnumerable GetAsync( throw new NotSupportedException(VectorDataStrings.IncludeVectorsNotSupportedWithEmbeddingGeneration); } - var stringKeys = keys is IEnumerable k ? k : keys.Cast(); - using var cursor = await this - .FindAsync(this.GetFilterByIds(stringKeys), top: null, skip: null, includeVectors, sortDefinition: null, cancellationToken) + .FindAsync(this.GetFilterByIds(keys), top: null, skip: null, includeVectors, sortDefinition: null, cancellationToken) .ConfigureAwait(false); while (await cursor.MoveNextAsync(cancellationToken).ConfigureAwait(false)) @@ -252,7 +257,7 @@ private async Task UpsertCoreAsync(TRecord record, int recordIndex, IReadOnlyLis var replaceOptions = new ReplaceOptions { IsUpsert = true }; var storageModel = this._mapper.MapFromDataToStorageModel(record, recordIndex, generatedEmbeddings); - var key = storageModel[MongoConstants.MongoReservedKeyPropertyName].AsString; + var key = GetStorageKey(storageModel); await this.RunOperationAsync(OperationName, async () => await this._mongoCollection @@ -260,6 +265,9 @@ await this._mongoCollection .ConfigureAwait(false)).ConfigureAwait(false); } + private static TKey GetStorageKey(BsonDocument document) + => (TKey)BsonTypeMapper.MapToDotNetValue(document[MongoConstants.MongoReservedKeyPropertyName]); + private static async ValueTask<(IEnumerable records, IReadOnlyList?[]?)> ProcessEmbeddingsAsync( CollectionModel model, IEnumerable records, @@ -562,11 +570,40 @@ private async IAsyncEnumerable> EnumerateAndMapSearc } } - private FilterDefinition GetFilterById(string id) - => Builders.Filter.Eq(document => document[MongoConstants.MongoReservedKeyPropertyName], id); + private FilterDefinition GetFilterById(TKey id) + { + // Use cached key serialization info but fall back to BsonValueFactory for dynamic mapper. + var bsonValue = this._keySerializationInfo?.SerializeValue(id) ?? BsonValueFactory.Create(id); + return Builders.Filter.Eq(MongoConstants.MongoReservedKeyPropertyName, bsonValue); + } + + private FilterDefinition GetFilterByIds(IEnumerable ids) + { + // Use cached key serialization info but fall back to BsonValueFactory for dynamic mapper. + var bsonValues = this._keySerializationInfo?.SerializeValues(ids) ?? (BsonArray)BsonValueFactory.Create(ids); + return Builders.Filter.In(MongoConstants.MongoReservedKeyPropertyName, bsonValues); + } - private FilterDefinition GetFilterByIds(IEnumerable ids) - => Builders.Filter.In(document => document[MongoConstants.MongoReservedKeyPropertyName].AsString, ids); + private BsonSerializationInfo GetKeySerializationInfo() + { + var documentSerializer = BsonSerializer.LookupSerializer(); + if (documentSerializer is null) + { + throw new InvalidOperationException($"BsonSerializer not found for type '{typeof(TRecord)}'"); + } + + if (documentSerializer is not IBsonDocumentSerializer bsonDocumentSerializer) + { + throw new InvalidOperationException($"BsonSerializer for type '{typeof(TRecord)}' does not implement IBsonDocumentSerializer"); + } + + if (!bsonDocumentSerializer.TryGetMemberSerializationInfo(this._model.KeyProperty.ModelName, out var keySerializationInfo)) + { + throw new InvalidOperationException($"BsonSerializer for type '{typeof(TRecord)}' does not recognize key property {this._model.KeyProperty.ModelName}"); + } + + return keySerializationInfo; + } private async Task InternalCollectionExistsAsync(CancellationToken cancellationToken) { diff --git a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionCreateMapping.cs b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionCreateMapping.cs index 7fa1a47ece87..08ab4c859e7e 100644 --- a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionCreateMapping.cs +++ b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionCreateMapping.cs @@ -112,7 +112,7 @@ private static string GetIndexKind(string? indexKind, string vectorPropertyName) { IndexKind.Hnsw => "vector-hnsw", IndexKind.IvfFlat => "vector-ivf", - _ => throw new InvalidOperationException($"Index kind '{indexKind}' on {nameof(VectorStoreVectorProperty)} '{vectorPropertyName}' is not supported by the Azure CosmosDB for MongoDB VectorStore.") + _ => throw new NotSupportedException($"Index kind '{indexKind}' on {nameof(VectorStoreVectorProperty)} '{vectorPropertyName}' is not supported by the Azure CosmosDB for MongoDB VectorStore.") }; /// @@ -124,6 +124,6 @@ private static string GetDistanceFunction(string? distanceFunction, string vecto DistanceFunction.CosineDistance => "COS", DistanceFunction.DotProductSimilarity => "IP", DistanceFunction.EuclideanDistance => "L2", - _ => throw new InvalidOperationException($"Distance function '{distanceFunction}' for {nameof(VectorStoreVectorProperty)} '{vectorPropertyName}' is not supported by the Azure CosmosDB for MongoDB VectorStore.") + _ => throw new NotSupportedException($"Distance function '{distanceFunction}' for {nameof(VectorStoreVectorProperty)} '{vectorPropertyName}' is not supported by the Azure CosmosDB for MongoDB VectorStore.") }; } diff --git a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionSearchMapping.cs b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionSearchMapping.cs index ea40edd73618..8cd011218e09 100644 --- a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionSearchMapping.cs +++ b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoCollectionSearchMapping.cs @@ -50,7 +50,7 @@ internal static class CosmosMongoCollectionSearchMapping if (filterClause is EqualToFilterClause equalToFilterClause) { propertyName = equalToFilterClause.FieldName; - propertyValue = BsonValue.Create(equalToFilterClause.Value); + propertyValue = BsonValueFactory.Create(equalToFilterClause.Value); filterOperator = EqualOperator; } else diff --git a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoDB.csproj b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoDB.csproj index b1f5178aaaeb..52b98d55b40e 100644 --- a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoDB.csproj +++ b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoDB.csproj @@ -4,7 +4,7 @@ Microsoft.SemanticKernel.Connectors.CosmosMongoDB $(AssemblyName) - net10.0;net8.0;netstandard2.0;net462 + net10.0;net8.0;netstandard2.1;net472 true preview diff --git a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoFilterTranslator.cs b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoFilterTranslator.cs index 1b938139cea8..e2124150c502 100644 --- a/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoFilterTranslator.cs +++ b/dotnet/src/VectorData/CosmosMongoDB/CosmosMongoFilterTranslator.cs @@ -81,7 +81,7 @@ private BsonDocument GenerateEqualityComparison(PropertyModel property, object? // Short form of equality (instead of $eq) if (nodeType is ExpressionType.Equal) { - return new BsonDocument { [property.StorageName] = BsonValue.Create(value) }; + return new BsonDocument { [property.StorageName] = BsonValueFactory.Create(value) }; } var filterOperator = nodeType switch @@ -95,7 +95,7 @@ private BsonDocument GenerateEqualityComparison(PropertyModel property, object? _ => throw new UnreachableException() }; - return new BsonDocument { [property.StorageName] = new BsonDocument { [filterOperator] = BsonValue.Create(value) } }; + return new BsonDocument { [property.StorageName] = new BsonDocument { [filterOperator] = BsonValueFactory.Create(value) } }; } private BsonDocument TranslateAndOr(BinaryExpression andOr) @@ -284,7 +284,7 @@ BsonDocument ProcessInlineEnumerable(IEnumerable elements, Expression item) { [property.StorageName] = new BsonDocument { - ["$in"] = new BsonArray(from object? element in elements select BsonValue.Create(element)) + ["$in"] = new BsonArray(from object? element in elements select BsonValueFactory.Create(element)) } }; } diff --git a/dotnet/src/VectorData/MongoDB/MongoCollection.cs b/dotnet/src/VectorData/MongoDB/MongoCollection.cs index 565667bda0e3..c52d3c25bc6d 100644 --- a/dotnet/src/VectorData/MongoDB/MongoCollection.cs +++ b/dotnet/src/VectorData/MongoDB/MongoCollection.cs @@ -14,6 +14,7 @@ using Microsoft.Extensions.VectorData; using Microsoft.Extensions.VectorData.ProviderServices; using MongoDB.Bson; +using MongoDB.Bson.Serialization; using MongoDB.Driver; using MEVD = Microsoft.Extensions.VectorData; @@ -75,6 +76,9 @@ public class MongoCollection : VectorStoreCollectionNumber of nearest neighbors to use during the vector search. private readonly int? _numCandidates; + /// to use for serializing key values. + private readonly BsonSerializationInfo? _keySerializationInfo; + /// Types of keys permitted. private static readonly Type[] s_validKeyTypes = [typeof(string), typeof(Guid), typeof(ObjectId), typeof(int), typeof(long)]; @@ -135,6 +139,11 @@ internal MongoCollection(IMongoDatabase mongoDatabase, string name, Func @@ -676,10 +685,39 @@ private async IAsyncEnumerable> EnumerateAndMapSearc } private FilterDefinition GetFilterById(TKey id) - => Builders.Filter.Eq(MongoConstants.MongoReservedKeyPropertyName, id); + { + // Use cached key serialization info but fall back to BsonValueFactory for dynamic mapper. + var bsonValue = this._keySerializationInfo?.SerializeValue(id) ?? BsonValueFactory.Create(id); + return Builders.Filter.Eq(MongoConstants.MongoReservedKeyPropertyName, bsonValue); + } private FilterDefinition GetFilterByIds(IEnumerable ids) - => Builders.Filter.In(MongoConstants.MongoReservedKeyPropertyName, ids); + { + // Use cached key serialization info but fall back to BsonValueFactory for dynamic mapper. + var bsonValues = this._keySerializationInfo?.SerializeValues(ids) ?? (BsonArray)BsonValueFactory.Create(ids); + return Builders.Filter.In(MongoConstants.MongoReservedKeyPropertyName, bsonValues); + } + + private BsonSerializationInfo GetKeySerializationInfo() + { + var documentSerializer = BsonSerializer.LookupSerializer(); + if (documentSerializer is null) + { + throw new InvalidOperationException($"BsonSerializer not found for type '{typeof(TRecord)}'"); + } + + if (documentSerializer is not IBsonDocumentSerializer bsonDocumentSerializer) + { + throw new InvalidOperationException($"BsonSerializer for type '{typeof(TRecord)}' does not implement IBsonDocumentSerializer"); + } + + if (!bsonDocumentSerializer.TryGetMemberSerializationInfo(this._model.KeyProperty.ModelName, out var keySerializationInfo)) + { + throw new InvalidOperationException($"BsonSerializer for type '{typeof(TRecord)}' does not recognize key property {this._model.KeyProperty.ModelName}"); + } + + return keySerializationInfo; + } private async Task InternalCollectionExistsAsync(CancellationToken cancellationToken) { diff --git a/dotnet/src/VectorData/MongoDB/MongoCollectionSearchMapping.cs b/dotnet/src/VectorData/MongoDB/MongoCollectionSearchMapping.cs index 12ab41934177..7d50f1ee9688 100644 --- a/dotnet/src/VectorData/MongoDB/MongoCollectionSearchMapping.cs +++ b/dotnet/src/VectorData/MongoDB/MongoCollectionSearchMapping.cs @@ -46,7 +46,7 @@ internal static class MongoCollectionSearchMapping if (filterClause is EqualToFilterClause equalToFilterClause) { propertyName = equalToFilterClause.FieldName; - propertyValue = BsonValue.Create(equalToFilterClause.Value); + propertyValue = BsonValueFactory.Create(equalToFilterClause.Value); filterOperator = EqualOperator; } else diff --git a/dotnet/src/VectorData/MongoDB/MongoDB.csproj b/dotnet/src/VectorData/MongoDB/MongoDB.csproj index 602d1b66e4b3..fa6369f2e179 100644 --- a/dotnet/src/VectorData/MongoDB/MongoDB.csproj +++ b/dotnet/src/VectorData/MongoDB/MongoDB.csproj @@ -4,7 +4,7 @@ Microsoft.SemanticKernel.Connectors.MongoDB $(AssemblyName) - net10.0;net8.0;netstandard2.0;net462 + net10.0;net8.0;netstandard2.1;net472 true preview diff --git a/dotnet/src/VectorData/MongoDB/MongoFilterTranslator.cs b/dotnet/src/VectorData/MongoDB/MongoFilterTranslator.cs index f6c28457f768..ed05f18e8295 100644 --- a/dotnet/src/VectorData/MongoDB/MongoFilterTranslator.cs +++ b/dotnet/src/VectorData/MongoDB/MongoFilterTranslator.cs @@ -87,7 +87,7 @@ private BsonDocument GenerateEqualityComparison(PropertyModel property, object? // Short form of equality (instead of $eq) if (nodeType is ExpressionType.Equal) { - return new BsonDocument { [property.StorageName] = BsonValue.Create(value) }; + return new BsonDocument { [property.StorageName] = BsonValueFactory.Create(value) }; } var filterOperator = nodeType switch @@ -101,7 +101,7 @@ private BsonDocument GenerateEqualityComparison(PropertyModel property, object? _ => throw new UnreachableException() }; - return new BsonDocument { [property.StorageName] = new BsonDocument { [filterOperator] = BsonValue.Create(value) } }; + return new BsonDocument { [property.StorageName] = new BsonDocument { [filterOperator] = BsonValueFactory.Create(value) } }; } private BsonDocument TranslateAndOr(BinaryExpression andOr) @@ -288,7 +288,7 @@ BsonDocument ProcessInlineEnumerable(IEnumerable elements, Expression item) { [property.StorageName] = new BsonDocument { - ["$in"] = new BsonArray(from object? element in elements select BsonValue.Create(element)) + ["$in"] = new BsonArray(from object? element in elements select BsonValueFactory.Create(element)) } }; } diff --git a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoDistanceFunctionTests.cs b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoDistanceFunctionTests.cs index 7cf58f65da31..6a4409ae7b12 100644 --- a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoDistanceFunctionTests.cs +++ b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoDistanceFunctionTests.cs @@ -10,7 +10,7 @@ namespace CosmosMongoDB.ConformanceTests; public class CosmosMongoDistanceFunctionTests(CosmosMongoDistanceFunctionTests.Fixture fixture) : DistanceFunctionTests(fixture), IClassFixture { - public override Task CosineDistance() => Assert.ThrowsAsync(base.CosineDistance); + public override Task CosineSimilarity() => Assert.ThrowsAsync(base.CosineSimilarity); public override Task EuclideanSquaredDistance() => Assert.ThrowsAsync(base.EuclideanSquaredDistance); public override Task NegativeDotProductSimilarity() => Assert.ThrowsAsync(base.NegativeDotProductSimilarity); public override Task HammingDistance() => Assert.ThrowsAsync(base.HammingDistance); diff --git a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoIndexKindTests.cs b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoIndexKindTests.cs index 421005184060..af6c091c4dab 100644 --- a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoIndexKindTests.cs +++ b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/CosmosMongoIndexKindTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft. All rights reserved. using CosmosMongoDB.ConformanceTests.Support; +using Microsoft.Extensions.VectorData; using VectorData.ConformanceTests; using VectorData.ConformanceTests.Support; +using VectorData.ConformanceTests.Xunit; using Xunit; namespace CosmosMongoDB.ConformanceTests; @@ -10,6 +12,18 @@ namespace CosmosMongoDB.ConformanceTests; public class CosmosMongoIndexKindTests(CosmosMongoIndexKindTests.Fixture fixture) : IndexKindTests(fixture), IClassFixture { + // Note: Cosmos Mongo support HNSW, but only in a specific tier. + // [ConditionalFact] + // public virtual Task Hnsw() + // => this.Test(IndexKind.Hnsw); + + [ConditionalFact] + public virtual Task IvfFlat() + => this.Test(IndexKind.IvfFlat); + + // Cosmos Mongo does not support index-less searching + public override Task Flat() => Assert.ThrowsAsync(base.Flat); + public new class Fixture() : IndexKindTests.Fixture { public override TestStore TestStore => CosmosMongoTestStore.Instance; diff --git a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/Support/CosmosMongoTestStore.cs b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/Support/CosmosMongoTestStore.cs index 2db6e9188e7d..280b42387451 100644 --- a/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/Support/CosmosMongoTestStore.cs +++ b/dotnet/test/VectorData/CosmosMongoDB.ConformanceTests/Support/CosmosMongoTestStore.cs @@ -6,7 +6,9 @@ namespace CosmosMongoDB.ConformanceTests.Support; +#pragma warning disable CA1001 public sealed class CosmosMongoTestStore : TestStore +#pragma warning restore CA1001 { public static CosmosMongoTestStore Instance { get; } = new();