From 49acedda2e846fcb4936cdf18bb2bc82999044bb Mon Sep 17 00:00:00 2001 From: Soby Chacko Date: Tue, 17 Dec 2024 15:25:33 -0500 Subject: [PATCH] Oracle vector store builder refactoring --- .../OracleVectorStoreAutoConfiguration.java | 23 +- .../oracle/OracleVectorStoreProperties.java | 4 +- .../OracleVectorStorePropertiesTests.java | 6 +- .../{ => oracle}/OracleVectorStore.java | 307 +++++++++++++++--- .../SqlJsonPathFilterExpressionConverter.java | 2 +- .../vectorstore/{ => oracle}/OracleImage.java | 2 +- .../{ => oracle}/OracleVectorStoreIT.java | 31 +- .../OracleVectorStoreObservationIT.java | 25 +- ...sonPathFilterExpressionConverterTests.java | 2 +- 9 files changed, 324 insertions(+), 78 deletions(-) rename vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/{ => oracle}/OracleVectorStore.java (69%) rename vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/{ => oracle}/SqlJsonPathFilterExpressionConverter.java (98%) rename vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/{ => oracle}/OracleImage.java (94%) rename vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/{ => oracle}/OracleVectorStoreIT.java (93%) rename vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/{ => oracle}/OracleVectorStoreObservationIT.java (91%) rename vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/{ => oracle}/SqlJsonPathFilterExpressionConverterTests.java (96%) diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreAutoConfiguration.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreAutoConfiguration.java index f5f5cec8885..f8fcff20d6d 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreAutoConfiguration.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreAutoConfiguration.java @@ -23,7 +23,7 @@ import org.springframework.ai.embedding.BatchingStrategy; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.embedding.TokenCountBatchingStrategy; -import org.springframework.ai.vectorstore.OracleVectorStore; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore; import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -59,11 +59,22 @@ public OracleVectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel e OracleVectorStoreProperties properties, ObjectProvider observationRegistry, ObjectProvider customObservationConvention, BatchingStrategy batchingStrategy) { - return new OracleVectorStore(jdbcTemplate, embeddingModel, properties.getTableName(), properties.getIndexType(), - properties.getDistanceType(), properties.getDimensions(), properties.getSearchAccuracy(), - properties.isInitializeSchema(), properties.isRemoveExistingVectorStoreTable(), - properties.isForcedNormalization(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP), - customObservationConvention.getIfAvailable(() -> null), batchingStrategy); + + return OracleVectorStore.builder() + .jdbcTemplate(jdbcTemplate) + .embeddingModel(embeddingModel) + .tableName(properties.getTableName()) + .indexType(properties.getIndexType()) + .distanceType(properties.getDistanceType()) + .dimensions(properties.getDimensions()) + .searchAccuracy(properties.getSearchAccuracy()) + .initializeSchema(properties.isInitializeSchema()) + .removeExistingVectorStoreTable(properties.isRemoveExistingVectorStoreTable()) + .forcedNormalization(properties.isForcedNormalization()) + .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP)) + .customObservationConvention(customObservationConvention.getIfAvailable(() -> null)) + .batchingStrategy(batchingStrategy) + .build(); } } diff --git a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreProperties.java b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreProperties.java index cedc3b0618c..ff355b99261 100644 --- a/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreProperties.java +++ b/spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStoreProperties.java @@ -17,7 +17,7 @@ package org.springframework.ai.autoconfigure.vectorstore.oracle; import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties; -import org.springframework.ai.vectorstore.OracleVectorStore; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -42,7 +42,7 @@ public class OracleVectorStoreProperties extends CommonVectorStoreProperties { private boolean forcedNormalization; - private int searchAccuracy = org.springframework.ai.vectorstore.OracleVectorStore.DEFAULT_SEARCH_ACCURACY; + private int searchAccuracy = OracleVectorStore.DEFAULT_SEARCH_ACCURACY; public String getTableName() { return this.tableName; diff --git a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStorePropertiesTests.java b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStorePropertiesTests.java index b57df38922d..efd5579dd9a 100644 --- a/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStorePropertiesTests.java +++ b/spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/oracle/OracleVectorStorePropertiesTests.java @@ -18,9 +18,9 @@ import org.junit.jupiter.api.Test; -import org.springframework.ai.vectorstore.OracleVectorStore; -import org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType; -import org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreIndexType; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore.OracleVectorStoreDistanceType; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore.OracleVectorStoreIndexType; import static org.assertj.core.api.Assertions.assertThat; diff --git a/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/OracleVectorStore.java b/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java similarity index 69% rename from vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/OracleVectorStore.java rename to vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java index 08d8e227e83..eb49dfa1a72 100644 --- a/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/OracleVectorStore.java +++ b/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/OracleVectorStore.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import java.io.ByteArrayOutputStream; import java.sql.PreparedStatement; @@ -46,15 +46,17 @@ 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.FilterExpressionConverter; import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore; import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext; -import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext.Builder; import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention; import org.springframework.beans.factory.InitializingBean; import org.springframework.jdbc.core.BatchPreparedStatementSetter; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -108,8 +110,6 @@ public class OracleVectorStore extends AbstractObservationVectorStore implements private final JdbcTemplate jdbcTemplate; - private final EmbeddingModel embeddingModel; - private final boolean initializeSchema; private final boolean removeExistingVectorStoreTable; @@ -146,16 +146,46 @@ public class OracleVectorStore extends AbstractObservationVectorStore implements private final ByteArrayOutputStream out = new ByteArrayOutputStream(); + /** + * Creates a new OracleVectorStore with default configuration. + * @param jdbcTemplate the JDBC template to use + * @param embeddingModel the embedding model to use + * @deprecated Since 1.0.0-M5, use {@link #builder()} instead + */ + @Deprecated(since = "1.0.0-M5", forRemoval = true) public OracleVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) { this(jdbcTemplate, embeddingModel, DEFAULT_TABLE_NAME, DEFAULT_INDEX_TYPE, DEFAULT_DISTANCE_TYPE, DEFAULT_DIMENSIONS, DEFAULT_SEARCH_ACCURACY, false, false, false); } + /** + * Creates a new OracleVectorStore with schema initialization option. + * @param jdbcTemplate the JDBC template to use + * @param embeddingModel the embedding model to use + * @param initializeSchema whether to initialize the schema + * @deprecated Since 1.0.0-M5, use {@link #builder()} instead + */ + @Deprecated(since = "1.0.0-M5", forRemoval = true) public OracleVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, boolean initializeSchema) { this(jdbcTemplate, embeddingModel, DEFAULT_TABLE_NAME, DEFAULT_INDEX_TYPE, DEFAULT_DISTANCE_TYPE, DEFAULT_DIMENSIONS, DEFAULT_SEARCH_ACCURACY, initializeSchema, false, false); } + /** + * Creates a new OracleVectorStore with full configuration options. + * @param jdbcTemplate the JDBC template to use + * @param embeddingModel the embedding model to use + * @param tableName the table name for vector storage + * @param indexType the type of vector index + * @param distanceType the distance type for similarity calculations + * @param dimensions the number of vector dimensions + * @param searchAccuracy the search accuracy parameter + * @param initializeSchema whether to initialize the schema + * @param removeExistingVectorStoreTable whether to remove existing vector store table + * @param forcedNormalization whether to force vector normalization + * @deprecated Since 1.0.0-M5, use {@link #builder()} instead + */ + @Deprecated(since = "1.0.0-M5", forRemoval = true) public OracleVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, String tableName, OracleVectorStoreIndexType indexType, OracleVectorStoreDistanceType distanceType, int dimensions, int searchAccuracy, boolean initializeSchema, boolean removeExistingVectorStoreTable, @@ -165,43 +195,70 @@ public OracleVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingMode new TokenCountBatchingStrategy()); } + /** + * Creates a new OracleVectorStore with full configuration including observation + * options. + * @param jdbcTemplate the JDBC template to use + * @param embeddingModel the embedding model to use + * @param tableName the table name for vector storage + * @param indexType the type of vector index + * @param distanceType the distance type for similarity calculations + * @param dimensions the number of vector dimensions + * @param searchAccuracy the search accuracy parameter + * @param initializeSchema whether to initialize the schema + * @param removeExistingVectorStoreTable whether to remove existing vector store table + * @param forcedNormalization whether to force vector normalization + * @param observationRegistry the observation registry + * @param customObservationConvention the custom observation convention + * @param batchingStrategy the batching strategy + * @deprecated Since 1.0.0-M5, use {@link #builder()} instead + */ + @Deprecated(since = "1.0.0-M5", forRemoval = true) public OracleVectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, String tableName, OracleVectorStoreIndexType indexType, OracleVectorStoreDistanceType distanceType, int dimensions, int searchAccuracy, boolean initializeSchema, boolean removeExistingVectorStoreTable, boolean forcedNormalization, ObservationRegistry observationRegistry, VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) { - super(observationRegistry, customObservationConvention); - - if (dimensions != DEFAULT_DIMENSIONS) { - if (dimensions <= 0) { - throw new RuntimeException("Number of dimensions must be strictly positive"); - } - if (dimensions > 65535) { - throw new RuntimeException("Number of dimensions must be at most 65535"); - } - } + this(builder().jdbcTemplate(jdbcTemplate) + .embeddingModel(embeddingModel) + .tableName(tableName) + .indexType(indexType) + .distanceType(distanceType) + .dimensions(dimensions) + .searchAccuracy(searchAccuracy) + .initializeSchema(initializeSchema) + .removeExistingVectorStoreTable(removeExistingVectorStoreTable) + .forcedNormalization(forcedNormalization) + .observationRegistry(observationRegistry) + .customObservationConvention(customObservationConvention) + .batchingStrategy(batchingStrategy)); + } - if (searchAccuracy != DEFAULT_SEARCH_ACCURACY) { - if (searchAccuracy < 1) { - throw new RuntimeException("Search accuracy must be greater or equals to 1"); - } - if (searchAccuracy > 100) { - throw new RuntimeException("Search accuracy must be lower or equals to 100"); - } - } + /** + * Protected constructor that accepts a builder instance. This is the preferred way to + * create new OracleVectorStore instances. + * @param builder the configured builder instance + */ + protected OracleVectorStore(OracleBuilder builder) { + super(builder); + + Assert.notNull(builder.jdbcTemplate, "JdbcTemplate must not be null"); + + this.jdbcTemplate = builder.jdbcTemplate; + this.tableName = builder.tableName; + this.indexType = builder.indexType; + this.distanceType = builder.distanceType; + this.dimensions = builder.dimensions; + this.searchAccuracy = builder.searchAccuracy; + this.initializeSchema = builder.initializeSchema; + this.removeExistingVectorStoreTable = builder.removeExistingVectorStoreTable; + this.forcedNormalization = builder.forcedNormalization; + this.batchingStrategy = builder.batchingStrategy; + } - this.jdbcTemplate = jdbcTemplate; - this.embeddingModel = embeddingModel; - this.tableName = tableName; - this.indexType = indexType; - this.distanceType = distanceType; - this.dimensions = dimensions; - this.searchAccuracy = searchAccuracy; - this.initializeSchema = initializeSchema; - this.removeExistingVectorStoreTable = removeExistingVectorStoreTable; - this.forcedNormalization = forcedNormalization; - this.batchingStrategy = batchingStrategy; + public static OracleBuilder builder() { + return new OracleBuilder(); } @Override @@ -386,11 +443,9 @@ public int getBatchSize() { from %s %sorder by distance fetch first %d rows only""", - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? "(1+" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? "(1+" : "", this.distanceType.name(), - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? ")/2" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? ")/2" : "", this.tableName, jsonPathFilter, request.getTopK()) : String.format( """ @@ -398,11 +453,9 @@ public int getBatchSize() { from %s %sorder by distance fetch APPROXIMATE first %d rows only WITH TARGET ACCURACY %d""", - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? "(1+" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? "(1+" : "", this.distanceType.name(), - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? ")/2" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? ")/2" : "", this.tableName, jsonPathFilter, request.getTopK(), this.searchAccuracy); logger.debug("SQL query: " + sql); @@ -419,11 +472,9 @@ else if (request.getSimilarityThreshold() == SIMILARITY_THRESHOLD_EXACT_MATCH) { from %s %sorder by distance fetch EXACT first %d rows only""", - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? "(1+" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? "(1+" : "", this.distanceType.name(), - this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT - ? ")/2" : "", + this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? ")/2" : "", this.tableName, jsonPathFilter, request.getTopK()); logger.debug("SQL query: " + sql); @@ -432,19 +483,19 @@ else if (request.getSimilarityThreshold() == SIMILARITY_THRESHOLD_EXACT_MATCH) { } else { if (!this.forcedNormalization || (this.distanceType != OracleVectorStoreDistanceType.COSINE - && this.distanceType != org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT)) { + && this.distanceType != OracleVectorStore.OracleVectorStoreDistanceType.DOT)) { throw new RuntimeException( "Similarity threshold filtering requires all vectors to be normalized, see the forcedNormalization parameter for this Vector store. Also only COSINE and DOT distance types are supported."); } - final double distance = this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT + final double distance = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? (1d - request.getSimilarityThreshold()) * 2d - 1d : 1d - request.getSimilarityThreshold(); if (StringUtils.hasText(nativeFilterExpression)) { jsonPathFilter = String.format(" and JSON_EXISTS( metadata, '%s' )", nativeFilterExpression); } - final String sql = this.distanceType == org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType.DOT + final String sql = this.distanceType == OracleVectorStore.OracleVectorStoreDistanceType.DOT ? (this.searchAccuracy == DEFAULT_SEARCH_ACCURACY ? String.format( """ @@ -685,4 +736,164 @@ private List toFloatList(final float[] embeddings) { } + /** + * Builder class for creating {@link OracleVectorStore} instances. + *

+ * Provides a fluent API for configuring all aspects of the Oracle vector store, + * including database connection, schema initialization, vector dimensions, and search + * parameters. + * + * @since 1.0.0 + */ + public static class OracleBuilder extends AbstractVectorStoreBuilder { + + private JdbcTemplate jdbcTemplate; + + private String tableName = DEFAULT_TABLE_NAME; + + private OracleVectorStoreIndexType indexType = DEFAULT_INDEX_TYPE; + + private OracleVectorStoreDistanceType distanceType = DEFAULT_DISTANCE_TYPE; + + private int dimensions = DEFAULT_DIMENSIONS; + + private int searchAccuracy = DEFAULT_SEARCH_ACCURACY; + + private boolean initializeSchema = false; + + private boolean removeExistingVectorStoreTable = false; + + private boolean forcedNormalization = false; + + private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy(); + + /** + * Sets the JdbcTemplate to be used for database operations. + * @param jdbcTemplate the JdbcTemplate instance + * @return the builder instance + * @throws IllegalArgumentException if jdbcTemplate is null + */ + public OracleBuilder jdbcTemplate(JdbcTemplate jdbcTemplate) { + Assert.notNull(jdbcTemplate, "JdbcTemplate must not be null"); + this.jdbcTemplate = jdbcTemplate; + return this; + } + + /** + * Sets the table name for vector storage. + * @param tableName the name of the table to use + * @return the builder instance + */ + public OracleBuilder tableName(String tableName) { + if (StringUtils.hasText(tableName)) { + this.tableName = tableName; + } + return this; + } + + /** + * Sets the vector index type. + * @param indexType the index type to use + * @return the builder instance + * @throws IllegalArgumentException if indexType is null + */ + public OracleBuilder indexType(OracleVectorStoreIndexType indexType) { + Assert.notNull(indexType, "Index type must not be null"); + this.indexType = indexType; + return this; + } + + /** + * Sets the distance type for vector similarity calculations. + * @param distanceType the distance type to use + * @return the builder instance + * @throws IllegalArgumentException if distanceType is null + */ + public OracleBuilder distanceType(OracleVectorStoreDistanceType distanceType) { + Assert.notNull(distanceType, "Distance type must not be null"); + this.distanceType = distanceType; + return this; + } + + /** + * Sets the number of dimensions for the vector space. + * @param dimensions the number of dimensions (must be between 1 and 65535) + * @return the builder instance + * @throws IllegalArgumentException if dimensions is not within valid range + */ + public OracleBuilder dimensions(int dimensions) { + if (dimensions != DEFAULT_DIMENSIONS) { + Assert.isTrue(dimensions > 0 && dimensions <= 65535, + "Number of dimensions must be between 1 and 65535"); + } + this.dimensions = dimensions; + return this; + } + + /** + * Sets the search accuracy parameter. + * @param searchAccuracy the search accuracy value (must be between 1 and 100) + * @return the builder instance + * @throws IllegalArgumentException if searchAccuracy is not within valid range + */ + public OracleBuilder searchAccuracy(int searchAccuracy) { + if (searchAccuracy != DEFAULT_SEARCH_ACCURACY) { + Assert.isTrue(searchAccuracy >= 1 && searchAccuracy <= 100, + "Search accuracy must be between 1 and 100"); + } + this.searchAccuracy = searchAccuracy; + return this; + } + + /** + * Sets whether to initialize the database schema. + * @param initializeSchema true to initialize schema, false otherwise + * @return the builder instance + */ + public OracleBuilder initializeSchema(boolean initializeSchema) { + this.initializeSchema = initializeSchema; + return this; + } + + /** + * Sets whether to remove existing vector store table before initialization. + * @param removeExistingVectorStoreTable true to remove existing table, false + * otherwise + * @return the builder instance + */ + public OracleBuilder removeExistingVectorStoreTable(boolean removeExistingVectorStoreTable) { + this.removeExistingVectorStoreTable = removeExistingVectorStoreTable; + return this; + } + + /** + * Sets whether to force vector normalization. + * @param forcedNormalization true to force normalization, false otherwise + * @return the builder instance + */ + public OracleBuilder forcedNormalization(boolean forcedNormalization) { + this.forcedNormalization = forcedNormalization; + return this; + } + + /** + * Sets the batching strategy for vector operations. + * @param batchingStrategy the strategy to use + * @return the builder instance + * @throws IllegalArgumentException if batchingStrategy is null + */ + public OracleBuilder batchingStrategy(BatchingStrategy batchingStrategy) { + Assert.notNull(batchingStrategy, "BatchingStrategy must not be null"); + this.batchingStrategy = batchingStrategy; + return this; + } + + @Override + public OracleVectorStore build() { + validate(); + return new OracleVectorStore(this); + } + + } + } diff --git a/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverter.java b/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverter.java similarity index 98% rename from vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverter.java rename to vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverter.java index 69f61f6ec86..50cd3435e7f 100644 --- a/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverter.java +++ b/vector-stores/spring-ai-oracle-store/src/main/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverter.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import org.springframework.ai.vectorstore.filter.Filter; import org.springframework.ai.vectorstore.filter.converter.AbstractFilterExpressionConverter; diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleImage.java similarity index 94% rename from vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java rename to vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleImage.java index 85816879f4d..1520094aef7 100644 --- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleImage.java +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleImage.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import org.testcontainers.utility.DockerImageName; diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreIT.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java similarity index 93% rename from vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreIT.java rename to vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java index 2c3fcee85c3..3130ff16b78 100644 --- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreIT.java +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -41,6 +41,8 @@ import org.springframework.ai.document.Document; import org.springframework.ai.embedding.EmbeddingModel; import org.springframework.ai.transformers.TransformersEmbeddingModel; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; import org.springframework.ai.vectorstore.filter.FilterExpressionTextParser; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.SpringBootConfiguration; @@ -119,8 +121,8 @@ private static boolean isSortedBySimilarity(final List documents) { @ValueSource(strings = { "COSINE", "DOT", "EUCLIDEAN", "EUCLIDEAN_SQUARED", "MANHATTAN" }) public void addAndSearch(String distanceType) { this.contextRunner.withPropertyValues("test.spring.ai.vectorstore.oracle.distanceType=" + distanceType) - .withPropertyValues("test.spring.ai.vectorstore.oracle.searchAccuracy=" - + org.springframework.ai.vectorstore.OracleVectorStore.DEFAULT_SEARCH_ACCURACY) + .withPropertyValues( + "test.spring.ai.vectorstore.oracle.searchAccuracy=" + OracleVectorStore.DEFAULT_SEARCH_ACCURACY) .run(context -> { VectorStore vectorStore = context.getBean(VectorStore.class); @@ -225,8 +227,8 @@ public void searchWithFilters(String distanceType, int searchAccuracy) { @ValueSource(strings = { "COSINE", "DOT", "EUCLIDEAN", "EUCLIDEAN_SQUARED", "MANHATTAN" }) public void documentUpdate(String distanceType) { this.contextRunner.withPropertyValues("test.spring.ai.vectorstore.oracle.distanceType=" + distanceType) - .withPropertyValues("test.spring.ai.vectorstore.oracle.searchAccuracy=" - + org.springframework.ai.vectorstore.OracleVectorStore.DEFAULT_SEARCH_ACCURACY) + .withPropertyValues( + "test.spring.ai.vectorstore.oracle.searchAccuracy=" + OracleVectorStore.DEFAULT_SEARCH_ACCURACY) .run(context -> { VectorStore vectorStore = context.getBean(VectorStore.class); @@ -265,8 +267,8 @@ public void documentUpdate(String distanceType) { @ValueSource(strings = { "COSINE", "DOT" }) public void searchWithThreshold(String distanceType) { this.contextRunner.withPropertyValues("test.spring.ai.vectorstore.oracle.distanceType=" + distanceType) - .withPropertyValues("test.spring.ai.vectorstore.oracle.searchAccuracy=" - + org.springframework.ai.vectorstore.OracleVectorStore.DEFAULT_SEARCH_ACCURACY) + .withPropertyValues( + "test.spring.ai.vectorstore.oracle.searchAccuracy=" + OracleVectorStore.DEFAULT_SEARCH_ACCURACY) .run(context -> { VectorStore vectorStore = context.getBean(VectorStore.class); @@ -308,9 +310,18 @@ public static class TestClient { @Bean public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) { - return new OracleVectorStore(jdbcTemplate, embeddingModel, OracleVectorStore.DEFAULT_TABLE_NAME, - OracleVectorStore.OracleVectorStoreIndexType.IVF, this.distanceType, 384, this.searchAccuracy, true, - true, true); + return OracleVectorStore.builder() + .jdbcTemplate(jdbcTemplate) + .embeddingModel(embeddingModel) + .tableName(OracleVectorStore.DEFAULT_TABLE_NAME) + .indexType(OracleVectorStore.OracleVectorStoreIndexType.IVF) + .distanceType(distanceType) + .dimensions(384) + .searchAccuracy(searchAccuracy) + .initializeSchema(true) + .removeExistingVectorStoreTable(true) + .forcedNormalization(true) + .build(); } @Bean diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java similarity index 91% rename from vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java rename to vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java index 50a496e448f..cd566647a63 100644 --- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/OracleVectorStoreObservationIT.java +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/OracleVectorStoreObservationIT.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import java.io.IOException; import java.nio.charset.StandardCharsets; @@ -40,7 +40,9 @@ import org.springframework.ai.observation.conventions.VectorStoreProvider; import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric; import org.springframework.ai.transformers.TransformersEmbeddingModel; -import org.springframework.ai.vectorstore.OracleVectorStore.OracleVectorStoreDistanceType; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.ai.vectorstore.oracle.OracleVectorStore.OracleVectorStoreDistanceType; import org.springframework.ai.vectorstore.observation.DefaultVectorStoreObservationConvention; import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.HighCardinalityKeyNames; import org.springframework.ai.vectorstore.observation.VectorStoreObservationDocumentation.LowCardinalityKeyNames; @@ -185,10 +187,21 @@ public TestObservationRegistry observationRegistry() { @Bean public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, ObservationRegistry observationRegistry) { - return new OracleVectorStore(jdbcTemplate, embeddingModel, OracleVectorStore.DEFAULT_TABLE_NAME, - OracleVectorStore.OracleVectorStoreIndexType.IVF, OracleVectorStoreDistanceType.COSINE, 384, - OracleVectorStore.DEFAULT_SEARCH_ACCURACY, true, true, true, observationRegistry, null, - new TokenCountBatchingStrategy()); + return OracleVectorStore.builder() + .jdbcTemplate(jdbcTemplate) + .embeddingModel(embeddingModel) + .tableName(OracleVectorStore.DEFAULT_TABLE_NAME) + .indexType(OracleVectorStore.OracleVectorStoreIndexType.IVF) + .distanceType(OracleVectorStoreDistanceType.COSINE) + .dimensions(384) + .searchAccuracy(OracleVectorStore.DEFAULT_SEARCH_ACCURACY) + .initializeSchema(true) + .removeExistingVectorStoreTable(true) + .forcedNormalization(true) + .observationRegistry(observationRegistry) + .customObservationConvention(null) + .batchingStrategy(new TokenCountBatchingStrategy()) + .build(); } @Bean diff --git a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverterTests.java b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverterTests.java similarity index 96% rename from vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverterTests.java rename to vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverterTests.java index 165e0a853c5..5e14cbc03b1 100644 --- a/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/SqlJsonPathFilterExpressionConverterTests.java +++ b/vector-stores/spring-ai-oracle-store/src/test/java/org/springframework/ai/vectorstore/oracle/SqlJsonPathFilterExpressionConverterTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.ai.vectorstore; +package org.springframework.ai.vectorstore.oracle; import org.junit.jupiter.api.Test;