results = this.vectorStore.similaritySearch(SearchRequest.query("Spring").withTopK(5));
-----
-
-=== Configuration properties
-
-You can use the following properties in your Spring Boot configuration to customize the OpenSearch vector store.
-
-[cols="2,5,1",stripes=even]
-|===
-|Property| Description | Default value
-
-|`spring.ai.vectorstore.opensearch.uris`| URIs of the OpenSearch cluster endpoints. | -
-|`spring.ai.vectorstore.opensearch.username`| Username for accessing the OpenSearch cluster. | -
-|`spring.ai.vectorstore.opensearch.password`| Password for the specified username. | -
-|`spring.ai.vectorstore.opensearch.indexName`| Name of the default index to be used within the OpenSearch cluster. | `spring-ai-document-index`
-|`spring.ai.vectorstore.opensearch.mappingJson`| JSON string defining the mapping for the index; specifies how documents and their
-fields are stored and indexed. Refer link:https://opensearch.org/docs/latest/search-plugins/vector-search/[here] for some sample configurations |
-{
- "properties":{
- "embedding":{
- "type":"knn_vector",
- "dimension":1536
- }
- }
+@Bean
+public VectorStore vectorStore(OpenSearchClient openSearchClient, EmbeddingModel embeddingModel) {
+ return OpenSearchVectorStore.builder()
+ .openSearchClient(openSearchClient)
+ .embeddingModel(embeddingModel)
+ .index("custom-index") // Optional: defaults to "spring-ai-document-index"
+ .similarityFunction("l2") // Optional: defaults to "cosinesimil"
+ .initializeSchema(true) // Optional: defaults to false
+ .batchingStrategy(new TokenCountBatchingStrategy()) // Optional: defaults to TokenCountBatchingStrategy
+ .build();
}
-|`spring.ai.vectorstore.opensearch.aws.host`| Hostname of the OpenSearch instance. | -
-|`spring.ai.vectorstore.opensearch.aws.serviceName`| AWS service name for the OpenSearch instance. | -
-|`spring.ai.vectorstore.opensearch.aws.accessKey`| AWS access key for the OpenSearch instance. | -
-|`spring.ai.vectorstore.opensearch.aws.secretKey`| AWS secret key for the OpenSearch instance. | -
-|`spring.ai.vectorstore.opensearch.aws.region`| AWS region for the OpenSearch instance. | -
-|===
-=== Customizing OpenSearch Client Configuration
-
-In cases where the Spring Boot auto-configured OpenSearchClient with `Apache HttpClient 5 Transport` bean is not what
-you want or need, you can still define your own bean.
-Please read the link:https://opensearch.org/docs/latest/clients/java/[OpenSearch Java Client Documentation]
+// This can be any EmbeddingModel implementation
+@Bean
+public EmbeddingModel embeddingModel() {
+ return new OpenAiEmbeddingModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")));
+}
+----
== Metadata Filtering
@@ -202,34 +200,30 @@ You can leverage the generic, portable xref:api/vectordbs.adoc#metadata-filters[
For example, you can use either the text expression language:
-[tabs]
-======
-SQL filter syntax::
-+
[source,java]
----
-vectorStore.similaritySearch(SearchRequest.defaults()
+vectorStore.similaritySearch(
+ SearchRequest.defaults()
.withQuery("The World")
.withTopK(TOP_K)
.withSimilarityThreshold(SIMILARITY_THRESHOLD)
.withFilterExpression("author in ['john', 'jill'] && 'article_type' == 'blog'"));
----
-`Filter.Expression` DSL::
-+
+or programmatically using the `Filter.Expression` DSL:
+
[source,java]
----
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.defaults()
- .withQuery("The World")
- .withTopK(TOP_K)
- .withSimilarityThreshold(SIMILARITY_THRESHOLD)
- .withFilterExpression(b.and(
- b.in("john", "jill"),
- b.eq("article_type", "blog")).build()));
+ .withQuery("The World")
+ .withTopK(TOP_K)
+ .withSimilarityThreshold(SIMILARITY_THRESHOLD)
+ .withFilterExpression(b.and(
+ b.in("author", "john", "jill"),
+ b.eq("article_type", "blog")).build()));
----
-======
NOTE: Those (portable) filter expressions get automatically converted into the proprietary OpenSearch link:https://opensearch.org/docs/latest/query-dsl/full-text/query-string/[Query string query].
diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java
index 47628a30c..28a4e4529 100644
--- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java
+++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfiguration.java
@@ -40,7 +40,7 @@ import software.amazon.awssdk.regions.Region;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
-import org.springframework.ai.vectorstore.OpenSearchVectorStore;
+import org.springframework.ai.vectorstore.opensearch.OpenSearchVectorStore;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -78,9 +78,17 @@ public class OpenSearchVectorStoreAutoConfiguration {
var indexName = Optional.ofNullable(properties.getIndexName()).orElse(OpenSearchVectorStore.DEFAULT_INDEX_NAME);
var mappingJson = Optional.ofNullable(properties.getMappingJson())
.orElse(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION);
- return new OpenSearchVectorStore(indexName, openSearchClient, embeddingModel, mappingJson,
- properties.isInitializeSchema(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
- customObservationConvention.getIfAvailable(() -> null), batchingStrategy);
+
+ return OpenSearchVectorStore.builder()
+ .index(indexName)
+ .openSearchClient(openSearchClient)
+ .embeddingModel(embeddingModel)
+ .mappingJson(mappingJson)
+ .initializeSchema(properties.isInitializeSchema())
+ .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
+ .customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
+ .batchingStrategy(batchingStrategy)
+ .build();
}
@Configuration(proxyBeanMethods = false)
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java
index d3449dc9f..0810c6cab 100644
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/AwsOpenSearchVectorStoreAutoConfigurationIT.java
@@ -36,7 +36,7 @@ import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.transformers.TransformersEmbeddingModel;
-import org.springframework.ai.vectorstore.OpenSearchVectorStore;
+import org.springframework.ai.vectorstore.opensearch.OpenSearchVectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java
index 55022d938..87c774ced 100644
--- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java
+++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/opensearch/OpenSearchVectorStoreAutoConfigurationIT.java
@@ -36,7 +36,7 @@ import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.transformers.TransformersEmbeddingModel;
-import org.springframework.ai.vectorstore.OpenSearchVectorStore;
+import org.springframework.ai.vectorstore.opensearch.OpenSearchVectorStore;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
import org.springframework.boot.autoconfigure.AutoConfigurations;
diff --git a/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverter.java b/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverter.java
similarity index 98%
rename from vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverter.java
rename to vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverter.java
index 71022a986..9b5be81e7 100644
--- a/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverter.java
+++ b/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverter.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.text.ParseException;
import java.text.SimpleDateFormat;
diff --git a/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchVectorStore.java b/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStore.java
similarity index 52%
rename from vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchVectorStore.java
rename to vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStore.java
index 0492ec80f..5a048b29b 100644
--- a/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/OpenSearchVectorStore.java
+++ b/vector-stores/spring-ai-opensearch-store/src/main/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStore.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.io.IOException;
import java.io.StringReader;
@@ -37,8 +37,6 @@ import org.opensearch.client.opensearch.core.search.Hit;
import org.opensearch.client.opensearch.indices.CreateIndexRequest;
import org.opensearch.client.opensearch.indices.CreateIndexResponse;
import org.opensearch.client.transport.endpoints.BooleanResponse;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentMetadata;
@@ -48,6 +46,8 @@ import org.springframework.ai.embedding.EmbeddingOptionsBuilder;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
+import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder;
+import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.filter.Filter;
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
@@ -57,7 +57,84 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Assert;
/**
- * An ObservationVectorStore implementation that stores vectors in OpenSearch.
+ * OpenSearch-based vector store implementation using OpenSearch's vector search
+ * capabilities.
+ *
+ *
+ * The store uses OpenSearch's k-NN functionality to persist and query vector embeddings
+ * along with their associated document content and metadata. The implementation supports
+ * various similarity functions and provides efficient vector search operations.
+ *
+ *
+ *
+ * Features:
+ *
+ *
+ * - Automatic schema initialization with configurable index creation
+ * - Support for multiple similarity functions: Cosine, L1, L2, and Linf
+ * - Metadata filtering using OpenSearch query expressions
+ * - Configurable similarity thresholds for search results
+ * - Batch processing support with configurable strategies
+ * - Observation and metrics support through Micrometer
+ *
+ *
+ *
+ * Basic usage example:
+ *
+ * {@code
+ * OpenSearchVectorStore vectorStore = OpenSearchVectorStore.builder()
+ * .openSearchClient(openSearchClient)
+ * .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 results = vectorStore.similaritySearch(
+ * SearchRequest.query("search text")
+ * .withTopK(5)
+ * .withSimilarityThreshold(0.7)
+ * .withFilterExpression("key1 == 'value1'")
+ * );
+ * }
+ *
+ *
+ * Advanced configuration example:
+ *
+ * {@code
+ * OpenSearchVectorStore vectorStore = OpenSearchVectorStore.builder()
+ * .openSearchClient(openSearchClient)
+ * .embeddingModel(embeddingModel)
+ * .index("custom-index")
+ * .mappingJson(customMapping)
+ * .similarityFunction("l2")
+ * .initializeSchema(true)
+ * .batchingStrategy(new TokenCountBatchingStrategy())
+ * .filterExpressionConverter(new CustomFilterExpressionConverter())
+ * .build();
+ * }
+ *
+ *
+ * Similarity Functions:
+ *
+ *
+ * - cosinesimil: Default, suitable for most use cases. Measures cosine similarity
+ * between vectors.
+ * - l1: Manhattan distance between vectors.
+ * - l2: Euclidean distance between vectors.
+ * - linf: Chebyshev distance between vectors.
+ *
+ *
+ *
+ * For more information about available similarity functions, see: OpenSearch
+ * KNN Spaces
+ *
*
* @author Jemin Huh
* @author Soby Chacko
@@ -83,10 +160,6 @@ public class OpenSearchVectorStore extends AbstractObservationVectorStore implem
}
""";
- private static final Logger logger = LoggerFactory.getLogger(OpenSearchVectorStore.class);
-
- private final EmbeddingModel embeddingModel;
-
private final OpenSearchClient openSearchClient;
private final String index;
@@ -101,40 +174,106 @@ public class OpenSearchVectorStore extends AbstractObservationVectorStore implem
private String similarityFunction;
+ /**
+ * Creates a new OpenSearchVectorStore with default mapping and collection name.
+ * @deprecated Use {@link #builder()} instead
+ * @param openSearchClient The OpenSearch client
+ * @param embeddingModel The embedding model to use
+ * @param initializeSchema Whether to initialize the schema
+ * @since 1.0.0
+ */
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public OpenSearchVectorStore(OpenSearchClient openSearchClient, EmbeddingModel embeddingModel,
boolean initializeSchema) {
this(openSearchClient, embeddingModel, DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION, initializeSchema);
}
+ /**
+ * Creates a new OpenSearchVectorStore with custom mapping.
+ * @deprecated Use {@link #builder()} instead
+ * @param openSearchClient The OpenSearch client
+ * @param embeddingModel The embedding model to use
+ * @param mappingJson The JSON mapping for the index
+ * @param initializeSchema Whether to initialize the schema
+ * @since 1.0.0
+ */
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public OpenSearchVectorStore(OpenSearchClient openSearchClient, EmbeddingModel embeddingModel, String mappingJson,
boolean initializeSchema) {
this(DEFAULT_INDEX_NAME, openSearchClient, embeddingModel, mappingJson, initializeSchema);
}
+ /**
+ * Creates a new OpenSearchVectorStore with custom index name and mapping.
+ * @deprecated Use {@link #builder()} instead
+ * @param index The name of the index
+ * @param openSearchClient The OpenSearch client
+ * @param embeddingModel The embedding model to use
+ * @param mappingJson The JSON mapping for the index
+ * @param initializeSchema Whether to initialize the schema
+ * @since 1.0.0
+ */
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public OpenSearchVectorStore(String index, OpenSearchClient openSearchClient, EmbeddingModel embeddingModel,
String mappingJson, boolean initializeSchema) {
this(index, openSearchClient, embeddingModel, mappingJson, initializeSchema, ObservationRegistry.NOOP, null,
new TokenCountBatchingStrategy());
}
+ /**
+ * Creates a new OpenSearchVectorStore with all configuration options.
+ * @deprecated Use {@link #builder()} instead
+ * @param index The name of the index
+ * @param openSearchClient The OpenSearch client
+ * @param embeddingModel The embedding model to use
+ * @param mappingJson The JSON mapping for the index
+ * @param initializeSchema Whether to initialize the schema
+ * @param observationRegistry The observation registry for metrics
+ * @param customObservationConvention Custom observation convention
+ * @param batchingStrategy The strategy for batching operations
+ * @since 1.0.0
+ */
+ @Deprecated(since = "1.0.0-M5", forRemoval = true)
public OpenSearchVectorStore(String index, OpenSearchClient openSearchClient, EmbeddingModel embeddingModel,
String mappingJson, boolean initializeSchema, ObservationRegistry observationRegistry,
VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
- super(observationRegistry, customObservationConvention);
+ this(builder().openSearchClient(openSearchClient)
+ .embeddingModel(embeddingModel)
+ .index(index)
+ .mappingJson(mappingJson)
+ .initializeSchema(initializeSchema)
+ .observationRegistry(observationRegistry)
+ .customObservationConvention(customObservationConvention)
+ .batchingStrategy(batchingStrategy));
+ }
- Objects.requireNonNull(embeddingModel, "RestClient must not be null");
- Objects.requireNonNull(embeddingModel, "EmbeddingModel must not be null");
- this.openSearchClient = openSearchClient;
- this.embeddingModel = embeddingModel;
- this.index = index;
- this.mappingJson = mappingJson;
- this.filterExpressionConverter = new OpenSearchAiSearchFilterExpressionConverter();
+ /**
+ * Creates a new OpenSearchVectorStore using the builder pattern.
+ * @param builder The configured builder instance
+ */
+ protected OpenSearchVectorStore(OpenSearchBuilder builder) {
+ super(builder);
+
+ Assert.notNull(builder.openSearchClient, "OpenSearchClient must not be null");
+
+ this.openSearchClient = builder.openSearchClient;
+ this.index = builder.index;
+ this.mappingJson = builder.mappingJson;
+ this.filterExpressionConverter = builder.filterExpressionConverter;
// the potential functions for vector fields at
// https://opensearch.org/docs/latest/search-plugins/knn/approximate-knn/#spaces
- this.similarityFunction = COSINE_SIMILARITY_FUNCTION;
- this.initializeSchema = initializeSchema;
- this.batchingStrategy = batchingStrategy;
+ this.similarityFunction = builder.similarityFunction;
+ this.initializeSchema = builder.initializeSchema;
+ this.batchingStrategy = builder.batchingStrategy;
+ }
+
+ /**
+ * Creates a new builder instance for configuring an OpenSearchVectorStore.
+ * @return A new OpenSearchBuilder instance
+ */
+ public static OpenSearchBuilder builder() {
+ return new OpenSearchBuilder();
}
public OpenSearchVectorStore withSimilarityFunction(String similarityFunction) {
@@ -306,4 +445,132 @@ public class OpenSearchVectorStore extends AbstractObservationVectorStore implem
public record OpenSearchDocument(String id, String content, Map metadata, float[] embedding) {
}
+ /**
+ * Builder class for creating OpenSearchVectorStore instances.
+ */
+ public static class OpenSearchBuilder extends AbstractVectorStoreBuilder {
+
+ private OpenSearchClient openSearchClient;
+
+ private String index = DEFAULT_INDEX_NAME;
+
+ private String mappingJson = DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION;
+
+ private boolean initializeSchema = false;
+
+ private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy();
+
+ private FilterExpressionConverter filterExpressionConverter = new OpenSearchAiSearchFilterExpressionConverter();
+
+ private String similarityFunction = COSINE_SIMILARITY_FUNCTION;
+
+ /**
+ * Sets the OpenSearch client.
+ * @param openSearchClient The OpenSearch client to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if openSearchClient is null
+ */
+ public OpenSearchBuilder openSearchClient(OpenSearchClient openSearchClient) {
+ Assert.notNull(openSearchClient, "OpenSearchClient must not be null");
+ this.openSearchClient = openSearchClient;
+ return this;
+ }
+
+ /**
+ * Sets the embedding model.
+ * @param embeddingModel The embedding model to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if embeddingModel is null
+ */
+ public OpenSearchBuilder embeddingModel(EmbeddingModel embeddingModel) {
+ Assert.notNull(embeddingModel, "EmbeddingModel must not be null");
+ this.embeddingModel = embeddingModel;
+ return this;
+ }
+
+ /**
+ * Sets the index name.
+ * @param index The name of the index to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if index is null or empty
+ */
+ public OpenSearchBuilder index(String index) {
+ Assert.hasText(index, "index must not be null or empty");
+ this.index = index;
+ return this;
+ }
+
+ /**
+ * Sets the JSON mapping for the index.
+ * @param mappingJson The JSON mapping to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if mappingJson is null or empty
+ */
+ public OpenSearchBuilder mappingJson(String mappingJson) {
+ Assert.hasText(mappingJson, "mappingJson must not be null or empty");
+ this.mappingJson = mappingJson;
+ return this;
+ }
+
+ /**
+ * Sets whether to initialize the schema.
+ * @param initializeSchema true to initialize schema, false otherwise
+ * @return The builder instance
+ */
+ public OpenSearchBuilder initializeSchema(boolean initializeSchema) {
+ this.initializeSchema = initializeSchema;
+ return this;
+ }
+
+ /**
+ * Sets the batching strategy.
+ * @param batchingStrategy The batching strategy to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if batchingStrategy is null
+ */
+ public OpenSearchBuilder batchingStrategy(BatchingStrategy batchingStrategy) {
+ Assert.notNull(batchingStrategy, "batchingStrategy must not be null");
+ this.batchingStrategy = batchingStrategy;
+ return this;
+ }
+
+ /**
+ * Sets the filter expression converter.
+ * @param converter The filter expression converter to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if converter is null
+ */
+ public OpenSearchBuilder filterExpressionConverter(FilterExpressionConverter converter) {
+ Assert.notNull(converter, "filterExpressionConverter must not be null");
+ this.filterExpressionConverter = converter;
+ return this;
+ }
+
+ /**
+ * Sets the similarity function for vector comparison. See
+ * https://opensearch.org/docs/latest/search-plugins/knn/approximate-knn/#spaces
+ * for available functions.
+ * @param similarityFunction The similarity function to use
+ * @return The builder instance
+ * @throws IllegalArgumentException if similarityFunction is null or empty
+ */
+ public OpenSearchBuilder similarityFunction(String similarityFunction) {
+ Assert.hasText(similarityFunction, "similarityFunction must not be null or empty");
+ this.similarityFunction = similarityFunction;
+ return this;
+ }
+
+ /**
+ * Builds a new OpenSearchVectorStore instance with the configured properties.
+ * @return A new OpenSearchVectorStore instance
+ * @throws IllegalStateException if the builder is in an invalid state
+ */
+ @Override
+ public OpenSearchVectorStore build() {
+ validate();
+ return new OpenSearchVectorStore(this);
+ }
+
+ }
+
}
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverterTest.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverterTest.java
similarity index 99%
rename from vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverterTest.java
rename to vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverterTest.java
index 77e2a95a0..ba511a850 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchAiSearchFilterExpressionConverterTest.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchAiSearchFilterExpressionConverterTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.util.Date;
import java.util.List;
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchImage.java
similarity index 94%
rename from vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java
rename to vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchImage.java
index 42c8d9b9c..99bf315e3 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchImage.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchImage.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import org.testcontainers.utility.DockerImageName;
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
similarity index 94%
rename from vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java
rename to vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
index 5a4a5b20a..e650efee8 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreIT.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -47,6 +47,8 @@ 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.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -396,9 +398,13 @@ class OpenSearchVectorStoreIT {
@Qualifier("vectorStore")
public OpenSearchVectorStore vectorStore(EmbeddingModel embeddingModel) {
try {
- return new OpenSearchVectorStore(new OpenSearchClient(ApacheHttpClient5TransportBuilder
- .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
- .build()), embeddingModel, true);
+ return OpenSearchVectorStore.builder()
+ .openSearchClient(new OpenSearchClient(ApacheHttpClient5TransportBuilder
+ .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
+ .build()))
+ .embeddingModel(embeddingModel)
+ .initializeSchema(true)
+ .build();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
@@ -409,12 +415,15 @@ class OpenSearchVectorStoreIT {
@Qualifier("anotherVectorStore")
public OpenSearchVectorStore anotherVectorStore(EmbeddingModel embeddingModel) {
try {
- return new OpenSearchVectorStore("another_index",
- new OpenSearchClient(ApacheHttpClient5TransportBuilder
- .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
- .build()),
- embeddingModel, OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION,
- true);
+ return OpenSearchVectorStore.builder()
+ .index("another_index")
+ .openSearchClient(new OpenSearchClient(ApacheHttpClient5TransportBuilder
+ .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
+ .build()))
+ .embeddingModel(embeddingModel)
+ .mappingJson(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION)
+ .initializeSchema(true)
+ .build();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
similarity index 92%
rename from vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java
rename to vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
index 69a36ce52..cba4321fb 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreObservationIT.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreObservationIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -47,6 +47,8 @@ import org.springframework.ai.observation.conventions.VectorStoreProvider;
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
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.ai.vectorstore.observation.DefaultVectorStoreObservationConvention;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.HighCardinalityKeyNames;
import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames;
@@ -205,12 +207,18 @@ public class OpenSearchVectorStoreObservationIT {
public OpenSearchVectorStore vectorStore(EmbeddingModel embeddingModel,
ObservationRegistry observationRegistry) {
try {
- return new OpenSearchVectorStore(OpenSearchVectorStore.DEFAULT_INDEX_NAME,
- new OpenSearchClient(ApacheHttpClient5TransportBuilder
- .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
- .build()),
- embeddingModel, OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION, true,
- observationRegistry, null, new TokenCountBatchingStrategy());
+ return OpenSearchVectorStore.builder()
+ .index(OpenSearchVectorStore.DEFAULT_INDEX_NAME)
+ .openSearchClient(new OpenSearchClient(ApacheHttpClient5TransportBuilder
+ .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
+ .build()))
+ .embeddingModel(embeddingModel)
+ .mappingJson(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION)
+ .initializeSchema(true)
+ .observationRegistry(observationRegistry)
+ .customObservationConvention(null)
+ .batchingStrategy(new TokenCountBatchingStrategy())
+ .build();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
diff --git a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreWithOllamaIT.java b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreWithOllamaIT.java
similarity index 89%
rename from vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreWithOllamaIT.java
rename to vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreWithOllamaIT.java
index ef71b5cf4..5ef309c83 100644
--- a/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/OpenSearchVectorStoreWithOllamaIT.java
+++ b/vector-stores/spring-ai-opensearch-store/src/test/java/org/springframework/ai/vectorstore/opensearch/OpenSearchVectorStoreWithOllamaIT.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package org.springframework.ai.vectorstore;
+package org.springframework.ai.vectorstore.opensearch;
import java.io.IOException;
import java.net.URISyntaxException;
@@ -46,6 +46,8 @@ import org.springframework.ai.ollama.api.OllamaOptions;
import org.springframework.ai.ollama.management.ModelManagementOptions;
import org.springframework.ai.ollama.management.OllamaModelManager;
import org.springframework.ai.ollama.management.PullModelStrategy;
+import org.springframework.ai.vectorstore.SearchRequest;
+import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
@@ -167,9 +169,13 @@ class OpenSearchVectorStoreWithOllamaIT {
@Qualifier("vectorStore")
public OpenSearchVectorStore vectorStore(EmbeddingModel embeddingModel) {
try {
- return new OpenSearchVectorStore(new OpenSearchClient(ApacheHttpClient5TransportBuilder
- .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
- .build()), embeddingModel, true);
+ return OpenSearchVectorStore.builder()
+ .openSearchClient(new OpenSearchClient(ApacheHttpClient5TransportBuilder
+ .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
+ .build()))
+ .embeddingModel(embeddingModel)
+ .initializeSchema(true)
+ .build();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);
@@ -180,12 +186,15 @@ class OpenSearchVectorStoreWithOllamaIT {
@Qualifier("anotherVectorStore")
public OpenSearchVectorStore anotherVectorStore(EmbeddingModel embeddingModel) {
try {
- return new OpenSearchVectorStore("another_index",
- new OpenSearchClient(ApacheHttpClient5TransportBuilder
- .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
- .build()),
- embeddingModel, OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION,
- true);
+ return OpenSearchVectorStore.builder()
+ .index("another_index")
+ .openSearchClient(new OpenSearchClient(ApacheHttpClient5TransportBuilder
+ .builder(HttpHost.create(opensearchContainer.getHttpHostAddress()))
+ .build()))
+ .embeddingModel(embeddingModel)
+ .mappingJson(OpenSearchVectorStore.DEFAULT_MAPPING_EMBEDDING_TYPE_KNN_VECTOR_DIMENSION)
+ .initializeSchema(true)
+ .build();
}
catch (URISyntaxException e) {
throw new RuntimeException(e);