Skip to content

Commit 71584e5

Browse files
sobychackomarkpollack
authored andcommitted
Add builder pattern to HanaCloudVectorStore and refactor package name
1 parent 7a7a914 commit 71584e5

File tree

11 files changed

+190
-42
lines changed

11 files changed

+190
-42
lines changed

spring-ai-docs/src/main/antora/modules/ROOT/pages/api/vectordbs/hana.adoc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ import jakarta.persistence.Table;
142142
import lombok.Data;
143143
import lombok.NoArgsConstructor;
144144
import lombok.extern.jackson.Jacksonized;
145-
import org.springframework.ai.vectorstore.HanaVectorEntity;
145+
import org.springframework.ai.vectorstore.hanadb.HanaVectorEntity;
146146
147147
@Entity
148148
@Table(name = "CRICKET_WORLD_CUP")
@@ -165,7 +165,7 @@ package com.interviewpedia.spring.ai.hana;
165165
import jakarta.persistence.EntityManager;
166166
import jakarta.persistence.PersistenceContext;
167167
import jakarta.transaction.Transactional;
168-
import org.springframework.ai.vectorstore.HanaVectorRepository;
168+
import org.springframework.ai.vectorstore.hanadb.HanaVectorRepository;
169169
import org.springframework.stereotype.Repository;
170170
171171
import java.util.List;
@@ -246,7 +246,7 @@ import org.springframework.ai.chat.prompt.SystemPromptTemplate;
246246
import org.springframework.ai.document.Document;
247247
import org.springframework.ai.reader.pdf.PagePdfDocumentReader;
248248
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
249-
import org.springframework.ai.vectorstore.HanaCloudVectorStore;
249+
import org.springframework.ai.vectorstore.hanadb.HanaCloudVectorStore;
250250
import org.springframework.ai.vectorstore.VectorStore;
251251
import org.springframework.beans.factory.annotation.Autowired;
252252
import org.springframework.core.io.Resource;

spring-ai-spring-boot-autoconfigure/src/main/java/org/springframework/ai/autoconfigure/vectorstore/hanadb/HanaCloudVectorStoreAutoConfiguration.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
import io.micrometer.observation.ObservationRegistry;
2222

2323
import org.springframework.ai.embedding.EmbeddingModel;
24-
import org.springframework.ai.vectorstore.HanaCloudVectorStore;
25-
import org.springframework.ai.vectorstore.HanaCloudVectorStoreConfig;
26-
import org.springframework.ai.vectorstore.HanaVectorEntity;
27-
import org.springframework.ai.vectorstore.HanaVectorRepository;
24+
import org.springframework.ai.vectorstore.hanadb.HanaCloudVectorStore;
25+
import org.springframework.ai.vectorstore.hanadb.HanaVectorEntity;
26+
import org.springframework.ai.vectorstore.hanadb.HanaVectorRepository;
2827
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
2928
import org.springframework.beans.factory.ObjectProvider;
3029
import org.springframework.boot.autoconfigure.AutoConfiguration;
@@ -39,6 +38,7 @@
3938
*
4039
* @author Rahul Mittal
4140
* @author Christian Tzolov
41+
* @author Soby Chacko
4242
* @since 1.0.0
4343
*/
4444
@AutoConfiguration(after = { JpaRepositoriesAutoConfiguration.class })
@@ -53,13 +53,14 @@ public HanaCloudVectorStore vectorStore(HanaVectorRepository<? extends HanaVecto
5353
ObjectProvider<ObservationRegistry> observationRegistry,
5454
ObjectProvider<VectorStoreObservationConvention> customObservationConvention) {
5555

56-
return new HanaCloudVectorStore(repository, embeddingModel,
57-
HanaCloudVectorStoreConfig.builder()
58-
.tableName(properties.getTableName())
59-
.topK(properties.getTopK())
60-
.build(),
61-
observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP),
62-
customObservationConvention.getIfAvailable(() -> null));
56+
return HanaCloudVectorStore.builder()
57+
.repository(repository)
58+
.embeddingModel(embeddingModel)
59+
.tableName(properties.getTableName())
60+
.topK(properties.getTopK())
61+
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
62+
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
63+
.build();
6364
}
6465

6566
}
Lines changed: 115 additions & 15 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.vectorstore.hanadb;
1818

1919
import java.util.Collections;
2020
import java.util.List;
@@ -34,9 +34,12 @@
3434
import org.springframework.ai.observation.conventions.VectorStoreProvider;
3535
import org.springframework.ai.observation.conventions.VectorStoreSimilarityMetric;
3636
import org.springframework.ai.util.JacksonUtils;
37+
import org.springframework.ai.vectorstore.AbstractVectorStoreBuilder;
38+
import org.springframework.ai.vectorstore.SearchRequest;
3739
import org.springframework.ai.vectorstore.observation.AbstractObservationVectorStore;
3840
import org.springframework.ai.vectorstore.observation.VectorStoreObservationContext;
3941
import org.springframework.ai.vectorstore.observation.VectorStoreObservationConvention;
42+
import org.springframework.util.Assert;
4043

4144
/**
4245
* The <b>SAP HANA Cloud vector engine</b> offers multiple use cases in AI scenarios.
@@ -67,6 +70,7 @@
6770
* @author Rahul Mittal
6871
* @author Christian Tzolov
6972
* @author Sebastien Deleuze
73+
* @author Soby Chacko
7074
* @see <a href=
7175
* "https://help.sap.com/docs/hana-cloud-database/sap-hana-cloud-sap-hana-database-vector-engine-guide/introduction">SAP
7276
* HANA Database Vector Engine Guide</a>
@@ -78,30 +82,71 @@ public class HanaCloudVectorStore extends AbstractObservationVectorStore {
7882

7983
private final HanaVectorRepository<? extends HanaVectorEntity> repository;
8084

81-
private final EmbeddingModel embeddingModel;
85+
private final String tableName;
8286

83-
private final HanaCloudVectorStoreConfig config;
87+
private final int topK;
8488

8589
private final ObjectMapper objectMapper;
8690

91+
/**
92+
* Creates a new HanaCloudVectorStore with basic configuration.
93+
* @param repository the HANA vector repository
94+
* @param embeddingModel the embedding model to use
95+
* @param config the vector store configuration
96+
* @deprecated Since 1.0.0-M5, use {@link #builder()} instead
97+
*/
98+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
8799
public HanaCloudVectorStore(HanaVectorRepository<? extends HanaVectorEntity> repository,
88100
EmbeddingModel embeddingModel, HanaCloudVectorStoreConfig config) {
89-
90101
this(repository, embeddingModel, config, ObservationRegistry.NOOP, null);
91102
}
92103

104+
/**
105+
* Creates a new HanaCloudVectorStore with observation configuration.
106+
* @param repository the HANA vector repository
107+
* @param embeddingModel the embedding model to use
108+
* @param config the vector store configuration
109+
* @param observationRegistry the observation registry
110+
* @param customObservationConvention the custom observation convention
111+
* @deprecated Since 1.0.0-M5, use {@link #builder()} instead
112+
*/
113+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
93114
public HanaCloudVectorStore(HanaVectorRepository<? extends HanaVectorEntity> repository,
94115
EmbeddingModel embeddingModel, HanaCloudVectorStoreConfig config, ObservationRegistry observationRegistry,
95116
VectorStoreObservationConvention customObservationConvention) {
96117

97-
super(observationRegistry, customObservationConvention);
118+
this(builder().repository(repository)
119+
.embeddingModel(embeddingModel)
120+
.tableName(config.getTableName())
121+
.topK(config.getTopK())
122+
.observationRegistry(observationRegistry)
123+
.customObservationConvention(customObservationConvention));
124+
}
125+
126+
/**
127+
* Protected constructor that accepts a builder instance. This is the preferred way to
128+
* create new HanaCloudVectorStore instances.
129+
* @param builder the configured builder instance
130+
*/
131+
protected HanaCloudVectorStore(HanaCloudBuilder builder) {
132+
super(builder);
98133

99-
this.repository = repository;
100-
this.embeddingModel = embeddingModel;
101-
this.config = config;
134+
Assert.notNull(builder.repository, "Repository must not be null");
135+
136+
this.repository = builder.repository;
137+
this.tableName = builder.tableName;
138+
this.topK = builder.topK;
102139
this.objectMapper = JsonMapper.builder().addModules(JacksonUtils.instantiateAvailableModules()).build();
103140
}
104141

142+
/**
143+
* Creates a new builder for configuring and creating HanaCloudVectorStore instances.
144+
* @return a new builder instance
145+
*/
146+
public static HanaCloudBuilder builder() {
147+
return new HanaCloudBuilder();
148+
}
149+
105150
@Override
106151
public void doAdd(List<Document> documents) {
107152
int count = 1;
@@ -110,27 +155,27 @@ public void doAdd(List<Document> documents) {
110155
document.getId());
111156
String content = document.getContent().replaceAll("\\s+", " ");
112157
String embedding = getEmbedding(document);
113-
this.repository.save(this.config.getTableName(), document.getId(), embedding, content);
158+
this.repository.save(this.tableName, document.getId(), embedding, content);
114159
}
115160
logger.info("Embeddings saved in HanaCloudVectorStore for {} documents", count - 1);
116161
}
117162

118163
@Override
119164
public Optional<Boolean> doDelete(List<String> idList) {
120-
int deleteCount = this.repository.deleteEmbeddingsById(this.config.getTableName(), idList);
165+
int deleteCount = this.repository.deleteEmbeddingsById(this.tableName, idList);
121166
logger.info("{} embeddings deleted", deleteCount);
122167
return Optional.of(deleteCount == idList.size());
123168
}
124169

125170
public int purgeEmbeddings() {
126-
int deleteCount = this.repository.deleteAllEmbeddings(this.config.getTableName());
171+
int deleteCount = this.repository.deleteAllEmbeddings(this.tableName);
127172
logger.info("{} embeddings deleted", deleteCount);
128173
return deleteCount;
129174
}
130175

131176
@Override
132177
public List<Document> similaritySearch(String query) {
133-
return similaritySearch(SearchRequest.query(query).withTopK(this.config.getTopK()));
178+
return similaritySearch(SearchRequest.query(query).withTopK(this.topK));
134179
}
135180

136181
@Override
@@ -141,8 +186,8 @@ public List<Document> doSimilaritySearch(SearchRequest request) {
141186
}
142187

143188
String queryEmbedding = getEmbedding(request);
144-
List<? extends HanaVectorEntity> searchResult = this.repository
145-
.cosineSimilaritySearch(this.config.getTableName(), request.getTopK(), queryEmbedding);
189+
List<? extends HanaVectorEntity> searchResult = this.repository.cosineSimilaritySearch(this.tableName,
190+
request.getTopK(), queryEmbedding);
146191
logger.info("Hana cosine-similarity for query={}, with topK={} returned {} results", request.getQuery(),
147192
request.getTopK(), searchResult.size());
148193

@@ -175,8 +220,63 @@ public VectorStoreObservationContext.Builder createObservationContextBuilder(Str
175220

176221
return VectorStoreObservationContext.builder(VectorStoreProvider.HANA.value(), operationName)
177222
.dimensions(this.embeddingModel.dimensions())
178-
.collectionName(this.config.getTableName())
223+
.collectionName(this.tableName)
179224
.similarityMetric(VectorStoreSimilarityMetric.COSINE.value());
180225
}
181226

227+
/**
228+
* Builder class for creating {@link HanaCloudVectorStore} instances.
229+
* <p>
230+
* Provides a fluent API for configuring all aspects of the HANA Cloud vector store.
231+
*
232+
* @since 1.0.0
233+
*/
234+
public static class HanaCloudBuilder extends AbstractVectorStoreBuilder<HanaCloudBuilder> {
235+
236+
private HanaVectorRepository<? extends HanaVectorEntity> repository;
237+
238+
private String tableName;
239+
240+
private int topK;
241+
242+
/**
243+
* Sets the HANA vector repository.
244+
* @param repository the repository to use
245+
* @return the builder instance
246+
* @throws IllegalArgumentException if repository is null
247+
*/
248+
public HanaCloudBuilder repository(HanaVectorRepository<? extends HanaVectorEntity> repository) {
249+
Assert.notNull(repository, "Repository must not be null");
250+
this.repository = repository;
251+
return this;
252+
}
253+
254+
/**
255+
* Sets the table name for vector storage.
256+
* @param tableName the name of the table to use
257+
* @return the builder instance
258+
*/
259+
public HanaCloudBuilder tableName(String tableName) {
260+
this.tableName = tableName;
261+
return this;
262+
}
263+
264+
/**
265+
* Sets the number of top results to return.
266+
* @param topK the number of results
267+
* @return the builder instance
268+
*/
269+
public HanaCloudBuilder topK(int topK) {
270+
this.topK = topK;
271+
return this;
272+
}
273+
274+
@Override
275+
public HanaCloudVectorStore build() {
276+
validate();
277+
return new HanaCloudVectorStore(this);
278+
}
279+
280+
}
281+
182282
}
Lines changed: 33 additions & 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.vectorstore.hanadb;
1818

1919
/**
2020
* The {@code HanaCloudVectorStoreConfig} class represents the configuration for the
@@ -23,7 +23,9 @@
2323
*
2424
* @author Rahul Mittal
2525
* @since 1.0.0
26+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
2627
*/
28+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
2729
public final class HanaCloudVectorStoreConfig {
2830

2931
private String tableName;
@@ -33,34 +35,64 @@ public final class HanaCloudVectorStoreConfig {
3335
private HanaCloudVectorStoreConfig() {
3436
}
3537

38+
/**
39+
* Creates a new builder for HanaCloudVectorStoreConfig.
40+
* @return a new builder instance
41+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
42+
*/
43+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
3644
public static HanaCloudVectorStoreConfigBuilder builder() {
3745
return new HanaCloudVectorStoreConfigBuilder();
3846
}
3947

48+
/**
49+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
50+
*/
51+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
4052
public String getTableName() {
4153
return this.tableName;
4254
}
4355

56+
/**
57+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
58+
*/
59+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
4460
public int getTopK() {
4561
return this.topK;
4662
}
4763

64+
/**
65+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
66+
*/
67+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
4868
public static class HanaCloudVectorStoreConfigBuilder {
4969

5070
private String tableName;
5171

5272
private int topK;
5373

74+
/**
75+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
76+
*/
77+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
5478
public HanaCloudVectorStoreConfigBuilder tableName(String tableName) {
5579
this.tableName = tableName;
5680
return this;
5781
}
5882

83+
/**
84+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
85+
*/
86+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
5987
public HanaCloudVectorStoreConfigBuilder topK(int topK) {
6088
this.topK = topK;
6189
return this;
6290
}
6391

92+
/**
93+
* @deprecated Since 1.0.0-M5, use {@link HanaCloudVectorStore#builder()}
94+
*/
95+
@Deprecated(since = "1.0.0-M5", forRemoval = true)
6496
public HanaCloudVectorStoreConfig build() {
6597
HanaCloudVectorStoreConfig config = new HanaCloudVectorStoreConfig();
6698
config.tableName = this.tableName;
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.vectorstore.hanadb;
1818

1919
import jakarta.persistence.Column;
2020
import jakarta.persistence.Id;
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.vectorstore.hanadb;
1818

1919
import java.util.List;
2020

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.vectorstore.hanadb;
1818

1919
import jakarta.persistence.Column;
2020
import jakarta.persistence.Entity;

0 commit comments

Comments
 (0)