Add MongoDB Atlas support for TestContainers and Docker Compose
Implement service connection support for MongoDB Atlas using both TestContainers and Docker Compose. This change enables easier integration testing and local development with MongoDB Atlas. - Leverage TestContainers 1.20.2 which introduces MongoDBAtlasLocalContainer - Add TestContainers support using MongoDBAtlasLocalContainer - Implement Docker Compose configuration for MongoDB Atlas - Create connection details factories for both TestContainers and Docker Compose - Update dependency management for MongoDB Atlas integration - Add integration tests for both TestContainers and Docker Compose setups - Update documentation to include MongoDB Atlas support
This commit is contained in:
committed by
Mark Pollack
parent
e58341a447
commit
e1d9bfc616
@@ -34,6 +34,9 @@ The following service connection factories are provided in the `spring-ai-spring
|
||||
| `ChromaConnectionDetails`
|
||||
| Containers named `chromadb/chroma`, `ghcr.io/chroma-core/chroma`
|
||||
|
||||
| `MongoConnectionDetails`
|
||||
| Containers named `mongodb/mongodb-atlas-local`
|
||||
|
||||
| `OllamaConnectionDetails`
|
||||
| Containers named `ollama/ollama`
|
||||
|
||||
|
||||
@@ -37,6 +37,9 @@ The following service connection factories are provided in the `spring-ai-spring
|
||||
| `MilvusServiceClientConnectionDetails`
|
||||
| Containers of type `MilvusContainer`
|
||||
|
||||
| `MongoConnectionDetails`
|
||||
| Containers of type `MongoDBAtlasLocalContainer`
|
||||
|
||||
| `OllamaConnectionDetails`
|
||||
| Containers of type `OllamaContainer`
|
||||
|
||||
|
||||
@@ -123,6 +123,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-mongodb-atlas-store</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ai.docker.compose.service.connection.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.docker.compose.core.RunningService;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionDetailsFactory;
|
||||
import org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource;
|
||||
|
||||
/**
|
||||
* A {@link DockerComposeConnectionDetailsFactory} implementation that creates
|
||||
* {@link MongoConnectionDetails} for a MongoDB Atlas Local instance running in a Docker
|
||||
* container.
|
||||
*
|
||||
* <p>
|
||||
* This factory is designed to work with Docker Compose configurations that include a
|
||||
* MongoDB Atlas Local container. It provides the necessary connection details for Spring
|
||||
* Boot applications to connect to the MongoDB instance.
|
||||
*
|
||||
* <p>
|
||||
* The factory matches containers with the image name "mongodb/mongodb-atlas-local".
|
||||
*
|
||||
* <p>
|
||||
* Usage of this factory requires the presence of the Spring Boot Docker Compose support
|
||||
* and the MongoDB driver on the classpath.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @see DockerComposeConnectionDetailsFactory
|
||||
* @see MongoConnectionDetails
|
||||
* @see org.springframework.boot.docker.compose.service.connection.DockerComposeConnectionSource
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class MongoDbAtlasLocalDockerComposeConnectionDetailsFactory
|
||||
extends DockerComposeConnectionDetailsFactory<MongoConnectionDetails> {
|
||||
|
||||
private static final int MONGODB_PORT = 27017;
|
||||
|
||||
protected MongoDbAtlasLocalDockerComposeConnectionDetailsFactory() {
|
||||
super("mongodb/mongodb-atlas-local");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MongoConnectionDetails getDockerComposeConnectionDetails(DockerComposeConnectionSource source) {
|
||||
return new MongoDbAtlasLocalContainerConnectionDetails(source.getRunningService());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MongoConnectionDetails} backed by a {@code MongoDB Atlas}
|
||||
* {@link RunningService}.
|
||||
*/
|
||||
static class MongoDbAtlasLocalContainerConnectionDetails extends DockerComposeConnectionDetails
|
||||
implements MongoConnectionDetails {
|
||||
|
||||
private final String connectionString;
|
||||
|
||||
MongoDbAtlasLocalContainerConnectionDetails(RunningService service) {
|
||||
super(service);
|
||||
this.connectionString = String.format("mongodb://%s:%d/?directConnection=true", service.host(),
|
||||
service.ports().get(MONGODB_PORT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionString getConnectionString() {
|
||||
return new ConnectionString(this.connectionString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\
|
||||
org.springframework.ai.docker.compose.service.connection.chroma.ChromaDockerComposeConnectionDetailsFactory,\
|
||||
org.springframework.ai.docker.compose.service.connection.mongo.MongoDbAtlasLocalDockerComposeConnectionDetailsFactory,\
|
||||
org.springframework.ai.docker.compose.service.connection.ollama.OllamaDockerComposeConnectionDetailsFactory,\
|
||||
org.springframework.ai.docker.compose.service.connection.opensearch.OpenSearchDockerComposeConnectionDetailsFactory,\
|
||||
org.springframework.ai.docker.compose.service.connection.qdrant.QdrantDockerComposeConnectionDetailsFactory,\
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.springframework.ai.docker.compose.service.connection.mongo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.docker.compose.service.connection.test.AbstractDockerComposeIntegrationTests;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryTests extends AbstractDockerComposeIntegrationTests {
|
||||
|
||||
protected MongoDbAtlasLocalDockerComposeConnectionDetailsFactoryTests() {
|
||||
super("mongo-compose.yaml", DockerImageName.parse("mongodb/mongodb-atlas-local"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void runCreatesConnectionDetails() {
|
||||
MongoConnectionDetails connectionDetails = run(MongoConnectionDetails.class);
|
||||
assertThat(connectionDetails.getConnectionString()).isNotNull();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
services:
|
||||
mongo:
|
||||
image: '{imageName}'
|
||||
ports:
|
||||
- '27017'
|
||||
@@ -131,6 +131,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-mongodb-atlas-store</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- test dependencies -->
|
||||
|
||||
<dependency>
|
||||
@@ -218,6 +225,13 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>mongodb</artifactId>
|
||||
<version>1.20.2</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.testcontainers</groupId>
|
||||
<artifactId>ollama</artifactId>
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ai.testcontainers.service.connection.mongo;
|
||||
|
||||
import com.mongodb.ConnectionString;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoConnectionDetails;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionDetailsFactory;
|
||||
import org.springframework.boot.testcontainers.service.connection.ContainerConnectionSource;
|
||||
import org.testcontainers.mongodb.MongoDBAtlasLocalContainer;
|
||||
|
||||
/**
|
||||
* A {@link ContainerConnectionDetailsFactory} implementation that provides
|
||||
* {@link MongoConnectionDetails} for a {@link MongoDBAtlasLocalContainer}.
|
||||
* <p>
|
||||
* This factory is used in conjunction with Spring Boot's auto-configuration for
|
||||
* Testcontainers to automatically create and configure a connection to a MongoDB instance
|
||||
* running in a testcontainer.
|
||||
* <p>
|
||||
* It generates {@link MongoConnectionDetails} based on the connection information
|
||||
* provided by the {@link MongoDBAtlasLocalContainer}, allowing integration of MongoDB
|
||||
* testcontainers in Spring Boot applications.
|
||||
*
|
||||
* @author Eddú Meléndez
|
||||
* @since 1.0.0
|
||||
* @see ContainerConnectionDetailsFactory
|
||||
* @see MongoConnectionDetails
|
||||
* @see MongoDBAtlasLocalContainer
|
||||
*/
|
||||
class MongoDbAtlasLocalContainerConnectionDetailsFactory
|
||||
extends ContainerConnectionDetailsFactory<MongoDBAtlasLocalContainer, MongoConnectionDetails> {
|
||||
|
||||
@Override
|
||||
protected MongoConnectionDetails getContainerConnectionDetails(
|
||||
ContainerConnectionSource<MongoDBAtlasLocalContainer> source) {
|
||||
return new MongoDbAtlasLocalContainerConnectionDetails(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link MongoConnectionDetails} backed by a {@link ContainerConnectionSource}.
|
||||
*/
|
||||
private static final class MongoDbAtlasLocalContainerConnectionDetails
|
||||
extends ContainerConnectionDetails<MongoDBAtlasLocalContainer> implements MongoConnectionDetails {
|
||||
|
||||
private MongoDbAtlasLocalContainerConnectionDetails(
|
||||
ContainerConnectionSource<MongoDBAtlasLocalContainer> source) {
|
||||
super(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConnectionString getConnectionString() {
|
||||
return new ConnectionString(getContainer().getConnectionString());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
org.springframework.boot.autoconfigure.service.connection.ConnectionDetailsFactory=\
|
||||
org.springframework.ai.testcontainers.service.connection.chroma.ChromaContainerConnectionDetailsFactory,\
|
||||
org.springframework.ai.testcontainers.service.connection.milvus.MilvusContainerConnectionDetailsFactory,\
|
||||
org.springframework.ai.testcontainers.service.connection.mongo.MongoDbAtlasLocalContainerConnectionDetailsFactory,\
|
||||
org.springframework.ai.testcontainers.service.connection.ollama.OllamaContainerConnectionDetailsFactory,\
|
||||
org.springframework.ai.testcontainers.service.connection.opensearch.OpenSearchContainerConnectionDetailsFactory,\
|
||||
org.springframework.ai.testcontainers.service.connection.qdrant.QdrantContainerConnectionDetailsFactory,\
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2023 - 2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ai.testcontainers.service.connection.mongo;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.autoconfigure.vectorstore.mongo.MongoDBAtlasVectorStoreAutoConfiguration;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.openai.OpenAiEmbeddingModel;
|
||||
import org.springframework.ai.openai.api.OpenAiApi;
|
||||
import org.springframework.ai.vectorstore.SearchRequest;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
|
||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
import org.testcontainers.mongodb.MongoDBAtlasLocalContainer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@SpringJUnitConfig
|
||||
@Testcontainers
|
||||
@TestPropertySource(properties = { "spring.data.mongodb.database=simpleaidb",
|
||||
"spring.ai.vectorstore.mongodb.initialize-schema=true",
|
||||
"spring.ai.vectorstore.mongodb.collection-name=test_collection",
|
||||
"spring.ai.vectorstore.mongodb.index-name=text_index" })
|
||||
class MongoDbAtlasLocalContainerConnectionDetailsFactoryTest {
|
||||
|
||||
@Container
|
||||
@ServiceConnection
|
||||
private static MongoDBAtlasLocalContainer container = new MongoDBAtlasLocalContainer(
|
||||
"mongodb/mongodb-atlas-local:7.0.9");
|
||||
|
||||
@Autowired
|
||||
private VectorStore vectorStore;
|
||||
|
||||
@Test
|
||||
public void addAndSearch() throws InterruptedException {
|
||||
List<Document> documents = List.of(
|
||||
new Document(
|
||||
"Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!",
|
||||
Collections.singletonMap("meta1", "meta1")),
|
||||
new Document("Hello World Hello World Hello World Hello World Hello World Hello World Hello World"),
|
||||
new Document(
|
||||
"Great Depression Great Depression Great Depression Great Depression Great Depression Great Depression",
|
||||
Collections.singletonMap("meta2", "meta2")));
|
||||
|
||||
vectorStore.add(documents);
|
||||
Thread.sleep(5000); // Await a second for the document to be indexed
|
||||
|
||||
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Great").withTopK(1));
|
||||
|
||||
assertThat(results).hasSize(1);
|
||||
Document resultDoc = results.get(0);
|
||||
assertThat(resultDoc.getId()).isEqualTo(documents.get(2).getId());
|
||||
assertThat(resultDoc.getContent()).isEqualTo(
|
||||
"Great Depression Great Depression Great Depression Great Depression Great Depression Great Depression");
|
||||
assertThat(resultDoc.getMetadata()).containsEntry("meta2", "meta2");
|
||||
|
||||
// Remove all documents from the store
|
||||
vectorStore.delete(documents.stream().map(Document::getId).collect(Collectors.toList()));
|
||||
|
||||
List<Document> results2 = vectorStore.similaritySearch(SearchRequest.query("Great").withTopK(1));
|
||||
assertThat(results2).isEmpty();
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ImportAutoConfiguration({ MongoAutoConfiguration.class, MongoDataAutoConfiguration.class,
|
||||
MongoDBAtlasVectorStoreAutoConfiguration.class })
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user