Skip to content

Commit

Permalink
Start adding support for Mongodb Atlas database (#311)
Browse files Browse the repository at this point in the history
It works with cluster from M10 upward (does not work still with shared cluster).

This code contains also the necessary code to automatically create the
Vector Indexes needed to execute queries.

---------

Co-authored-by: Devis Lucato <devis@microsoft.com>
  • Loading branch information
alkampfergit and dluc committed Mar 15, 2024
1 parent ae03364 commit 87e12b1
Show file tree
Hide file tree
Showing 31 changed files with 2,062 additions and 157 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Expand Up @@ -24,6 +24,7 @@
<PackageVersion Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.Logging.TraceSource" Version="8.0.0" />
<PackageVersion Include="MongoDB.Driver.GridFS" Version="2.23.1" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="PdfPig" Version="0.1.8" />
<PackageVersion Include="Pgvector" Version="0.2.0" />
Expand Down
14 changes: 14 additions & 0 deletions KernelMemory.sln
Expand Up @@ -115,6 +115,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{CA49F1A1
tools\run-redis.sh = tools\run-redis.sh
tools\run-mssql.sh = tools\run-mssql.sh
tools\run-elasticsearch.sh = tools\run-elasticsearch.sh
tools\run-mongodb-atlas.sh = tools\run-mongodb-atlas.sh
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Abstractions", "service\Abstractions\Abstractions.csproj", "{8A9FA587-7EBA-4D43-BE47-38D798B1C74C}"
Expand Down Expand Up @@ -225,6 +226,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SQLServer.FunctionalTests",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elasticsearch.FunctionalTests", "extensions\Elasticsearch\Elasticsearch.FunctionalTests\Elasticsearch.FunctionalTests.csproj", "{C089DE42-9D39-4633-AC0C-5034BA0B98E1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDbAtlas", "extensions\MongoDbAtlas\MongoDbAtlas\MongoDbAtlas.csproj", "{4A539320-EB49-4688-82E1-4FA0ADBB3810}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MongoDbAtlas.FunctionalTests", "extensions\MongoDbAtlas\MongoDbAtlas.FunctionalTests\MongoDbAtlas.FunctionalTests.csproj", "{8A602227-B291-4F1B-ACB8-237F49501B6A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -291,6 +296,8 @@ Global
{47CEEA8F-7858-4635-B902-4C704CF55EA0} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
{48E79819-1E9E-4075-90DA-BAEC761C89B2} = {B8976338-7CDC-47AE-8502-C2FBAFBEBD68}
{A5CE3CB2-F746-4832-B239-1A3226F9BD48} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
{4A539320-EB49-4688-82E1-4FA0ADBB3810} = {155DA079-E267-49AF-973A-D1D44681970F}
{8A602227-B291-4F1B-ACB8-237F49501B6A} = {3C17F42B-CFC8-4900-8CFB-88936311E919}
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A9FA587-7EBA-4D43-BE47-38D798B1C74C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
Expand Down Expand Up @@ -472,5 +479,12 @@ Global
{A5CE3CB2-F746-4832-B239-1A3226F9BD48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A5CE3CB2-F746-4832-B239-1A3226F9BD48}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A5CE3CB2-F746-4832-B239-1A3226F9BD48}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A539320-EB49-4688-82E1-4FA0ADBB3810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A539320-EB49-4688-82E1-4FA0ADBB3810}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A539320-EB49-4688-82E1-4FA0ADBB3810}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A539320-EB49-4688-82E1-4FA0ADBB3810}.Release|Any CPU.Build.0 = Release|Any CPU
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A602227-B291-4F1B-ACB8-237F49501B6A}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
EndGlobal
5 changes: 5 additions & 0 deletions extensions/MongoDbAtlas/Docker/Local6/Dockerfile
@@ -0,0 +1,5 @@
FROM mongodb/atlas

COPY startatlas6.sh /usr/startatlas6.sh

CMD /usr/startatlas6.sh
29 changes: 29 additions & 0 deletions extensions/MongoDbAtlas/Docker/Local6/startatlas6.sh
@@ -0,0 +1,29 @@
#!/bin/bash

# The name of the deployment to search for

# Run the command and save the output
OUTPUT=$(atlas deployments list)

echo "Output: "
echo $OUTPUT

# count line of output
LINE=$(echo "$OUTPUT" | wc -l)
echo "Count line of output: $LINE "

if [ $LINE -lt 2 ]; then
echo "No deployment found. create a new one"
atlas deployments setup local --mdbVersion 6.0 --bindIpAll --username root --password root --type local --force
else
echo "Deployment found. Start it"
atlas deployments start local
fi

function pause_atlas() {
atlas deployments pause local
}
# This will call the 'on_exit' function when the container exits
trap pause_atlas EXIT

tail -f /dev/null
5 changes: 5 additions & 0 deletions extensions/MongoDbAtlas/Docker/Local7/Dockerfile
@@ -0,0 +1,5 @@
FROM mongodb/atlas

COPY startatlas.sh /usr/startatlas.sh

CMD /usr/startatlas.sh
29 changes: 29 additions & 0 deletions extensions/MongoDbAtlas/Docker/Local7/startatlas.sh
@@ -0,0 +1,29 @@
#!/bin/bash

# The name of the deployment to search for

# Run the command and save the output
OUTPUT=$(atlas deployments list)

echo "Output: "
echo $OUTPUT

# count line of output
LINE=$(echo "$OUTPUT" | wc -l)
echo "Count line of output: $LINE "

if [ $LINE -lt 2 ]; then
echo "No deployment found. create a new one"
atlas deployments setup local --mdbVersion 7.0 --bindIpAll --username root --password root --type local --force
else
echo "Deployment found. Start it"
atlas deployments start local
fi

function pause_atlas() {
atlas deployments pause local
}
# This will call the 'on_exit' function when the container exits
trap pause_atlas EXIT

tail -f /dev/null
143 changes: 143 additions & 0 deletions extensions/MongoDbAtlas/MongoDbAtlas.FunctionalTests/DefaultTests.cs
@@ -0,0 +1,143 @@
// Copyright (c) Microsoft. All rights reserved.

using FunctionalTests.DefaultTestCases;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.MongoDbAtlas;
using Microsoft.TestHelpers;

namespace MongoDbAtlas.FunctionalTests;

public class DefaultTestsSingleCollection : DefaultTests
{
public DefaultTestsSingleCollection(IConfiguration cfg, ITestOutputHelper output)
: base(cfg, output, multiCollection: false)
{
}
}

public class DefaultTestsMultipleCollections : DefaultTests
{
public DefaultTestsMultipleCollections(IConfiguration cfg, ITestOutputHelper output)
: base(cfg, output, multiCollection: true)
{
}
}

public abstract class DefaultTests : BaseFunctionalTestCase
{
private readonly MemoryServerless _memory;

protected DefaultTests(IConfiguration cfg, ITestOutputHelper output, bool multiCollection) : base(cfg, output)
{
Assert.False(string.IsNullOrEmpty(this.OpenAiConfig.APIKey), "OpenAI API Key is empty");

if (multiCollection)
{
// this._config = this.MongoDbAtlasConfig;
this.MongoDbAtlasConfig
.WithSingleCollectionForVectorSearch(false)
// Need to wait for atlas to grab the data from the collection and index.
.WithAfterIndexCallback(async () => await Task.Delay(2000));

this.MongoDbAtlasConfig.DatabaseName += "multicoll";
}
else
{
this.MongoDbAtlasConfig
.WithSingleCollectionForVectorSearch(true)
//Need to wait for atlas to grab the data from the collection and index.
.WithAfterIndexCallback(async () => await Task.Delay(2000));
}

// Clear all content in any collection before running the test.
var ash = new MongoDbAtlasSearchHelper(this.MongoDbAtlasConfig.ConnectionString, this.MongoDbAtlasConfig.DatabaseName);
if (this.MongoDbAtlasConfig.UseSingleCollectionForVectorSearch)
{
//delete everything for every collection
ash.DropAllDocumentsFromCollectionsAsync().Wait();
}
else
{
//drop the entire db to be sure we can start with a clean test.
ash.DropDatabaseAsync().Wait();
}

this._memory = new KernelMemoryBuilder()
.WithSearchClientConfig(new SearchClientConfig { EmptyAnswer = NotFound })
.WithOpenAI(this.OpenAiConfig)
// .WithAzureOpenAITextGeneration(this.AzureOpenAITextConfiguration)
// .WithAzureOpenAITextEmbeddingGeneration(this.AzureOpenAIEmbeddingConfiguration)
.WithMongoDbAtlasMemoryDb(this.MongoDbAtlasConfig)
.Build<MemoryServerless>();
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItSupportsASingleFilter()
{
await FilteringTest.ItSupportsASingleFilter(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItSupportsMultipleFilters()
{
await FilteringTest.ItSupportsMultipleFilters(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItIgnoresEmptyFilters()
{
await FilteringTest.ItIgnoresEmptyFilters(this._memory, this.Log, true);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItListsIndexes()
{
await IndexListTest.ItListsIndexes(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItNormalizesIndexNames()
{
await IndexListTest.ItNormalizesIndexNames(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItUsesDefaultIndexName()
{
await IndexListTest.ItUsesDefaultIndexName(this._memory, this.Log, "default4tests");
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItDeletesIndexes()
{
await IndexDeletionTest.ItDeletesIndexes(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItHandlesMissingIndexesConsistently()
{
await MissingIndexTest.ItHandlesMissingIndexesConsistently(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItUploadsPDFDocsAndDeletes()
{
await DocumentUploadTest.ItUploadsPDFDocsAndDeletes(this._memory, this.Log);
}

[Fact]
[Trait("Category", "MongoDbAtlas")]
public async Task ItSupportsTags()
{
await DocumentUploadTest.ItSupportsTags(this._memory, this.Log);
}
}
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<RollForward>LatestMajor</RollForward>
<IsTestProject>true</IsTestProject>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
<NoWarn>$(NoWarn);CA2007;CA1303;IDE0005;IDE1006;IDE0130;CA1711</NoWarn>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\service\tests\Core.FunctionalTests\Core.FunctionalTests.csproj" />
<ProjectReference Include="..\..\..\service\tests\TestHelpers\TestHelpers.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.abstractions" />
<PackageReference Include="Xunit.DependencyInjection" />
<PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Content Update="file1-NASA-news.pdf">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
21 changes: 21 additions & 0 deletions extensions/MongoDbAtlas/MongoDbAtlas.FunctionalTests/Startup.cs
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.

/* IMPORTANT: the Startup class must be at the root of the namespace and
* the namespace must match exactly (required by Xunit.DependencyInjection) */

namespace MongoDbAtlas.FunctionalTests;

public class Startup
{
public void ConfigureHost(IHostBuilder hostBuilder)
{
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.development.json", optional: true)
.AddUserSecrets<Startup>()
.AddEnvironmentVariables()
.Build();

hostBuilder.ConfigureHostConfiguration(builder => builder.AddConfiguration(config));
}
}

0 comments on commit 87e12b1

Please sign in to comment.