Skip to content

Commit d50afec

Browse files
committed
Refactor SearchRequest builder methods
- Deprecate builder methods with the prefix `with` - Since there is a conflicting method named query(String) which returns the instance of SearchRequest, changed the existing builder method query(String) to queryString(String) - Update docs and references
1 parent 99a44fb commit d50afec

File tree

104 files changed

+857
-836
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

104 files changed

+857
-836
lines changed

spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ private AdvisedRequest before(AdvisedRequest request) {
218218
// 2. Search for similar documents in the vector store.
219219
String query = new PromptTemplate(request.userText(), request.userParams()).render();
220220
var searchRequestToUse = SearchRequest.from(this.searchRequest)
221-
.withQuery(query)
222-
.withFilterExpression(doGetFilterExpression(context));
221+
.queryString(query)
222+
.filterExpression(doGetFilterExpression(context));
223223

224224
List<Document> documents = this.vectorStore.similaritySearch(searchRequestToUse);
225225

spring-ai-core/src/main/java/org/springframework/ai/chat/client/advisor/VectorStoreChatMemoryAdvisor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,8 @@ private AdvisedRequest before(AdvisedRequest request) {
139139
}
140140

141141
var searchRequest = SearchRequest.query(request.userText())
142-
.withTopK(this.doGetChatMemoryRetrieveSize(request.adviseContext()))
143-
.withFilterExpression(DOCUMENT_METADATA_CONVERSATION_ID + "=='"
142+
.topK(this.doGetChatMemoryRetrieveSize(request.adviseContext()))
143+
.filterExpression(DOCUMENT_METADATA_CONVERSATION_ID + "=='"
144144
+ this.doGetConversationId(request.adviseContext()) + "'");
145145

146146
List<Document> documents = this.getChatMemoryStore().similaritySearch(searchRequest);

spring-ai-core/src/main/java/org/springframework/ai/rag/retrieval/search/VectorStoreDocumentRetriever.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ public VectorStoreDocumentRetriever(VectorStore vectorStore, @Nullable Double si
7676
public List<Document> retrieve(Query query) {
7777
Assert.notNull(query, "query cannot be null");
7878
var searchRequest = SearchRequest.query(query.text())
79-
.withFilterExpression(this.filterExpression.get())
80-
.withSimilarityThreshold(this.similarityThreshold)
81-
.withTopK(this.topK);
79+
.filterExpression(this.filterExpression.get())
80+
.similarityThreshold(this.similarityThreshold)
81+
.topK(this.topK);
8282
return this.vectorStore.similaritySearch(searchRequest);
8383
}
8484

spring-ai-core/src/main/java/org/springframework/ai/vectorstore/SearchRequest.java

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
*
3333
* @author Christian Tzolov
3434
* @author Thomas Vitale
35+
* @author Ilayaperumal Gopinathan
3536
*/
3637
public final class SearchRequest {
3738

@@ -73,8 +74,8 @@ public static SearchRequest query(String query) {
7374

7475
/**
7576
* Create a new {@link SearchRequest} builder instance with an empty embedding query
76-
* string. Use the {@link #withQuery(String query)} to set/update the embedding query
77-
* text.
77+
* string. Use the {@link #queryString(String query)} to set/update the embedding
78+
* query text.
7879
* @return Returns new {@link SearchRequest} builder instance.
7980
*/
8081
public static SearchRequest defaults() {
@@ -87,16 +88,16 @@ public static SearchRequest defaults() {
8788
* @return Returns new {@link SearchRequest} builder instance.
8889
*/
8990
public static SearchRequest from(SearchRequest originalSearchRequest) {
90-
return new SearchRequest(originalSearchRequest.getQuery()).withTopK(originalSearchRequest.getTopK())
91-
.withSimilarityThreshold(originalSearchRequest.getSimilarityThreshold())
92-
.withFilterExpression(originalSearchRequest.getFilterExpression());
91+
return new SearchRequest(originalSearchRequest.getQuery()).topK(originalSearchRequest.getTopK())
92+
.similarityThreshold(originalSearchRequest.getSimilarityThreshold())
93+
.filterExpression(originalSearchRequest.getFilterExpression());
9394
}
9495

9596
/**
9697
* @param query Text to use for embedding similarity comparison.
9798
* @return this builder.
9899
*/
99-
public SearchRequest withQuery(String query) {
100+
public SearchRequest queryString(String query) {
100101
Assert.notNull(query, "Query can not be null.");
101102
this.query = query;
102103
return this;
@@ -106,7 +107,7 @@ public SearchRequest withQuery(String query) {
106107
* @param topK the top 'k' similar results to return.
107108
* @return this builder.
108109
*/
109-
public SearchRequest withTopK(int topK) {
110+
public SearchRequest topK(int topK) {
110111
Assert.isTrue(topK >= 0, "TopK should be positive.");
111112
this.topK = topK;
112113
return this;
@@ -121,7 +122,7 @@ public SearchRequest withTopK(int topK) {
121122
* @param threshold The lower bound of the similarity score.
122123
* @return this builder.
123124
*/
124-
public SearchRequest withSimilarityThreshold(double threshold) {
125+
public SearchRequest similarityThreshold(double threshold) {
125126
Assert.isTrue(threshold >= 0 && threshold <= 1, "Similarity threshold must be in [0,1] range.");
126127
this.similarityThreshold = threshold;
127128
return this;
@@ -132,8 +133,8 @@ public SearchRequest withSimilarityThreshold(double threshold) {
132133
* accepted.
133134
* @return this builder.
134135
*/
135-
public SearchRequest withSimilarityThresholdAll() {
136-
return withSimilarityThreshold(SIMILARITY_THRESHOLD_ACCEPT_ALL);
136+
public SearchRequest similarityThresholdAll() {
137+
return similarityThreshold(SIMILARITY_THRESHOLD_ACCEPT_ALL);
137138
}
138139

139140
/**
@@ -189,7 +190,7 @@ public SearchRequest withSimilarityThresholdAll() {
189190
* filter criteria. The 'null' value stands for no expression filters.
190191
* @return this builder.
191192
*/
192-
public SearchRequest withFilterExpression(@Nullable Filter.Expression expression) {
193+
public SearchRequest filterExpression(@Nullable Filter.Expression expression) {
193194
this.filterExpression = expression;
194195
return this;
195196
}
@@ -228,6 +229,63 @@ public SearchRequest withFilterExpression(@Nullable Filter.Expression expression
228229
* 'null' value stands for no expression filters.
229230
* @return this.builder
230231
*/
232+
public SearchRequest filterExpression(@Nullable String textExpression) {
233+
this.filterExpression = (textExpression != null) ? new FilterExpressionTextParser().parse(textExpression)
234+
: null;
235+
return this;
236+
}
237+
238+
/**
239+
* @deprecated use {@link #queryString(String)} instead.
240+
*/
241+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
242+
public SearchRequest withQuery(String query) {
243+
Assert.notNull(query, "Query can not be null.");
244+
this.query = query;
245+
return this;
246+
}
247+
248+
/**
249+
* @deprecated use {@link #topK(int)} instead.
250+
*/
251+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
252+
public SearchRequest withTopK(int topK) {
253+
Assert.isTrue(topK >= 0, "TopK should be positive.");
254+
this.topK = topK;
255+
return this;
256+
}
257+
258+
/**
259+
* @deprecated use {@link #similarityThreshold(double)} instead.
260+
*/
261+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
262+
public SearchRequest withSimilarityThreshold(double threshold) {
263+
Assert.isTrue(threshold >= 0 && threshold <= 1, "Similarity threshold must be in [0,1] range.");
264+
this.similarityThreshold = threshold;
265+
return this;
266+
}
267+
268+
/**
269+
* @deprecated use {@link #similarityThresholdAll()} instead.
270+
*/
271+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
272+
public SearchRequest withSimilarityThresholdAll() {
273+
return withSimilarityThreshold(SIMILARITY_THRESHOLD_ACCEPT_ALL);
274+
}
275+
276+
/**
277+
* @deprecated use {@link #filterExpression(Filter.Expression)} instead.
278+
*/
279+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
280+
public SearchRequest withFilterExpression(@Nullable Filter.Expression expression) {
281+
this.filterExpression = expression;
282+
return this;
283+
}
284+
285+
/**
286+
* @deprecated use {@link #filterExpression(String)} instead.
287+
*/
288+
@Deprecated(forRemoval = true, since = "1.0.0-M5")
231289
public SearchRequest withFilterExpression(@Nullable String textExpression) {
232290
this.filterExpression = (textExpression != null) ? new FilterExpressionTextParser().parse(textExpression)
233291
: null;

spring-ai-core/src/test/java/org/springframework/ai/chat/client/advisor/QuestionAnswerAdvisorTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ public Duration getTokensReset() {
112112
.willReturn(List.of(new Document("doc1"), new Document("doc2")));
113113

114114
var qaAdvisor = new QuestionAnswerAdvisor(this.vectorStore,
115-
SearchRequest.defaults().withSimilarityThreshold(0.99d).withTopK(6));
115+
SearchRequest.defaults().similarityThreshold(0.99d).topK(6));
116116

117117
var chatClient = ChatClient.builder(this.chatModel)
118118
.defaultSystem("Default system text.")

spring-ai-core/src/test/java/org/springframework/ai/vectorstore/SimpleVectorStoreTests.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ void shouldPerformSimilaritySearchWithThreshold() {
125125

126126
this.vectorStore.add(List.of(doc));
127127

128-
SearchRequest request = SearchRequest.query("query").withSimilarityThreshold(0.99f).withTopK(5);
128+
SearchRequest request = SearchRequest.query("query").similarityThreshold(0.99f).topK(5);
129129

130130
List<Document> results = this.vectorStore.similaritySearch(request);
131131
assertThat(results).isEmpty();
@@ -191,7 +191,7 @@ void shouldHandleConcurrentOperations() throws InterruptedException {
191191
thread.join();
192192
}
193193

194-
SearchRequest request = SearchRequest.query("test").withTopK(numThreads);
194+
SearchRequest request = SearchRequest.query("test").topK(numThreads);
195195

196196
List<Document> results = this.vectorStore.similaritySearch(request);
197197

@@ -213,14 +213,14 @@ void shouldHandleConcurrentOperations() throws InterruptedException {
213213

214214
@Test
215215
void shouldRejectInvalidSimilarityThreshold() {
216-
assertThatThrownBy(() -> SearchRequest.query("test").withSimilarityThreshold(2.0f))
216+
assertThatThrownBy(() -> SearchRequest.query("test").similarityThreshold(2.0f))
217217
.isInstanceOf(IllegalArgumentException.class)
218218
.hasMessage("Similarity threshold must be in [0,1] range.");
219219
}
220220

221221
@Test
222222
void shouldRejectNegativeTopK() {
223-
assertThatThrownBy(() -> SearchRequest.query("test").withTopK(-1)).isInstanceOf(IllegalArgumentException.class)
223+
assertThatThrownBy(() -> SearchRequest.query("test").topK(-1)).isInstanceOf(IllegalArgumentException.class)
224224
.hasMessage("TopK should be positive.");
225225
}
226226

spring-ai-core/src/test/java/org/springframework/ai/vectorstore/filter/SearchRequestTests.java

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public void createQuery() {
4646
@Test
4747
public void createFrom() {
4848
var originalRequest = SearchRequest.query("New Query")
49-
.withTopK(696)
50-
.withSimilarityThreshold(0.678)
51-
.withFilterExpression("country == 'NL'");
49+
.topK(696)
50+
.similarityThreshold(0.678)
51+
.filterExpression("country == 'NL'");
5252

5353
var newRequest = SearchRequest.from(originalRequest);
5454

@@ -60,72 +60,71 @@ public void createFrom() {
6060
}
6161

6262
@Test
63-
public void withQuery() {
63+
public void queryString() {
6464
var emptyRequest = SearchRequest.defaults();
6565
assertThat(emptyRequest.getQuery()).isEqualTo("");
6666

67-
emptyRequest.withQuery("New Query");
67+
emptyRequest.queryString("New Query");
6868
assertThat(emptyRequest.getQuery()).isEqualTo("New Query");
6969
}
7070

7171
@Test
72-
public void withSimilarityThreshold() {
73-
var request = SearchRequest.query("Test").withSimilarityThreshold(0.678);
72+
public void similarityThreshold() {
73+
var request = SearchRequest.query("Test").similarityThreshold(0.678);
7474
assertThat(request.getSimilarityThreshold()).isEqualTo(0.678);
7575

76-
request.withSimilarityThreshold(0.9);
76+
request.similarityThreshold(0.9);
7777
assertThat(request.getSimilarityThreshold()).isEqualTo(0.9);
7878

79-
assertThatThrownBy(() -> request.withSimilarityThreshold(-1)).isInstanceOf(IllegalArgumentException.class)
79+
assertThatThrownBy(() -> request.similarityThreshold(-1)).isInstanceOf(IllegalArgumentException.class)
8080
.hasMessageContaining("Similarity threshold must be in [0,1] range.");
8181

82-
assertThatThrownBy(() -> request.withSimilarityThreshold(1.1)).isInstanceOf(IllegalArgumentException.class)
82+
assertThatThrownBy(() -> request.similarityThreshold(1.1)).isInstanceOf(IllegalArgumentException.class)
8383
.hasMessageContaining("Similarity threshold must be in [0,1] range.");
8484

8585
}
8686

8787
@Test
88-
public void withTopK() {
89-
var request = SearchRequest.query("Test").withTopK(66);
88+
public void topK() {
89+
var request = SearchRequest.query("Test").topK(66);
9090
assertThat(request.getTopK()).isEqualTo(66);
9191

92-
request.withTopK(89);
92+
request.topK(89);
9393
assertThat(request.getTopK()).isEqualTo(89);
9494

95-
assertThatThrownBy(() -> request.withTopK(-1)).isInstanceOf(IllegalArgumentException.class)
95+
assertThatThrownBy(() -> request.topK(-1)).isInstanceOf(IllegalArgumentException.class)
9696
.hasMessageContaining("TopK should be positive.");
9797

9898
}
9999

100100
@Test
101-
public void withFilterExpression() {
101+
public void filterExpression() {
102102

103-
var request = SearchRequest.query("Test").withFilterExpression("country == 'BG' && year >= 2022");
103+
var request = SearchRequest.query("Test").filterExpression("country == 'BG' && year >= 2022");
104104
assertThat(request.getFilterExpression()).isEqualTo(new Filter.Expression(Filter.ExpressionType.AND,
105105
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("country"), new Filter.Value("BG")),
106106
new Filter.Expression(Filter.ExpressionType.GTE, new Filter.Key("year"), new Filter.Value(2022))));
107107
assertThat(request.hasFilterExpression()).isTrue();
108108

109-
request.withFilterExpression("active == true");
109+
request.filterExpression("active == true");
110110
assertThat(request.getFilterExpression()).isEqualTo(
111111
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("active"), new Filter.Value(true)));
112112
assertThat(request.hasFilterExpression()).isTrue();
113113

114-
request.withFilterExpression(new FilterExpressionBuilder().eq("country", "NL").build());
114+
request.filterExpression(new FilterExpressionBuilder().eq("country", "NL").build());
115115
assertThat(request.getFilterExpression()).isEqualTo(
116116
new Filter.Expression(Filter.ExpressionType.EQ, new Filter.Key("country"), new Filter.Value("NL")));
117117
assertThat(request.hasFilterExpression()).isTrue();
118118

119-
request.withFilterExpression((String) null);
119+
request.filterExpression((String) null);
120120
assertThat(request.getFilterExpression()).isNull();
121121
assertThat(request.hasFilterExpression()).isFalse();
122122

123-
request.withFilterExpression((Filter.Expression) null);
123+
request.filterExpression((Filter.Expression) null);
124124
assertThat(request.getFilterExpression()).isNull();
125125
assertThat(request.hasFilterExpression()).isFalse();
126126

127-
assertThatThrownBy(() -> request.withFilterExpression("FooBar"))
128-
.isInstanceOf(FilterExpressionParseException.class)
127+
assertThatThrownBy(() -> request.filterExpression("FooBar")).isInstanceOf(FilterExpressionParseException.class)
129128
.hasMessageContaining("Error: no viable alternative at input 'FooBar'");
130129

131130
}

spring-ai-core/src/test/java/org/springframework/ai/vectorstore/observation/DefaultVectorStoreObservationConventionTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void shouldHaveOptionalKeyValues() {
8383
.withFieldName("FIELD_NAME")
8484
.withNamespace("NAMESPACE")
8585
.withSimilarityMetric("SIMILARITY_METRIC")
86-
.withQueryRequest(SearchRequest.query("VDB QUERY").withFilterExpression("country == 'UK' && year >= 2020"))
86+
.withQueryRequest(SearchRequest.query("VDB QUERY").filterExpression("country == 'UK' && year >= 2020"))
8787
.build();
8888

8989
List<Document> queryResponseDocs = List.of(new Document("doc1"), new Document("doc2"));

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ public class SearchRequest {
5353

5454
private SearchRequest(String query) { this.query = query; }
5555

56-
public SearchRequest withTopK(int topK) {...}
57-
public SearchRequest withSimilarityThreshold(double threshold) {...}
58-
public SearchRequest withSimilarityThresholdAll() {...}
59-
public SearchRequest withFilterExpression(Filter.Expression expression) {...}
60-
public SearchRequest withFilterExpression(String textExpression) {...}
56+
public SearchRequest topK(int topK) {...}
57+
public SearchRequest similarityThreshold(double threshold) {...}
58+
public SearchRequest similarityThresholdAll() {...}
59+
public SearchRequest filterExpression(Filter.Expression expression) {...}
60+
public SearchRequest filterExpression(String textExpression) {...}
6161

6262
public String getQuery() {...}
6363
public int getTopK() {...}

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

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ And retrieve documents similar to a query:
142142
[source,java]
143143
----
144144
List<Document> results = vectorStore.similaritySearch(
145-
SearchRequest.query("Spring").withTopK(5));
145+
SearchRequest.query("Spring").topK(5));
146146
----
147147

148148
You can also limit results based on a similarity threshold:
@@ -151,8 +151,8 @@ You can also limit results based on a similarity threshold:
151151
----
152152
List<Document> results = vectorStore.similaritySearch(
153153
SearchRequest.query("Spring")
154-
.withTopK(5)
155-
.withSimilarityThreshold(0.5d));
154+
.topK(5)
155+
.similarityThreshold(0.5d));
156156
----
157157

158158
=== Advanced Configuration
@@ -193,8 +193,8 @@ For example, you can use either the text expression language:
193193
----
194194
vectorStore.similaritySearch(
195195
SearchRequest.query("The World")
196-
.withTopK(5)
197-
.withFilterExpression("country in ['UK', 'NL'] && year >= 2020"));
196+
.topK(5)
197+
.filterExpression("country in ['UK', 'NL'] && year >= 2020"));
198198
----
199199

200200
or programmatically using the expression DSL:
@@ -209,8 +209,8 @@ Filter.Expression f = new FilterExpressionBuilder()
209209
210210
vectorStore.similaritySearch(
211211
SearchRequest.query("The World")
212-
.withTopK(5)
213-
.withFilterExpression(f));
212+
.topK(5)
213+
.filterExpression(f));
214214
----
215215

216216
The portable filter expressions get automatically converted into link:https://cassandra.apache.org/doc/latest/cassandra/developing/cql/index.html[CQL queries].

0 commit comments

Comments
 (0)