Skip to content

Commit 138e11a

Browse files
sobychackomarkpollack
authored andcommitted
Add builder pattern to AzureVectorStore and refactor package name
1 parent 8c28c6d commit 138e11a

File tree

4 files changed

+207
-49
lines changed

4 files changed

+207
-49
lines changed

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/azure/AzureVectorStoreAutoConfiguration.java

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,25 @@ public AzureVectorStore vectorStore(SearchIndexClient searchIndexClient, Embeddi
8484
ObjectProvider<VectorStoreObservationConvention> customObservationConvention,
8585
BatchingStrategy batchingStrategy) {
8686

87-
var vectorStore = new AzureVectorStore(searchIndexClient, embeddingModel, properties.isInitializeSchema(),
88-
List.of(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
89-
customObservationConvention.getIfAvailable(() -> null), batchingStrategy);
90-
91-
vectorStore.setIndexName(properties.getIndexName());
87+
var builder = AzureVectorStore.builder()
88+
.searchIndexClient(searchIndexClient)
89+
.embeddingModel(embeddingModel)
90+
.initializeSchema(properties.isInitializeSchema())
91+
.filterMetadataFields(List.of())
92+
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
93+
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
94+
.batchingStrategy(batchingStrategy)
95+
.indexName(properties.getIndexName());
9296

9397
if (properties.getDefaultTopK() >= 0) {
94-
vectorStore.setDefaultTopK(properties.getDefaultTopK());
98+
builder.defaultTopK(properties.getDefaultTopK());
9599
}
96100

97101
if (properties.getDefaultSimilarityThreshold() >= 0.0) {
98-
vectorStore.setDefaultSimilarityThreshold(properties.getDefaultSimilarityThreshold());
102+
builder.defaultSimilarityThreshold(properties.getDefaultSimilarityThreshold());
99103
}
100104

101-
return vectorStore;
105+
return builder.build();
102106
}
103107

104108
}

vector-stores/spring-ai-azure-store/src/main/java/org/springframework/ai/vectorstore/azure/AzureVectorStore.java

Lines changed: 178 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.springframework.ai.model.EmbeddingUtils;
5656
import org.springframework.ai.observation.conventions.VectorStoreProvider;
5757
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
58+
import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder;
5859
import org.springframework.ai.vectorstore.SearchRequest;
5960
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
6061
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
@@ -104,8 +105,6 @@ public class AzureVectorStore extends AbstractObservationVectorStore implements
104105

105106
private final SearchIndexClient searchIndexClient;
106107

107-
private final EmbeddingModel embeddingModel;
108-
109108
private final FilterExpressionConverter filterExpressionConverter;
110109

111110
private final boolean initializeSchema;
@@ -125,70 +124,96 @@ public class AzureVectorStore extends AbstractObservationVectorStore implements
125124

126125
private SearchClient searchClient;
127126

128-
private int defaultTopK = DEFAULT_TOP_K;
127+
private int defaultTopK;
129128

130-
private Double defaultSimilarityThreshold = DEFAULT_SIMILARITY_THRESHOLD;
129+
private Double defaultSimilarityThreshold;
131130

132-
private String indexName = DEFAULT_INDEX_NAME;
131+
private String indexName;
133132

134133
/**
135-
* Constructs a new AzureCognitiveSearchVectorStore.
136-
* @param searchIndexClient A pre-configured Azure {@link SearchIndexClient} that CRUD
137-
* for Azure search indexes and factory for {@link SearchClient}.
138-
* @param embeddingModel The client for embedding operations.
134+
* Creates a new AzureVectorStore with basic configuration.
135+
* @param searchIndexClient the Azure search index client
136+
* @param embeddingModel the embedding model to use
137+
* @param initializeSchema whether to initialize schema
138+
* @deprecated Since 1.0.0-M5, use {@link #builder()} instead
139139
*/
140+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
140141
public AzureVectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel,
141142
boolean initializeSchema) {
142143
this(searchIndexClient, embeddingModel, initializeSchema, List.of());
143144
}
144145

145146
/**
146-
* Constructs a new AzureCognitiveSearchVectorStore.
147-
* @param searchIndexClient A pre-configured Azure {@link SearchIndexClient} that CRUD
148-
* for Azure search indexes and factory for {@link SearchClient}.
149-
* @param embeddingModel The client for embedding operations.
150-
* @param filterMetadataFields List of metadata fields (as field name and type) that
151-
* can be used in similarity search query filter expressions.
147+
* Creates a new AzureVectorStore with metadata fields configuration.
148+
* @param searchIndexClient the Azure search index client
149+
* @param embeddingModel the embedding model to use
150+
* @param initializeSchema whether to initialize schema
151+
* @param filterMetadataFields list of metadata fields for filtering
152+
* @deprecated Since 1.0.0-M5, use {@link #builder()} instead
152153
*/
154+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
153155
public AzureVectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel,
154156
boolean initializeSchema, List<MetadataField> filterMetadataFields) {
155157
this(searchIndexClient, embeddingModel, initializeSchema, filterMetadataFields, ObservationRegistry.NOOP, null,
156158
new TokenCountBatchingStrategy());
157159
}
158160

159161
/**
160-
* Constructs a new AzureCognitiveSearchVectorStore.
161-
* @param searchIndexClient A pre-configured Azure {@link SearchIndexClient} that CRUD
162-
* for Azure search indexes and factory for {@link SearchClient}.
163-
* @param embeddingModel The client for embedding operations.
164-
* @param filterMetadataFields List of metadata fields (as field name and type) that
165-
* can be used in similarity search query filter expressions.
166-
* @param observationRegistry The observation registry to use.
167-
* @param customObservationConvention The optional, custom search observation
168-
* convention to use.
162+
* Creates a new AzureVectorStore with full configuration.
163+
* @param searchIndexClient the Azure search index client
164+
* @param embeddingModel the embedding model to use
165+
* @param initializeSchema whether to initialize schema
166+
* @param filterMetadataFields list of metadata fields for filtering
167+
* @param observationRegistry the observation registry
168+
* @param customObservationConvention the custom observation convention
169+
* @deprecated Since 1.0.0-M5, use {@link #builder()} instead
169170
*/
171+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
170172
public AzureVectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel,
171173
boolean initializeSchema, List<MetadataField> filterMetadataFields, ObservationRegistry observationRegistry,
172174
VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
173175

174-
super(observationRegistry, customObservationConvention);
175-
176-
Assert.notNull(embeddingModel, "The embedding model can not be null.");
177-
Assert.notNull(searchIndexClient, "The search index client can not be null.");
178-
Assert.notNull(filterMetadataFields, "The filterMetadataFields can not be null.");
176+
this(builder().searchIndexClient(searchIndexClient)
177+
.embeddingModel(embeddingModel)
178+
.initializeSchema(initializeSchema)
179+
.filterMetadataFields(filterMetadataFields)
180+
.observationRegistry(observationRegistry)
181+
.customObservationConvention(customObservationConvention)
182+
.batchingStrategy(batchingStrategy));
183+
}
179184

180-
this.initializeSchema = initializeSchema;
181-
this.searchIndexClient = searchIndexClient;
182-
this.embeddingModel = embeddingModel;
183-
this.filterMetadataFields = filterMetadataFields;
185+
/**
186+
* Protected constructor that accepts a builder instance. This is the preferred way to
187+
* create new AzureVectorStore instances.
188+
* @param builder the configured builder instance
189+
*/
190+
protected AzureVectorStore(AzureBuilder builder) {
191+
super(builder);
192+
193+
Assert.notNull(builder.searchIndexClient, "The search index client cannot be null");
194+
Assert.notNull(builder.filterMetadataFields, "The filterMetadataFields cannot be null");
195+
196+
this.searchIndexClient = builder.searchIndexClient;
197+
this.initializeSchema = builder.initializeSchema;
198+
this.filterMetadataFields = builder.filterMetadataFields;
199+
this.batchingStrategy = builder.batchingStrategy;
200+
this.defaultTopK = builder.defaultTopK;
201+
this.defaultSimilarityThreshold = builder.defaultSimilarityThreshold;
202+
this.indexName = builder.indexName;
184203
this.filterExpressionConverter = new AzureAiSearchFilterExpressionConverter(filterMetadataFields);
185-
this.batchingStrategy = batchingStrategy;
204+
}
205+
206+
public static AzureBuilder builder() {
207+
return new AzureBuilder();
186208
}
187209

188210
/**
189211
* Change the Index Name.
190212
* @param indexName The Azure VectorStore index name to use.
213+
* @deprecated Since 1.0.0-M5, use {@link #builder()} with
214+
* {@link AzureBuilder#indexName(String)} instead
191215
*/
216+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
192217
public void setIndexName(String indexName) {
193218
Assert.hasText(indexName, "The index name can not be empty.");
194219
this.indexName = indexName;
@@ -197,7 +222,10 @@ public void setIndexName(String indexName) {
197222
/**
198223
* Sets the a default maximum number of similar documents returned.
199224
* @param topK The default maximum number of similar documents returned.
225+
* @deprecated Since 1.0.0-M5, use {@link #builder()} with
226+
* {@link AzureBuilder#indexName(String)} instead
200227
*/
228+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
201229
public void setDefaultTopK(int topK) {
202230
Assert.isTrue(topK >= 0, "The topK should be positive value.");
203231
this.defaultTopK = topK;
@@ -207,7 +235,10 @@ public void setDefaultTopK(int topK) {
207235
* Sets the a default similarity threshold for returned documents.
208236
* @param similarityThreshold The a default similarity threshold for returned
209237
* documents.
238+
* @deprecated Since 1.0.0-M5, use {@link #builder()} with
239+
* {@link AzureBuilder#indexName(String)} instead
210240
*/
241+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
211242
public void setDefaultSimilarityThreshold(Double similarityThreshold) {
212243
Assert.isTrue(similarityThreshold >= 0.0 && similarityThreshold <= 1.0,
213244
"The similarity threshold must be in range [0.0:1.00].");
@@ -429,4 +460,117 @@ private record AzureSearchDocument(String id, String content, List<Float> embedd
429460

430461
}
431462

463+
/**
464+
* Builder class for creating {@link AzureVectorStore} instances.
465+
* <p>
466+
* Provides a fluent API for configuring all aspects of the Azure vector store.
467+
*
468+
* @since 1.0.0
469+
*/
470+
public static class AzureBuilder extends AbstractVectorStoreBuilder<AzureBuilder> {
471+
472+
private SearchIndexClient searchIndexClient;
473+
474+
private boolean initializeSchema = false;
475+
476+
private List<MetadataField> filterMetadataFields = List.of();
477+
478+
private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy();
479+
480+
private int defaultTopK = DEFAULT_TOP_K;
481+
482+
private Double defaultSimilarityThreshold = DEFAULT_SIMILARITY_THRESHOLD;
483+
484+
private String indexName = DEFAULT_INDEX_NAME;
485+
486+
/**
487+
* Sets the Azure search index client.
488+
* @param searchIndexClient the client to use
489+
* @return the builder instance
490+
* @throws IllegalArgumentException if searchIndexClient is null
491+
*/
492+
public AzureBuilder searchIndexClient(SearchIndexClient searchIndexClient) {
493+
Assert.notNull(searchIndexClient, "SearchIndexClient must not be null");
494+
this.searchIndexClient = searchIndexClient;
495+
return this;
496+
}
497+
498+
/**
499+
* Sets whether to initialize the schema.
500+
* @param initializeSchema true to initialize schema, false otherwise
501+
* @return the builder instance
502+
*/
503+
public AzureBuilder initializeSchema(boolean initializeSchema) {
504+
this.initializeSchema = initializeSchema;
505+
return this;
506+
}
507+
508+
/**
509+
* Sets the metadata fields for filtering.
510+
* @param filterMetadataFields the list of metadata fields
511+
* @return the builder instance
512+
*/
513+
public AzureBuilder filterMetadataFields(List<MetadataField> filterMetadataFields) {
514+
this.filterMetadataFields = filterMetadataFields != null ? filterMetadataFields : List.of();
515+
return this;
516+
}
517+
518+
/**
519+
* Sets the batching strategy.
520+
* @param batchingStrategy the strategy to use
521+
* @return the builder instance
522+
*/
523+
public AzureBuilder batchingStrategy(BatchingStrategy batchingStrategy) {
524+
Assert.notNull(batchingStrategy, "BatchingStrategy must not be null");
525+
this.batchingStrategy = batchingStrategy;
526+
return this;
527+
}
528+
529+
/**
530+
* Sets the index name for the Azure Vector Store.
531+
* @param indexName the name of the index to use
532+
* @return the builder instance
533+
* @throws IllegalArgumentException if indexName is null or empty
534+
*/
535+
public AzureBuilder indexName(String indexName) {
536+
Assert.hasText(indexName, "The index name can not be empty.");
537+
this.indexName = indexName;
538+
return this;
539+
}
540+
541+
/**
542+
* Sets the default maximum number of similar documents to return.
543+
* @param defaultTopK the maximum number of documents
544+
* @return the builder instance
545+
* @throws IllegalArgumentException if defaultTopK is negative
546+
*/
547+
public AzureBuilder defaultTopK(int defaultTopK) {
548+
Assert.isTrue(defaultTopK >= 0, "The topK should be positive value.");
549+
this.defaultTopK = defaultTopK;
550+
return this;
551+
}
552+
553+
/**
554+
* Sets the default similarity threshold for returned documents.
555+
* @param defaultSimilarityThreshold the similarity threshold (must be between 0.0
556+
* and 1.0)
557+
* @return the builder instance
558+
* @throws IllegalArgumentException if defaultSimilarityThreshold is not between
559+
* 0.0 and 1.0
560+
*/
561+
public AzureBuilder defaultSimilarityThreshold(Double defaultSimilarityThreshold) {
562+
Assert.isTrue(defaultSimilarityThreshold >= 0.0 && defaultSimilarityThreshold <= 1.0,
563+
"The similarity threshold must be in range [0.0:1.00].");
564+
this.defaultSimilarityThreshold = defaultSimilarityThreshold;
565+
return this;
566+
}
567+
568+
@Override
569+
public AzureVectorStore build() {
570+
validate();
571+
return new AzureVectorStore(this);
572+
}
573+
574+
}
575+
432576
}

vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreIT.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,9 +310,13 @@ public SearchIndexClient searchIndexClient() {
310310

311311
@Bean
312312
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel) {
313-
var filterableMetaFields = List.of(MetadataField.text("country"), MetadataField.int64("year"),
314-
MetadataField.date("activationDate"));
315-
return new AzureVectorStore(searchIndexClient, embeddingModel, true, filterableMetaFields);
313+
return AzureVectorStore.builder()
314+
.searchIndexClient(searchIndexClient)
315+
.embeddingModel(embeddingModel)
316+
.initializeSchema(true)
317+
.filterMetadataFields(List.of(MetadataField.text("country"), MetadataField.int64("year"),
318+
MetadataField.date("activationDate")))
319+
.build();
316320
}
317321

318322
@Bean

vector-stores/spring-ai-azure-store/src/test/java/org/springframework/ai/vectorstore/azure/AzureVectorStoreObservationIT.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,16 @@ public SearchIndexClient searchIndexClient() {
183183
@Bean
184184
public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingModel embeddingModel,
185185
ObservationRegistry observationRegistry) {
186-
var filterableMetaFields = List.of(MetadataField.text("country"), MetadataField.int64("year"),
187-
MetadataField.date("activationDate"));
188-
return new AzureVectorStore(searchIndexClient, embeddingModel, true, filterableMetaFields,
189-
observationRegistry, null, new TokenCountBatchingStrategy());
186+
return AzureVectorStore.builder()
187+
.searchIndexClient(searchIndexClient)
188+
.embeddingModel(embeddingModel)
189+
.initializeSchema(true)
190+
.filterMetadataFields(List.of(MetadataField.text("country"), MetadataField.int64("year"),
191+
MetadataField.date("activationDate")))
192+
.observationRegistry(observationRegistry)
193+
.customObservationConvention(null)
194+
.batchingStrategy(new TokenCountBatchingStrategy())
195+
.build();
190196
}
191197

192198
@Bean

0 commit comments

Comments
 (0)