Skip to content

Commit a1a2e00

Browse files
committed
refactor: move Elasticsearch classes to dedicated package and use builder pattern
1 parent e2825c9 commit a1a2e00

File tree

11 files changed

+157
-33
lines changed

11 files changed

+157
-33
lines changed

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreAutoConfiguration.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
import org.springframework.ai.embedding.BatchingStrategy;
2323
import org.springframework.ai.embedding.EmbeddingModel;
2424
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
25-
import org.springframework.ai.vectorstore.ElasticsearchVectorStore;
26-
import org.springframework.ai.vectorstore.ElasticsearchVectorStoreOptions;
25+
import org.springframework.ai.elasticsearch.vectorstore.ElasticsearchVectorStore;
26+
import org.springframework.ai.elasticsearch.vectorstore.ElasticsearchVectorStoreOptions;
2727
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
2828
import org.springframework.beans.factory.ObjectProvider;
2929
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -73,9 +73,14 @@ ElasticsearchVectorStore vectorStore(ElasticsearchVectorStoreProperties properti
7373
elasticsearchVectorStoreOptions.setSimilarity(properties.getSimilarity());
7474
}
7575

76-
return new ElasticsearchVectorStore(elasticsearchVectorStoreOptions, restClient, embeddingModel,
77-
properties.isInitializeSchema(), observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
78-
customObservationConvention.getIfAvailable(() -> null), batchingStrategy);
76+
return ElasticsearchVectorStore.builder(restClient)
77+
.options(elasticsearchVectorStoreOptions)
78+
.embeddingModel(embeddingModel)
79+
.initializeSchema(properties.isInitializeSchema())
80+
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
81+
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
82+
.batchingStrategy(batchingStrategy)
83+
.build();
7984
}
8085

8186
}

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreProperties.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.ai.autoconfigure.vectorstore.elasticsearch;
1818

1919
import org.springframework.ai.autoconfigure.vectorstore.CommonVectorStoreProperties;
20-
import org.springframework.ai.vectorstore.SimilarityFunction;
20+
import org.springframework.ai.elasticsearch.vectorstore.SimilarityFunction;
2121
import org.springframework.boot.context.properties.ConfigurationProperties;
2222

2323
/**

spring-ai-spring-boot-autoconfigure/src/test/java/org/springframework/ai/autoconfigure/vectorstore/elasticsearch/ElasticsearchVectorStoreAutoConfigurationIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@
3333
import org.springframework.ai.autoconfigure.retry.SpringAiRetryAutoConfiguration;
3434
import org.springframework.ai.document.Document;
3535
import org.springframework.ai.observation.conventions.VectorStoreProvider;
36-
import org.springframework.ai.vectorstore.ElasticsearchVectorStore;
36+
import org.springframework.ai.elasticsearch.vectorstore.ElasticsearchVectorStore;
3737
import org.springframework.ai.vectorstore.SearchRequest;
38-
import org.springframework.ai.vectorstore.SimilarityFunction;
38+
import org.springframework.ai.elasticsearch.vectorstore.SimilarityFunction;
3939
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
4040
import org.springframework.boot.autoconfigure.AutoConfigurations;
4141
import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
import java.text.ParseException;
2020
import java.text.SimpleDateFormat;
Lines changed: 111 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
import java.io.IOException;
2020
import java.util.List;
@@ -48,11 +48,12 @@
4848
import org.springframework.ai.model.EmbeddingUtils;
4949
import org.springframework.ai.observation.conventions.VectorStoreProvider;
5050
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
51+
import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder;
52+
import org.springframework.ai.vectorstore.SearchRequest;
5153
import org.springframework.ai.vectorstore.filter.Filter;
5254
import org.springframework.ai.vectorstore.filter.FilterExpressionConverter;
5355
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
5456
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
55-
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext.Builder;
5657
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
5758
import org.springframework.beans.factory.InitializingBean;
5859
import org.springframework.util.Assert;
@@ -83,8 +84,6 @@ public class ElasticsearchVectorStore extends AbstractObservationVectorStore imp
8384
SimilarityFunction.cosine, VectorStoreSimilarityMetric.COSINE, SimilarityFunction.l2_norm,
8485
VectorStoreSimilarityMetric.EUCLIDEAN, SimilarityFunction.dot_product, VectorStoreSimilarityMetric.DOT);
8586

86-
private final EmbeddingModel embeddingModel;
87-
8887
private final ElasticsearchClient elasticsearchClient;
8988

9089
private final ElasticsearchVectorStoreOptions options;
@@ -95,34 +94,43 @@ public class ElasticsearchVectorStore extends AbstractObservationVectorStore imp
9594

9695
private final BatchingStrategy batchingStrategy;
9796

97+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
9898
public ElasticsearchVectorStore(RestClient restClient, EmbeddingModel embeddingModel, boolean initializeSchema) {
9999
this(new ElasticsearchVectorStoreOptions(), restClient, embeddingModel, initializeSchema);
100100
}
101101

102+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
102103
public ElasticsearchVectorStore(ElasticsearchVectorStoreOptions options, RestClient restClient,
103104
EmbeddingModel embeddingModel, boolean initializeSchema) {
104105
this(options, restClient, embeddingModel, initializeSchema, ObservationRegistry.NOOP, null,
105106
new TokenCountBatchingStrategy());
106107
}
107108

109+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
108110
public ElasticsearchVectorStore(ElasticsearchVectorStoreOptions options, RestClient restClient,
109111
EmbeddingModel embeddingModel, boolean initializeSchema, ObservationRegistry observationRegistry,
110112
VectorStoreObservationConvention customObservationConvention, BatchingStrategy batchingStrategy) {
111113

112-
super(observationRegistry, customObservationConvention);
114+
this(builder(restClient).options(options)
115+
.embeddingModel(embeddingModel)
116+
.initializeSchema(initializeSchema)
117+
.observationRegistry(observationRegistry)
118+
.customObservationConvention(customObservationConvention)
119+
.batchingStrategy(batchingStrategy));
120+
}
121+
122+
private ElasticsearchVectorStore(ElasticsearchBuilder builder) {
123+
super(builder);
124+
this.initializeSchema = builder.initializeSchema;
125+
this.options = builder.options;
126+
this.filterExpressionConverter = builder.filterExpressionConverter;
127+
this.batchingStrategy = builder.batchingStrategy;
113128

114-
this.initializeSchema = initializeSchema;
115-
Objects.requireNonNull(embeddingModel, "RestClient must not be null");
116-
Objects.requireNonNull(embeddingModel, "EmbeddingModel must not be null");
117129
String version = Version.VERSION == null ? "Unknown" : Version.VERSION.toString();
118-
this.elasticsearchClient = new ElasticsearchClient(new RestClientTransport(restClient,
130+
this.elasticsearchClient = new ElasticsearchClient(new RestClientTransport(builder.restClient,
119131
new JacksonJsonpMapper(
120132
new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false))))
121133
.withTransportOptions(t -> t.addHeader("user-agent", "spring-ai elastic-java/" + version));
122-
this.embeddingModel = embeddingModel;
123-
this.options = options;
124-
this.filterExpressionConverter = new ElasticsearchAiSearchFilterExpressionConverter();
125-
this.batchingStrategy = batchingStrategy;
126134
}
127135

128136
@Override
@@ -297,4 +305,94 @@ private String getSimilarityMetric() {
297305
public record ElasticSearchDocument(String id, String content, Map<String, Object> metadata, float[] embedding) {
298306
}
299307

308+
/**
309+
* Creates a new builder instance for ElasticsearchVectorStore.
310+
* @param restClient the Elasticsearch REST client
311+
* @return a new ElasticsearchBuilder instance
312+
*/
313+
public static ElasticsearchBuilder builder(RestClient restClient) {
314+
return new ElasticsearchBuilder(restClient);
315+
}
316+
317+
public static class ElasticsearchBuilder extends AbstractVectorStoreBuilder<ElasticsearchBuilder> {
318+
319+
private final RestClient restClient;
320+
321+
private ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
322+
323+
private boolean initializeSchema = false;
324+
325+
private BatchingStrategy batchingStrategy = new TokenCountBatchingStrategy();
326+
327+
private FilterExpressionConverter filterExpressionConverter = new ElasticsearchAiSearchFilterExpressionConverter();
328+
329+
/**
330+
* Creates a new builder instance with the specified REST client.
331+
* @param restClient the Elasticsearch REST client
332+
* @throws IllegalArgumentException if restClient is null
333+
*/
334+
public ElasticsearchBuilder(RestClient restClient) {
335+
Assert.notNull(restClient, "RestClient must not be null");
336+
this.restClient = restClient;
337+
}
338+
339+
/**
340+
* Sets the Elasticsearch vector store options.
341+
* @param options the vector store options to use
342+
* @return the builder instance
343+
* @throws IllegalArgumentException if options is null
344+
*/
345+
public ElasticsearchBuilder options(ElasticsearchVectorStoreOptions options) {
346+
Assert.notNull(options, "options must not be null");
347+
this.options = options;
348+
return this;
349+
}
350+
351+
/**
352+
* Sets whether to initialize the schema.
353+
* @param initializeSchema true to initialize schema, false otherwise
354+
* @return the builder instance
355+
*/
356+
public ElasticsearchBuilder initializeSchema(boolean initializeSchema) {
357+
this.initializeSchema = initializeSchema;
358+
return this;
359+
}
360+
361+
/**
362+
* Sets the batching strategy for vector operations.
363+
* @param batchingStrategy the batching strategy to use
364+
* @return the builder instance
365+
* @throws IllegalArgumentException if batchingStrategy is null
366+
*/
367+
public ElasticsearchBuilder batchingStrategy(BatchingStrategy batchingStrategy) {
368+
Assert.notNull(batchingStrategy, "batchingStrategy must not be null");
369+
this.batchingStrategy = batchingStrategy;
370+
return this;
371+
}
372+
373+
/**
374+
* Sets the filter expression converter.
375+
* @param converter the filter expression converter to use
376+
* @return the builder instance
377+
* @throws IllegalArgumentException if converter is null
378+
*/
379+
public ElasticsearchBuilder filterExpressionConverter(FilterExpressionConverter converter) {
380+
Assert.notNull(converter, "filterExpressionConverter must not be null");
381+
this.filterExpressionConverter = converter;
382+
return this;
383+
}
384+
385+
/**
386+
* Builds the ElasticsearchVectorStore instance.
387+
* @return a new ElasticsearchVectorStore instance
388+
* @throws IllegalStateException if the builder is in an invalid state
389+
*/
390+
@Override
391+
public ElasticsearchVectorStore build() {
392+
validate();
393+
return new ElasticsearchVectorStore(this);
394+
}
395+
396+
}
397+
300398
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
/**
2020
* Provided Elasticsearch vector option configuration.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
/**
2020
* https://www.elastic.co/guide/en/elasticsearch/reference/master/dense-vector.html
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
import java.util.Date;
2020
import java.util.List;
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
import org.testcontainers.utility.DockerImageName;
2020

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.ai.vectorstore;
17+
package org.springframework.ai.elasticsearch.vectorstore;
1818

1919
import java.io.IOException;
2020
import java.nio.charset.StandardCharsets;
@@ -51,6 +51,7 @@
5151
import org.springframework.ai.embedding.EmbeddingModel;
5252
import org.springframework.ai.openai.OpenAiEmbeddingModel;
5353
import org.springframework.ai.openai.api.OpenAiApi;
54+
import org.springframework.ai.vectorstore.SearchRequest;
5455
import org.springframework.boot.SpringBootConfiguration;
5556
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5657
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -376,23 +377,34 @@ public static class TestApplication {
376377

377378
@Bean("vectorStore_cosine")
378379
public ElasticsearchVectorStore vectorStoreDefault(EmbeddingModel embeddingModel, RestClient restClient) {
379-
return new ElasticsearchVectorStore(restClient, embeddingModel, true);
380+
return ElasticsearchVectorStore.builder(restClient)
381+
.embeddingModel(embeddingModel)
382+
.initializeSchema(true)
383+
.build();
380384
}
381385

382386
@Bean("vectorStore_l2_norm")
383387
public ElasticsearchVectorStore vectorStoreL2(EmbeddingModel embeddingModel, RestClient restClient) {
384388
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
385389
options.setIndexName("index_l2");
386390
options.setSimilarity(SimilarityFunction.l2_norm);
387-
return new ElasticsearchVectorStore(options, restClient, embeddingModel, true);
391+
return ElasticsearchVectorStore.builder(restClient)
392+
.embeddingModel(embeddingModel)
393+
.initializeSchema(true)
394+
.options(options)
395+
.build();
388396
}
389397

390398
@Bean("vectorStore_dot_product")
391399
public ElasticsearchVectorStore vectorStoreDotProduct(EmbeddingModel embeddingModel, RestClient restClient) {
392400
ElasticsearchVectorStoreOptions options = new ElasticsearchVectorStoreOptions();
393401
options.setIndexName("index_dot_product");
394402
options.setSimilarity(SimilarityFunction.dot_product);
395-
return new ElasticsearchVectorStore(options, restClient, embeddingModel, true);
403+
return ElasticsearchVectorStore.builder(restClient)
404+
.embeddingModel(embeddingModel)
405+
.initializeSchema(true)
406+
.options(options)
407+
.build();
396408
}
397409

398410
@Bean

0 commit comments

Comments
 (0)