Add builder pattern to QdrantVectorStore

Introduces a builder pattern for configuring QdrantVectorStore instances to
provide a more flexible and type-safe way to create and configure vector stores.
This change:

- Makes configuration more intuitive through fluent builder methods
- Improves validation by enforcing required parameters at compile time
- Deprecates old constructors in favor of the builder pattern
- Adds comprehensive builder tests to ensure reliability
- Updates reference documentation with builder usage examples
- Maintains backward compatibility while providing a clear migration path

The builder pattern simplifies QdrantVectorStore configuration by providing
clear method names, proper validation, and better IDE support through method
chaining. This makes the API more user-friendly and helps prevent configuration
errors at compile time rather than runtime.
This commit is contained in:
Soby Chacko
2024-12-13 19:17:50 -05:00
committed by Mark Pollack
parent 19e61bf71a
commit 48bcbd1555
6 changed files with 413 additions and 135 deletions

View File

@@ -2,27 +2,26 @@
This section walks you through setting up the Qdrant `VectorStore` to store document embeddings and perform similarity searches.
link:https://www.qdrant.tech/[Qdrant] is an open-source, high-performance vector search engine/database.
link:https://www.qdrant.tech/[Qdrant] is an open-source, high-performance vector search engine/database. It uses HNSW (Hierarchical Navigable Small World) algorithm for efficient k-NN search operations and provides advanced filtering capabilities for metadata-based queries.
== Prerequisites
* Qdrant Instance: Set up a Qdrant instance by following the link:https://qdrant.tech/documentation/guides/installation/[installation instructions] in the Qdrant documentation.
* If required, an API key for the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] to generate the embeddings stored by the `QdrantVectorStore`.
To set up `QdrantVectorStore`, you'll need the following information from your Qdrant instance: `Host`, `GRPC Port`, `Collection Name`, and `API Key` (if required).
NOTE: It is recommended that the Qdrant collection is link:https://qdrant.tech/documentation/concepts/collections/#create-a-collection[created] in advance with the appropriate dimensions and configurations.
If the collection is not created, the `QdrantVectorStore` will attempt to create one using the `Cosine` similarity and the dimension of the configured `EmbeddingModel`.
== Auto-configuration
Then add the Qdrant boot starter dependency to your project:
Spring AI provides Spring Boot auto-configuration for the Qdrant Vector Store.
To enable it, add the following dependency to your project's Maven `pom.xml` file:
[source,xml]
----
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store-spring-boot-starter</artifactId>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store-spring-boot-starter</artifactId>
</dependency>
----
@@ -35,53 +34,19 @@ dependencies {
}
----
The vector store implementation can initialize the requisite schema for you, but you must opt-in by specifying the `initializeSchema` boolean in the appropriate constructor or by setting `...initialize-schema=true` in the `application.properties` file.
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
Please have a look at the list of xref:#qdrant-vectorstore-properties[configuration parameters] for the vector store to learn about the default values and configuration options.
TIP: Refer to the xref:getting-started.adoc#repositories[Repositories] section to add Milestone and/or Snapshot Repositories to your build file.
The vector store implementation can initialize the requisite schema for you, but you must opt-in by specifying the `initializeSchema` boolean in the builder or by setting `...initialize-schema=true` in the `application.properties` file.
NOTE: this is a breaking change! In earlier versions of Spring AI, this schema initialization happened by default.
Additionally, you will need a configured `EmbeddingModel` bean. Refer to the xref:api/embeddings.adoc#available-implementations[EmbeddingModel] section for more information.
The Vector Store, also requires an `EmbeddingModel` instance to calculate embeddings for the documents.
You can pick one of the available xref:api/embeddings.adoc#available-implementations[EmbeddingModel Implementations].
For example to use the xref:api/embeddings/openai-embeddings.adoc[OpenAI EmbeddingModel] add the following dependency to your project:
[source,xml]
----
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
----
or to your Gradle `build.gradle` build file.
[source,groovy]
----
dependencies {
implementation 'org.springframework.ai:spring-ai-openai-spring-boot-starter'
}
----
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
Refer to the xref:getting-started.adoc#repositories[Repositories] section to add Milestone and/or Snapshot Repositories to your build file.
To connect to Qdrant and use the `QdrantVectorStore`, you need to provide access details for your instance.
A simple configuration can either be provided via Spring Boot's _application.properties_,
[source,properties]
----
spring.ai.vectorstore.qdrant.host=<host of your qdrant instance>
spring.ai.vectorstore.qdrant.port=<the GRPC port of your qdrant instance>
spring.ai.vectorstore.qdrant.api-key=<your api key>
spring.ai.vectorstore.qdrant.collection-name=<The name of the collection to use in Qdrant>
# API key if needed, e.g. OpenAI
spring.ai.openai.api.key=<api-key>
----
TIP: Check the list of xref:#qdrant-vectorstore-properties[configuration parameters] to learn about the default values and configuration options.
Now you can Auto-wire the Qdrant Vector Store in your application and use it
Now you can auto-wire the `QdrantVectorStore` as a vector store in your application.
[source,java]
----
@@ -89,7 +54,7 @@ Now you can Auto-wire the Qdrant Vector Store in your application and use it
// ...
List <Document> documents = List.of(
List<Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
@@ -98,29 +63,109 @@ List <Document> documents = List.of(
vectorStore.add(documents);
// Retrieve documents similar to a query
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
List<Document> results = vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
----
[[qdrant-vectorstore-properties]]
=== Configuration properties
=== Configuration Properties
You can use the following properties in your Spring Boot configuration to customize the Qdrant vector store.
To connect to Qdrant and use the `QdrantVectorStore`, you need to provide access details for your instance.
A simple configuration can be provided via Spring Boot's `application.yml`:
[cols="3,5,1",stripes=even]
[source,yaml]
----
spring:
ai:
vectorstore:
qdrant:
host: <qdrant host>
port: <qdrant grpc port>
api-key: <qdrant api key>
collection-name: <collection name>
use-tls: false
initialize-schema: true
batching-strategy: TOKEN_COUNT # Optional: Controls how documents are batched for embedding
----
Properties starting with `spring.ai.vectorstore.qdrant.*` are used to configure the `QdrantVectorStore`:
[cols="2,5,1",stripes=even]
|===
|Property| Description | Default value
|Property | Description | Default Value
|`spring.ai.vectorstore.qdrant.host`| The host of the Qdrant server. | localhost
|`spring.ai.vectorstore.qdrant.port`| The gRPC port of the Qdrant server. | 6334
|`spring.ai.vectorstore.qdrant.api-key`| The API key to use for authentication with the Qdrant server. | -
|`spring.ai.vectorstore.qdrant.collection-name`| The name of the collection to use in Qdrant. | -
|`spring.ai.vectorstore.qdrant.use-tls`| Whether to use TLS(HTTPS). | false
|`spring.ai.vectorstore.qdrant.initialize-schema`| Whether to initialize the backend schema or not | false
|`spring.ai.vectorstore.qdrant.host`| The host of the Qdrant server | `localhost`
|`spring.ai.vectorstore.qdrant.port`| The gRPC port of the Qdrant server | `6334`
|`spring.ai.vectorstore.qdrant.api-key`| The API key to use for authentication | -
|`spring.ai.vectorstore.qdrant.collection-name`| The name of the collection to use | `vector_store`
|`spring.ai.vectorstore.qdrant.use-tls`| Whether to use TLS(HTTPS) | `false`
|`spring.ai.vectorstore.qdrant.initialize-schema`| Whether to initialize the schema | `false`
|`spring.ai.vectorstore.qdrant.batching-strategy`| Strategy for batching documents when calculating embeddings. Options are `TOKEN_COUNT` or `FIXED_SIZE` | `TOKEN_COUNT`
|===
== Metadata filtering
== Manual Configuration
You can leverage the generic, portable link:https://docs.spring.io/spring-ai/reference/api/vectordbs.html#_metadata_filters[metadata filters] with the Qdrant vector store.
Instead of using the Spring Boot auto-configuration, you can manually configure the Qdrant vector store. For this you need to add the `spring-ai-qdrant-store` to your project:
[source,xml]
----
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store</artifactId>
</dependency>
----
or to your Gradle `build.gradle` build file.
[source,groovy]
----
dependencies {
implementation 'org.springframework.ai:spring-ai-qdrant-store'
}
----
TIP: Refer to the xref:getting-started.adoc#dependency-management[Dependency Management] section to add the Spring AI BOM to your build file.
Create a Qdrant client bean:
[source,java]
----
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient.Builder grpcClientBuilder =
QdrantGrpcClient.newBuilder(
"<QDRANT_HOSTNAME>",
<QDRANT_GRPC_PORT>,
<IS_TLS>);
grpcClientBuilder.withApiKey("<QDRANT_API_KEY>");
return new QdrantClient(grpcClientBuilder.build());
}
----
Then create the `QdrantVectorStore` bean using the builder pattern:
[source,java]
----
@Bean
public VectorStore vectorStore(QdrantClient qdrantClient, EmbeddingModel embeddingModel) {
return QdrantVectorStore.builder(qdrantClient)
.embeddingModel(embeddingModel)
.collectionName("custom-collection") // Optional: defaults to "vector_store"
.initializeSchema(true) // Optional: defaults to false
.batchingStrategy(new TokenCountBatchingStrategy()) // Optional: defaults to TokenCountBatchingStrategy
.build();
}
// This can be any EmbeddingModel implementation
@Bean
public EmbeddingModel embeddingModel() {
return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")));
}
----
== Metadata Filtering
You can leverage the generic, portable xref:api/vectordbs.adoc#metadata-filters[metadata filters] with Qdrant store as well.
For example, you can use either the text expression language:
@@ -128,10 +173,10 @@ For example, you can use either the text expression language:
----
vectorStore.similaritySearch(
SearchRequest.defaults()
.withQuery("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("author in ['john', 'jill'] && article_type == 'blog'"));
.withQuery("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("author in ['john', 'jill'] && article_type == 'blog'"));
----
or programmatically using the `Filter.Expression` DSL:
@@ -149,54 +194,4 @@ vectorStore.similaritySearch(SearchRequest.defaults()
b.eq("article_type", "blog")).build()));
----
NOTE: These filter expressions are converted into the equivalent Qdrant link:https://qdrant.tech/documentation/concepts/filtering/[filters].
== Manual Configuration
Instead of using the Spring Boot auto-configuration, you can manually configure the `QdrantVectorStore`. For this you need to add the `spring-ai-qdrant-store` dependency to your project:
[source,xml]
----
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-qdrant-store</artifactId>
</dependency>
----
or to your Gradle `build.gradle` build file.
[source,groovy]
----
dependencies {
implementation 'org.springframework.ai:spring-ai-qdrant'
}
----
To configure Qdrant in your application, you can create a QdrantClient:
[source,java]
----
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient.Builder grpcClientBuilder =
QdrantGrpcClient.newBuilder(
"<QDRANT_HOSTNAME>",
<QDRANT_GRPC_PORT>,
<IS_TSL>);
grpcClientBuilder.withApiKey("<QDRANT_API_KEY>");
return new QdrantClient(grpcClientBuilder.build());
}
----
Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI starter to your project.
This provides you with an implementation of the Embeddings client:
[source,java]
----
@Bean
public QdrantVectorStore vectorStore(EmbeddingModel embeddingModel, QdrantClient qdrantClient) {
return new QdrantVectorStore(qdrantClient, "<QDRANT_COLLECTION_NAME>", embeddingModel);
}
----
NOTE: These (portable) filter expressions get automatically converted into the proprietary Qdrant link:https://qdrant.tech/documentation/concepts/filtering/[filter expressions].

View File

@@ -77,9 +77,14 @@ public class QdrantVectorStoreAutoConfiguration {
QdrantClient qdrantClient, ObjectProvider<ObservationRegistry> observationRegistry,
ObjectProvider<VectorStoreObservationConvention> 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 {

View File

@@ -42,6 +42,7 @@ import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
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;
@@ -53,6 +54,71 @@ import org.springframework.util.Assert;
* Qdrant vectorStore implementation. This store supports creating, updating, deleting,
* and similarity searching of documents in a Qdrant collection.
*
* <p>
* The store uses Qdrant's vector search functionality to persist and query vector
* embeddings along with their associated document content and metadata. The
* implementation leverages Qdrant's HNSW (Hierarchical Navigable Small World) algorithm
* for efficient k-NN search operations.
* </p>
*
* <p>
* Features:
* </p>
* <ul>
* <li>Automatic schema initialization with configurable collection creation</li>
* <li>Support for cosine similarity distance metric</li>
* <li>Metadata filtering using Qdrant's filter expressions</li>
* <li>Configurable similarity thresholds for search results</li>
* <li>Batch processing support with configurable strategies</li>
* <li>Observation and metrics support through Micrometer</li>
* </ul>
*
* <p>
* Basic usage example:
* </p>
* <pre>{@code
* QdrantVectorStore vectorStore = QdrantVectorStore.builder(qdrantClient)
* .embeddingModel(embeddingModel)
* .initializeSchema(true)
* .build();
*
* // Add documents
* vectorStore.add(List.of(
* new Document("content1", Map.of("key1", "value1")),
* new Document("content2", Map.of("key2", "value2"))
* ));
*
* // Search with filters
* List<Document> results = vectorStore.similaritySearch(
* SearchRequest.query("search text")
* .withTopK(5)
* .withSimilarityThreshold(0.7)
* .withFilterExpression("key1 == 'value1'")
* );
* }</pre>
*
* <p>
* Advanced configuration example:
* </p>
* <pre>{@code
* QdrantVectorStore vectorStore = QdrantVectorStore.builder(qdrantClient)
* .embeddingModel(embeddingModel)
* .collectionName("custom-collection")
* .initializeSchema(true)
* .batchingStrategy(new TokenCountBatchingStrategy())
* .observationRegistry(observationRegistry)
* .customObservationConvention(customConvention)
* .build();
* }</pre>
*
* <p>
* Requirements:
* </p>
* <ul>
* <li>Running Qdrant instance accessible via gRPC</li>
* <li>Collection with vector size matching the embedding model dimensions</li>
* </ul>
*
* @author Anush Shetty
* @author Christian Tzolov
* @author Eddú Meléndez
@@ -67,8 +133,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 +149,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 +166,48 @@ public class QdrantVectorStore extends AbstractObservationVectorStore implements
* @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);
this.initializeSchema = initializeSchema;
this.embeddingModel = embeddingModel;
this.collectionName = collectionName;
this.qdrantClient = qdrantClient;
this.batchingStrategy = batchingStrategy;
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;
}
/**
* 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 +364,80 @@ public class QdrantVectorStore extends AbstractObservationVectorStore implements
}
/**
* 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<QdrantBuilder> {
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);
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* 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.vectorstore.qdrant;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link QdrantVectorStore.QdrantBuilder}.
*
* @author Mark Pollack
*/
class QdrantVectorStoreBuilderTests {
private QdrantClient qdrantClient;
private EmbeddingModel embeddingModel;
@BeforeEach
void setUp() {
this.qdrantClient = mock(QdrantClient.class);
this.embeddingModel = mock(EmbeddingModel.class);
}
@Test
void defaultConfiguration() {
QdrantVectorStore vectorStore = QdrantVectorStore.builder(qdrantClient).embeddingModel(embeddingModel).build();
// Verify default values
assertThat(vectorStore).hasFieldOrPropertyWithValue("collectionName", "vector_store");
assertThat(vectorStore).hasFieldOrPropertyWithValue("initializeSchema", false);
assertThat(vectorStore).hasFieldOrPropertyWithValue("batchingStrategy.class", TokenCountBatchingStrategy.class);
}
@Test
void customConfiguration() {
QdrantVectorStore vectorStore = QdrantVectorStore.builder(qdrantClient)
.embeddingModel(embeddingModel)
.collectionName("custom_collection")
.initializeSchema(true)
.batchingStrategy(new TokenCountBatchingStrategy())
.build();
assertThat(vectorStore).hasFieldOrPropertyWithValue("collectionName", "custom_collection");
assertThat(vectorStore).hasFieldOrPropertyWithValue("initializeSchema", true);
assertThat(vectorStore).hasFieldOrPropertyWithValue("batchingStrategy.class", TokenCountBatchingStrategy.class);
}
@Test
void nullQdrantClientInConstructorShouldThrowException() {
assertThatThrownBy(() -> QdrantVectorStore.builder(null)).isInstanceOf(IllegalArgumentException.class)
.hasMessage("QdrantClient must not be null");
}
@Test
void nullEmbeddingModelShouldThrowException() {
assertThatThrownBy(() -> QdrantVectorStore.builder(qdrantClient).embeddingModel(null).build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("EmbeddingModel must not be null");
}
@Test
void emptyCollectionNameShouldThrowException() {
assertThatThrownBy(
() -> QdrantVectorStore.builder(qdrantClient).embeddingModel(embeddingModel).collectionName("").build())
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("collectionName must not be empty");
}
@Test
void nullBatchingStrategyShouldThrowException() {
assertThatThrownBy(() -> QdrantVectorStore.builder(qdrantClient)
.embeddingModel(embeddingModel)
.batchingStrategy(null)
.build()).isInstanceOf(IllegalArgumentException.class).hasMessage("BatchingStrategy must not be null");
}
}

View File

@@ -254,7 +254,11 @@ public class QdrantVectorStoreIT {
@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

View File

@@ -195,8 +195,14 @@ public class QdrantVectorStoreObservationIT {
@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