Skip to content

Commit b0958b4

Browse files
committed
feat(query): add runtime parameters support for vector queries
Implements runtime parameter support for VectorQuery and VectorRangeQuery to enable dynamic query-time tuning without rebuilding indexes. Brings feature parity with Python redis-vl PR #439. Runtime Parameters Added: - efRuntime: HNSW dynamic candidate list size for improved recall - epsilon: Range search approximation factor for VectorRangeQuery - searchWindowSize: SVS-VAMANA KNN search window size - useSearchHistory: SVS-VAMANA search buffer control (OFF/ON/AUTO) - searchBufferCapacity: SVS-VAMANA compression tuning parameter Implementation Details: - Added fields, getters/setters with validation to VectorQuery - Added fields, getters/setters with validation to VectorRangeQuery - Updated Builder classes with fluent API methods - Parameters passed through toParams() map for Redis execution - All parameters optional (null by default) for backward compatibility Testing: - Created RuntimeParametersIntegrationTest with 12 comprehensive tests - All parameter validation tested (positive, non-negative, enum checks) - Functional behavior verified with actual Redis indexes - All 12 integration tests passing Documentation: - Javadoc added to all builder methods and setters - Added FT.AGGREGATE limitation notes to HybridQuery and MultiVectorQuery - References to Python PR #439 included throughout Note: Runtime parameters are NOT supported in FT.AGGREGATE queries (HybridQuery, MultiVectorQuery). Use VectorQuery or VectorRangeQuery if runtime parameter support is required.
1 parent 305aaf8 commit b0958b4

File tree

5 files changed

+758
-3
lines changed

5 files changed

+758
-3
lines changed

core/src/main/java/com/redis/vl/query/HybridQuery.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@
1919
* <p>It allows you to perform a hybrid search using both text and vector similarity. It scores
2020
* documents based on a weighted combination of text and vector similarity.
2121
*
22+
* <p><strong>Note on Runtime Parameters:</strong> HybridQuery uses Redis FT.AGGREGATE for
23+
* aggregation-based hybrid search. As of Redis Stack 7.2+, runtime parameters (efRuntime, epsilon,
24+
* etc.) are NOT supported in FT.AGGREGATE queries. If you need runtime parameter support, use
25+
* {@link VectorQuery} or {@link VectorRangeQuery} instead. See Python PR #439 for details.
26+
*
2227
* <p>Python equivalent:
2328
*
2429
* <pre>

core/src/main/java/com/redis/vl/query/MultiVectorQuery.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
* <p>Vectors may be of different size and datatype, but must be indexed using the 'cosine'
1818
* distance_metric.
1919
*
20+
* <p><strong>Note on Runtime Parameters:</strong> MultiVectorQuery uses Redis FT.AGGREGATE for
21+
* aggregation-based multi-vector search. As of Redis Stack 7.2+, runtime parameters (efRuntime,
22+
* epsilon, etc.) are NOT supported in FT.AGGREGATE queries. If you need runtime parameter support
23+
* with single-vector queries, use {@link VectorQuery} or {@link VectorRangeQuery} instead. See
24+
* Python PR #439 for details.
25+
*
2026
* <p>Ported from Python: redisvl/query/aggregate.py:257-400 (MultiVectorQuery class)
2127
*
2228
* <p>Python equivalent:

core/src/main/java/com/redis/vl/query/VectorQuery.java

Lines changed: 171 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ public class VectorQuery {
4343
/** EF runtime parameter for HNSW algorithm */
4444
private Integer efRuntime;
4545

46+
/** Search window size for SVS-VAMANA algorithm (Python PR #439) */
47+
private Integer searchWindowSize;
48+
49+
/** Search history usage for SVS-VAMANA algorithm: OFF, ON, AUTO (Python PR #439) */
50+
private String useSearchHistory;
51+
52+
/** Search buffer capacity for SVS-VAMANA algorithm (Python PR #439) */
53+
private Integer searchBufferCapacity;
54+
4655
/** Filter query for pre-filtering */
4756
private Filter filter;
4857

@@ -76,6 +85,9 @@ private VectorQuery(
7685
String hybridField,
7786
String hybridQuery,
7887
Integer efRuntime,
88+
Integer searchWindowSize,
89+
String useSearchHistory,
90+
Integer searchBufferCapacity,
7991
List<String> returnFields,
8092
boolean normalizeVectorDistance,
8193
String sortBy,
@@ -92,6 +104,9 @@ private VectorQuery(
92104
this.hybridField = hybridField;
93105
this.hybridQuery = hybridQuery;
94106
this.efRuntime = efRuntime;
107+
this.searchWindowSize = searchWindowSize;
108+
this.useSearchHistory = useSearchHistory;
109+
this.searchBufferCapacity = searchBufferCapacity;
95110
this.returnFields = returnFields;
96111
this.normalizeVectorDistance = normalizeVectorDistance;
97112
this.sortBy = sortBy;
@@ -293,11 +308,25 @@ public Map<String, Object> toParams() {
293308
byte[] vectorBytes = ArrayUtils.floatArrayToBytes(vector);
294309
params.put("vec", vectorBytes);
295310

296-
// Add EF runtime if specified (for HNSW)
311+
// Add runtime parameters if specified
312+
// EF runtime for HNSW
297313
if (efRuntime != null) {
298314
params.put("ef_runtime", efRuntime);
299315
}
300316

317+
// SVS-VAMANA runtime parameters (Python PR #439)
318+
if (searchWindowSize != null) {
319+
params.put("search_window_size", searchWindowSize);
320+
}
321+
322+
if (useSearchHistory != null) {
323+
params.put("use_search_history", useSearchHistory);
324+
}
325+
326+
if (searchBufferCapacity != null) {
327+
params.put("search_buffer_capacity", searchBufferCapacity);
328+
}
329+
301330
return params;
302331
}
303332

@@ -407,11 +436,81 @@ public Integer getEfRuntime() {
407436
* Set the EF runtime parameter for HNSW
408437
*
409438
* @param efRuntime EF runtime value
439+
* @throws IllegalArgumentException if efRuntime is not positive
410440
*/
411441
public void setEfRuntime(int efRuntime) {
442+
if (efRuntime <= 0) {
443+
throw new IllegalArgumentException("efRuntime must be positive");
444+
}
412445
this.efRuntime = efRuntime;
413446
}
414447

448+
/**
449+
* Get the search window size for SVS-VAMANA
450+
*
451+
* @return Search window size value
452+
*/
453+
public Integer getSearchWindowSize() {
454+
return searchWindowSize;
455+
}
456+
457+
/**
458+
* Set the search window size parameter for SVS-VAMANA
459+
*
460+
* @param searchWindowSize Search window size value
461+
* @throws IllegalArgumentException if searchWindowSize is not positive
462+
*/
463+
public void setSearchWindowSize(int searchWindowSize) {
464+
if (searchWindowSize <= 0) {
465+
throw new IllegalArgumentException("searchWindowSize must be positive");
466+
}
467+
this.searchWindowSize = searchWindowSize;
468+
}
469+
470+
/**
471+
* Get the use search history parameter for SVS-VAMANA
472+
*
473+
* @return Use search history value (OFF, ON, or AUTO)
474+
*/
475+
public String getUseSearchHistory() {
476+
return useSearchHistory;
477+
}
478+
479+
/**
480+
* Set the use search history parameter for SVS-VAMANA
481+
*
482+
* @param useSearchHistory Use search history value (must be OFF, ON, or AUTO)
483+
* @throws IllegalArgumentException if useSearchHistory is not one of the allowed values
484+
*/
485+
public void setUseSearchHistory(String useSearchHistory) {
486+
if (useSearchHistory != null && !useSearchHistory.matches("OFF|ON|AUTO")) {
487+
throw new IllegalArgumentException("useSearchHistory must be one of: OFF, ON, AUTO");
488+
}
489+
this.useSearchHistory = useSearchHistory;
490+
}
491+
492+
/**
493+
* Get the search buffer capacity for SVS-VAMANA
494+
*
495+
* @return Search buffer capacity value
496+
*/
497+
public Integer getSearchBufferCapacity() {
498+
return searchBufferCapacity;
499+
}
500+
501+
/**
502+
* Set the search buffer capacity parameter for SVS-VAMANA
503+
*
504+
* @param searchBufferCapacity Search buffer capacity value
505+
* @throws IllegalArgumentException if searchBufferCapacity is not positive
506+
*/
507+
public void setSearchBufferCapacity(int searchBufferCapacity) {
508+
if (searchBufferCapacity <= 0) {
509+
throw new IllegalArgumentException("searchBufferCapacity must be positive");
510+
}
511+
this.searchBufferCapacity = searchBufferCapacity;
512+
}
513+
415514
/**
416515
* Get the fields to return in results
417516
*
@@ -560,6 +659,9 @@ public Builder() {
560659
private String hybridField;
561660
private String hybridQuery;
562661
private Integer efRuntime;
662+
private Integer searchWindowSize;
663+
private String useSearchHistory;
664+
private Integer searchBufferCapacity;
563665
private List<String> returnFields;
564666
private boolean normalizeVectorDistance = false;
565667
private String sortBy;
@@ -774,8 +876,12 @@ public Builder withHybridSearch(String field, String query) {
774876
*
775877
* @param efRuntime EF runtime value
776878
* @return This builder
879+
* @throws IllegalArgumentException if efRuntime is not positive
777880
*/
778881
public Builder efRuntime(Integer efRuntime) {
882+
if (efRuntime != null && efRuntime <= 0) {
883+
throw new IllegalArgumentException("efRuntime must be positive");
884+
}
779885
this.efRuntime = efRuntime;
780886
return this;
781887
}
@@ -785,12 +891,73 @@ public Builder efRuntime(Integer efRuntime) {
785891
*
786892
* @param efRuntime EF runtime value
787893
* @return This builder
894+
* @throws IllegalArgumentException if efRuntime is not positive
788895
*/
789896
public Builder withEfRuntime(int efRuntime) {
897+
if (efRuntime <= 0) {
898+
throw new IllegalArgumentException("efRuntime must be positive");
899+
}
790900
this.efRuntime = efRuntime;
791901
return this;
792902
}
793903

904+
/**
905+
* Set the search window size parameter for SVS-VAMANA algorithm.
906+
*
907+
* <p>Controls the KNN search window size. Must be positive.
908+
*
909+
* <p>Python PR #439: SVS-VAMANA runtime parameter support
910+
*
911+
* @param searchWindowSize Search window size (must be positive)
912+
* @return This builder
913+
* @throws IllegalArgumentException if searchWindowSize is not positive
914+
*/
915+
public Builder searchWindowSize(Integer searchWindowSize) {
916+
if (searchWindowSize != null && searchWindowSize <= 0) {
917+
throw new IllegalArgumentException("searchWindowSize must be positive");
918+
}
919+
this.searchWindowSize = searchWindowSize;
920+
return this;
921+
}
922+
923+
/**
924+
* Set the use search history parameter for SVS-VAMANA algorithm.
925+
*
926+
* <p>Controls search buffer usage. Valid values: "OFF", "ON", "AUTO"
927+
*
928+
* <p>Python PR #439: SVS-VAMANA runtime parameter support
929+
*
930+
* @param useSearchHistory Search history mode (OFF, ON, or AUTO)
931+
* @return This builder
932+
* @throws IllegalArgumentException if useSearchHistory is not one of: OFF, ON, AUTO
933+
*/
934+
public Builder useSearchHistory(String useSearchHistory) {
935+
if (useSearchHistory != null && !useSearchHistory.matches("OFF|ON|AUTO")) {
936+
throw new IllegalArgumentException("useSearchHistory must be one of: OFF, ON, AUTO");
937+
}
938+
this.useSearchHistory = useSearchHistory;
939+
return this;
940+
}
941+
942+
/**
943+
* Set the search buffer capacity parameter for SVS-VAMANA algorithm.
944+
*
945+
* <p>Controls compression tuning. Must be positive.
946+
*
947+
* <p>Python PR #439: SVS-VAMANA runtime parameter support
948+
*
949+
* @param searchBufferCapacity Search buffer capacity (must be positive)
950+
* @return This builder
951+
* @throws IllegalArgumentException if searchBufferCapacity is not positive
952+
*/
953+
public Builder searchBufferCapacity(Integer searchBufferCapacity) {
954+
if (searchBufferCapacity != null && searchBufferCapacity <= 0) {
955+
throw new IllegalArgumentException("searchBufferCapacity must be positive");
956+
}
957+
this.searchBufferCapacity = searchBufferCapacity;
958+
return this;
959+
}
960+
794961
/**
795962
* Set the fields to return in results
796963
*
@@ -995,6 +1162,9 @@ public VectorQuery build() {
9951162
hybridField,
9961163
hybridQuery,
9971164
efRuntime,
1165+
searchWindowSize,
1166+
useSearchHistory,
1167+
searchBufferCapacity,
9981168
returnFields,
9991169
normalizeVectorDistance,
10001170
sortBy,

0 commit comments

Comments
 (0)