Skip to content

Commit c160b9c

Browse files
feat(redis): enhance RedisVectorStore with text search, range queries, and HNSW tuning
Add new capabilities to Redis Vector Store: - Text search with searchByText() method and configurable scoring algorithms - Range-based vector search with searchByRange() method - Support for multiple distance metrics (COSINE, L2, IP) - Configurable HNSW parameters (M, efConstruction, efRuntime) - Document count queries with count() method - Module-level checkstyle configuration Update Redis documentation with comprehensive coverage of new features including configuration properties, usage examples, and parameter guidelines. Co-authored-by: Brian Sam-Bodden <brian@redis.com> Signed-off-by: Mark Pollack <mark.pollack@broadcom.com> Co-authored-by: Soby Chacko <soby.chacko@broadcom.com>
1 parent c5a7042 commit c160b9c

File tree

14 files changed

+1690
-126
lines changed

14 files changed

+1690
-126
lines changed

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@
1717
package org.springframework.ai.vectorstore.redis.autoconfigure;
1818

1919
import io.micrometer.observation.ObservationRegistry;
20-
import redis.clients.jedis.DefaultJedisClientConfig;
21-
import redis.clients.jedis.HostAndPort;
22-
import redis.clients.jedis.JedisClientConfig;
23-
import redis.clients.jedis.JedisPooled;
24-
2520
import org.springframework.ai.embedding.BatchingStrategy;
2621
import org.springframework.ai.embedding.EmbeddingModel;
2722
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
@@ -38,6 +33,10 @@
3833
import org.springframework.boot.data.redis.autoconfigure.DataRedisAutoConfiguration;
3934
import org.springframework.context.annotation.Bean;
4035
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
36+
import redis.clients.jedis.DefaultJedisClientConfig;
37+
import redis.clients.jedis.HostAndPort;
38+
import redis.clients.jedis.JedisClientConfig;
39+
import redis.clients.jedis.JedisPooled;
4140

4241
/**
4342
* {@link AutoConfiguration Auto-configuration} for Redis Vector Store.
@@ -46,6 +45,7 @@
4645
* @author Eddú Meléndez
4746
* @author Soby Chacko
4847
* @author Jihoon Kim
48+
* @author Brian Sam-Bodden
4949
*/
5050
@AutoConfiguration(after = DataRedisAutoConfiguration.class)
5151
@ConditionalOnClass({ JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class })
@@ -69,14 +69,27 @@ public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorSt
6969
BatchingStrategy batchingStrategy) {
7070

7171
JedisPooled jedisPooled = this.jedisPooled(jedisConnectionFactory);
72-
return RedisVectorStore.builder(jedisPooled, embeddingModel)
72+
RedisVectorStore.Builder builder = RedisVectorStore.builder(jedisPooled, embeddingModel)
7373
.initializeSchema(properties.isInitializeSchema())
7474
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
7575
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
7676
.batchingStrategy(batchingStrategy)
7777
.indexName(properties.getIndexName())
78-
.prefix(properties.getPrefix())
79-
.build();
78+
.prefix(properties.getPrefix());
79+
80+
// Configure HNSW parameters if available
81+
hnswConfiguration(builder, properties);
82+
83+
return builder.build();
84+
}
85+
86+
/**
87+
* Configures the HNSW-related parameters on the builder
88+
*/
89+
private void hnswConfiguration(RedisVectorStore.Builder builder, RedisVectorStoreProperties properties) {
90+
builder.hnswM(properties.getHnsw().getM())
91+
.hnswEfConstruction(properties.getHnsw().getEfConstruction())
92+
.hnswEfRuntime(properties.getHnsw().getEfRuntime());
8093
}
8194

8295
private JedisPooled jedisPooled(JedisConnectionFactory jedisConnectionFactory) {

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/main/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreProperties.java

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,28 @@
1818

1919
import org.springframework.ai.vectorstore.properties.CommonVectorStoreProperties;
2020
import org.springframework.boot.context.properties.ConfigurationProperties;
21+
import org.springframework.boot.context.properties.NestedConfigurationProperty;
2122

2223
/**
2324
* Configuration properties for Redis Vector Store.
2425
*
26+
* <p>
27+
* Example application.properties:
28+
* </p>
29+
* <pre>
30+
* spring.ai.vectorstore.redis.index-name=my-index
31+
* spring.ai.vectorstore.redis.prefix=doc:
32+
* spring.ai.vectorstore.redis.initialize-schema=true
33+
*
34+
* # HNSW algorithm configuration
35+
* spring.ai.vectorstore.redis.hnsw.m=32
36+
* spring.ai.vectorstore.redis.hnsw.ef-construction=100
37+
* spring.ai.vectorstore.redis.hnsw.ef-runtime=50
38+
* </pre>
39+
*
2540
* @author Julien Ruaux
2641
* @author Eddú Meléndez
42+
* @author Brian Sam-Bodden
2743
*/
2844
@ConfigurationProperties(RedisVectorStoreProperties.CONFIG_PREFIX)
2945
public class RedisVectorStoreProperties extends CommonVectorStoreProperties {
@@ -34,6 +50,12 @@ public class RedisVectorStoreProperties extends CommonVectorStoreProperties {
3450

3551
private String prefix = "default:";
3652

53+
/**
54+
* HNSW algorithm configuration properties.
55+
*/
56+
@NestedConfigurationProperty
57+
private HnswProperties hnsw = new HnswProperties();
58+
3759
public String getIndexName() {
3860
return this.indexName;
3961
}
@@ -50,4 +72,64 @@ public void setPrefix(String prefix) {
5072
this.prefix = prefix;
5173
}
5274

75+
public HnswProperties getHnsw() {
76+
return this.hnsw;
77+
}
78+
79+
public void setHnsw(HnswProperties hnsw) {
80+
this.hnsw = hnsw;
81+
}
82+
83+
/**
84+
* HNSW (Hierarchical Navigable Small World) algorithm configuration properties.
85+
*/
86+
public static class HnswProperties {
87+
88+
/**
89+
* M parameter for HNSW algorithm. Represents the maximum number of connections
90+
* per node in the graph. Higher values increase recall but also memory usage.
91+
* Typically between 5-100. Default: 16
92+
*/
93+
private Integer m = 16;
94+
95+
/**
96+
* EF_CONSTRUCTION parameter for HNSW algorithm. Size of the dynamic candidate
97+
* list during index building. Higher values lead to better recall but slower
98+
* indexing. Typically between 50-500. Default: 200
99+
*/
100+
private Integer efConstruction = 200;
101+
102+
/**
103+
* EF_RUNTIME parameter for HNSW algorithm. Size of the dynamic candidate list
104+
* during search. Higher values lead to more accurate but slower searches.
105+
* Typically between 20-200. Default: 10
106+
*/
107+
private Integer efRuntime = 10;
108+
109+
public Integer getM() {
110+
return this.m;
111+
}
112+
113+
public void setM(Integer m) {
114+
this.m = m;
115+
}
116+
117+
public Integer getEfConstruction() {
118+
return this.efConstruction;
119+
}
120+
121+
public void setEfConstruction(Integer efConstruction) {
122+
this.efConstruction = efConstruction;
123+
}
124+
125+
public Integer getEfRuntime() {
126+
return this.efRuntime;
127+
}
128+
129+
public void setEfRuntime(Integer efRuntime) {
130+
this.efRuntime = efRuntime;
131+
}
132+
133+
}
134+
53135
}

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfigurationIT.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023-2024 the original author or authors.
2+
* Copyright 2023-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -49,6 +49,7 @@
4949
* @author Soby Chacko
5050
* @author Christian Tzolov
5151
* @author Thomas Vitale
52+
* @author Brian Sam-Bodden
5253
*/
5354
@Testcontainers
5455
class RedisVectorStoreAutoConfigurationIT {
@@ -57,11 +58,13 @@ class RedisVectorStoreAutoConfigurationIT {
5758
static RedisStackContainer redisContainer = new RedisStackContainer(
5859
RedisStackContainer.DEFAULT_IMAGE_NAME.withTag(RedisStackContainer.DEFAULT_TAG));
5960

61+
// Use host and port explicitly since getRedisURI() might not be consistent
6062
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
6163
.withConfiguration(
6264
AutoConfigurations.of(DataRedisAutoConfiguration.class, RedisVectorStoreAutoConfiguration.class))
6365
.withUserConfiguration(Config.class)
64-
.withPropertyValues("spring.data.redis.url=" + redisContainer.getRedisURI())
66+
.withPropertyValues("spring.data.redis.host=" + redisContainer.getHost(),
67+
"spring.data.redis.port=" + redisContainer.getFirstMappedPort())
6568
.withPropertyValues("spring.ai.vectorstore.redis.initialize-schema=true")
6669
.withPropertyValues("spring.ai.vectorstore.redis.index=myIdx")
6770
.withPropertyValues("spring.ai.vectorstore.redis.prefix=doc:")
@@ -151,4 +154,4 @@ public EmbeddingModel embeddingModel() {
151154

152155
}
153156

154-
}
157+
}

auto-configurations/vector-stores/spring-ai-autoconfigure-vector-store-redis/src/test/java/org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStorePropertiesTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
/**
2424
* @author Julien Ruaux
2525
* @author Eddú Meléndez
26+
* @author Brian Sam-Bodden
2627
*/
2728
class RedisVectorStorePropertiesTests {
2829

@@ -31,6 +32,11 @@ void defaultValues() {
3132
var props = new RedisVectorStoreProperties();
3233
assertThat(props.getIndexName()).isEqualTo("default-index");
3334
assertThat(props.getPrefix()).isEqualTo("default:");
35+
36+
// Verify default HNSW parameters
37+
assertThat(props.getHnsw().getM()).isEqualTo(16);
38+
assertThat(props.getHnsw().getEfConstruction()).isEqualTo(200);
39+
assertThat(props.getHnsw().getEfRuntime()).isEqualTo(10);
3440
}
3541

3642
@Test
@@ -43,4 +49,18 @@ void customValues() {
4349
assertThat(props.getPrefix()).isEqualTo("doc:");
4450
}
4551

52+
@Test
53+
void customHnswValues() {
54+
var props = new RedisVectorStoreProperties();
55+
RedisVectorStoreProperties.HnswProperties hnsw = props.getHnsw();
56+
57+
hnsw.setM(32);
58+
hnsw.setEfConstruction(100);
59+
hnsw.setEfRuntime(50);
60+
61+
assertThat(props.getHnsw().getM()).isEqualTo(32);
62+
assertThat(props.getHnsw().getEfConstruction()).isEqualTo(100);
63+
assertThat(props.getHnsw().getEfRuntime()).isEqualTo(50);
64+
}
65+
4666
}

0 commit comments

Comments
 (0)