From 0c7ade2bf9fdbcd814b934354b94249ef40396d9 Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Fri, 13 Dec 2024 19:17:50 -0500 Subject: [PATCH] Qdrant vector store builder refactoring --- .../QdrantVectorStoreAutoConfiguration.java | 11 +- .../vectorstore/qdrant/QdrantVectorStore.java | 125 ++++++++++++++++-- .../qdrant/QdrantVectorStoreIT.java | 6 +- .../QdrantVectorStoreObservationIT.java | 10 +- 4 files changed, 135 insertions(+), 17 deletions(-) diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfiguration.java index 28249bc9721..790b53c3aef 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/qdrant/QdrantVectorStoreAutoConfiguration.java @@ -77,9 +77,14 @@ public QdrantVectorStore vectorStore(EmbeddingModel embeddingModel, QdrantVector QdrantClient qdrantClient, ObjectProvider observationRegistry, ObjectProvider customObservationConvention, BatchingStrategy batchingStrategy) { - return new QdrantVectorStore(qdrantClient, properties.getCollectionName(), embeddingModel, - properties.isInitializeSchema(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP), - customObservationConvention.getIfAvailable(() -> null), batchingStrategy); + return QdrantVectorStore.builder(qdrantClient) + .collectionName(properties.getCollectionName()) + .embeddingModel(embeddingModel) + .initializeSchema(properties.isInitializeSchema()) + .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)) + .customObservationConvention(customObservationConvention.getIfAvailable(() -> null)) + .batchingStrategy(batchingStrategy) + .build(); } static class PropertiesQdrantConnectionDetails implements QdrantConnectionDetails { diff --git a/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java index a5d5ec7484e..48444deb590 100644 --- a/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java +++ b/vector-stores/spring-ai-qdrant-store/src/main/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStore.java @@ -42,6 +42,7 @@ import org.springframework.ai.embedding.TokenCountBatchingStrategy; import org.springframework.ai.model.EmbeddingUtils; import org.springframework.ai.observation.conventions.VectorStoreProvider; +import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder; import org.springframework.ai.vectorstore.SearchRequest; import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore; import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext; @@ -67,8 +68,6 @@ public class QdrantVectorStore extends AbstractObservationVectorStore implements private static final String CONTENT_FIELD_NAME = "doc_content"; - private final EmbeddingModel embeddingModel; - private final QdrantClient qdrantClient; private final String collectionName; @@ -85,7 +84,9 @@ public class QdrantVectorStore extends AbstractObservationVectorStore implements * @param collectionName The name of the collection to use in Qdrant. * @param embeddingModel The client for embedding operations. * @param initializeSchema A boolean indicating whether to initialize the schema. + * @deprecated Use {@link #builder(QdrantClient)} */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") public QdrantVectorStore(QdrantClient qdrantClient, String collectionName, EmbeddingModel embeddingModel, boolean initializeSchema) { this(qdrantClient, collectionName, embeddingModel, initializeSchema, ObservationRegistry.NOOP, null, @@ -100,22 +101,48 @@ public QdrantVectorStore(QdrantClient qdrantClient, String collectionName, Embed * @param initializeSchema A boolean indicating whether to initialize the schema. * @param observationRegistry The observation registry to use. * @param customObservationConvention The custom search observation convention to use. + * @deprecated Use {@link #builder(QdrantClient)} */ + @Deprecated(forRemoval = true, since = "1.0.0-M5") public QdrantVectorStore(QdrantClient qdrantClient, String collectionName, EmbeddingModel embeddingModel, boolean initializeSchema, ObservationRegistry observationRegistry, VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) { - super(observationRegistry, customObservationConvention); + this(builder(qdrantClient).embeddingModel(embeddingModel) + .collectionName(collectionName) + .initializeSchema(initializeSchema) + .observationRegistry(observationRegistry) + .customObservationConvention(customObservationConvention) + .batchingStrategy(batchingStrategy)); + } - Assert.notNull(qdrantClient, "QdrantClient must not be null"); - Assert.notNull(collectionName, "collectionName must not be null"); - Assert.notNull(embeddingModel, "EmbeddingModel must not be null"); + /** + * Protected constructor for creating a QdrantVectorStore instance using the builder + * pattern. + * @param builder the {@link QdrantBuilder} containing all configuration settings + * @throws IllegalArgumentException if qdrant client is missing + * @see QdrantBuilder + * @since 1.0.0 + */ + protected QdrantVectorStore(QdrantBuilder builder) { + super(builder); + + Assert.notNull(builder.qdrantClient, "QdrantClient must not be null"); + + this.qdrantClient = builder.qdrantClient; + this.collectionName = builder.collectionName; + this.initializeSchema = builder.initializeSchema; + this.batchingStrategy = builder.batchingStrategy; + } - this.initializeSchema = initializeSchema; - this.embeddingModel = embeddingModel; - this.collectionName = collectionName; - this.qdrantClient = qdrantClient; - this.batchingStrategy = batchingStrategy; + /** + * Creates a new QdrantBuilder instance. This is the recommended way to instantiate a + * QdrantVectorStore. + * @param qdrantClient the client for interfacing with Qdrant + * @return a new QdrantBuilder instance + */ + public static QdrantBuilder builder(QdrantClient qdrantClient) { + return new QdrantBuilder(qdrantClient); } /** @@ -272,4 +299,80 @@ public VectorStoreObservationContext.Builder createObservationContextBuilder(Str } + /** + * Builder for creating instances of {@link QdrantVectorStore}. This builder provides + * a fluent API for configuring all aspects of the vector store. + * + * @since 1.0.0 + */ + public static final class QdrantBuilder extends AbstractVectorStoreBuilder { + + private final QdrantClient qdrantClient; + + private String collectionName = DEFAULT_COLLECTION_NAME; + + private boolean initializeSchema = false; + + private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy(); + + /** + * Creates a new builder instance with the required QdrantClient and + * EmbeddingModel. + * @param qdrantClient the client for Qdrant operations + * @throws IllegalArgumentException if qdrantClient is null + */ + QdrantBuilder(QdrantClient qdrantClient) { + Assert.notNull(qdrantClient, "QdrantClient must not be null"); + this.qdrantClient = qdrantClient; + } + + /** + * Configures the Qdrant collection name. + * @param collectionName the name of the collection to use (defaults to + * {@value DEFAULT_COLLECTION_NAME}) + * @return this builder instance + * @throws IllegalArgumentException if collectionName is null or empty + */ + public QdrantBuilder collectionName(String collectionName) { + Assert.hasText(collectionName, "collectionName must not be empty"); + this.collectionName = collectionName; + return this; + } + + /** + * Configures whether to initialize the collection schema. + * @param initializeSchema true to initialize schema automatically + * @return this builder instance + */ + public QdrantBuilder initializeSchema(boolean initializeSchema) { + this.initializeSchema = initializeSchema; + return this; + } + + /** + * Configures the strategy for batching operations. + * @param batchingStrategy the batching strategy to use + * @return this builder instance + * @throws IllegalArgumentException if batchingStrategy is null + */ + public QdrantBuilder batchingStrategy(BatchingStrategy batchingStrategy) { + Assert.notNull(batchingStrategy, "BatchingStrategy must not be null"); + this.batchingStrategy = batchingStrategy; + return this; + } + + /** + * Builds and returns a new QdrantVectorStore instance with the configured + * settings. + * @return a new QdrantVectorStore instance + * @throws IllegalStateException if the builder configuration is invalid + */ + @Override + public QdrantVectorStore build() { + validate(); + return new QdrantVectorStore(this); + } + + } + } diff --git a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java index 0cb14fde45d..282fb5577bf 100644 --- a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java +++ b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreIT.java @@ -254,7 +254,11 @@ public QdrantClient qdrantClient() { @Bean public VectorStore qdrantVectorStore(EmbeddingModel embeddingModel, QdrantClient qdrantClient) { - return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingModel, true); + return QdrantVectorStore.builder(qdrantClient) + .collectionName(COLLECTION_NAME) + .embeddingModel(embeddingModel) + .initializeSchema(true) + .build(); } @Bean diff --git a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java index 42031c3c5b2..f54a2b480a0 100644 --- a/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-qdrant-store/src/test/java/org/springframework/ai/vectorstore/qdrant/QdrantVectorStoreObservationIT.java @@ -195,8 +195,14 @@ public QdrantClient qdrantClient() { @Bean public VectorStore qdrantVectorStore(EmbeddingModel embeddingModel, QdrantClient qdrantClient, ObservationRegistry observationRegistry) { - return new QdrantVectorStore(qdrantClient, COLLECTION_NAME, embeddingModel, true, observationRegistry, null, - new TokenCountBatchingStrategy()); + return QdrantVectorStore.builder(qdrantClient) + .collectionName(COLLECTION_NAME) + .embeddingModel(embeddingModel) + .initializeSchema(true) + .observationRegistry(observationRegistry) + .customObservationConvention(null) + .batchingStrategy(new TokenCountBatchingStrategy()) + .build(); } @Bean