Skip to content

Commit 3acb0cd

Browse files
committed
feat: add redis vector set command(vadd)
1 parent 4748a18 commit 3acb0cd

File tree

12 files changed

+852
-4
lines changed

12 files changed

+852
-4
lines changed

src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,10 @@ public RedisZSetCommands zSetCommands() {
246246
return this;
247247
}
248248

249-
@Override
249+
@Override
250+
public RedisVectorSetCommands vectorSetCommands() { return delegate.vectorSetCommands(); }
251+
252+
@Override
250253
public Long append(byte[] key, byte[] value) {
251254
return convertAndReturn(delegate.append(key, value), Converters.identityConverter());
252255
}

src/main/java/org/springframework/data/redis/connection/RedisCommandsProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,4 +118,12 @@ public interface RedisCommandsProvider {
118118
* @since 2.0
119119
*/
120120
RedisZSetCommands zSetCommands();
121+
122+
/**
123+
* Get {@link RedisVectorSetCommands}.
124+
*
125+
* @return never {@literal null}.
126+
* @since 3.5
127+
*/
128+
RedisVectorSetCommands vectorSetCommands();
121129
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection;
17+
18+
import org.jspecify.annotations.NonNull;
19+
import org.jspecify.annotations.NullUnmarked;
20+
import org.jspecify.annotations.Nullable;
21+
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
25+
/**
26+
* Vector Set-specific commands supported by Redis.
27+
*
28+
* @author Anne Lee
29+
* @see RedisCommands
30+
*/
31+
@NullUnmarked
32+
public interface RedisVectorSetCommands {
33+
34+
/**
35+
* Add a vector to a vector set using FP32 binary format.
36+
*
37+
* @param key the key
38+
* @param values the vector as FP32 binary blob
39+
* @param element the element name
40+
* @param options the options for the command
41+
* @return true if the element was added, false if it already existed
42+
*/
43+
Boolean vAdd(byte @NonNull [] key, byte @NonNull [] values, byte @NonNull [] element, VAddOptions options);
44+
45+
/**
46+
* Add a vector to a vector set using double array.
47+
*
48+
* @param key the key
49+
* @param values the vector as double array
50+
* @param element the element name
51+
* @param options the options for the command
52+
* @return true if the element was added, false if it already existed
53+
*/
54+
Boolean vAdd(byte @NonNull [] key, double @NonNull [] values, byte @NonNull [] element, VAddOptions options);
55+
56+
/**
57+
* Options for the VADD command.
58+
*
59+
* Note on attributes:
60+
* - Attributes are serialized to JSON and must be JavaScript/JSON compatible types
61+
* - Supported types: String, Number (Integer, Long, Double, Float), Boolean, null
62+
* - Collections (List, Map) are supported for nested structures
63+
* - Custom objects require proper JSON serialization support
64+
* - Date/Time objects should be converted to String or timestamp before use
65+
*/
66+
class VAddOptions {
67+
private final @Nullable Integer reduceDim;
68+
private final boolean cas;
69+
private final QuantizationType quantization;
70+
private final @Nullable Integer efBuildFactor;
71+
private final @Nullable Map<String, Object> attributes;
72+
private final @Nullable Integer maxConnections;
73+
74+
private VAddOptions(Builder builder) {
75+
this.reduceDim = builder.reduceDim;
76+
this.cas = builder.cas;
77+
this.quantization = builder.quantization;
78+
this.efBuildFactor = builder.efBuildFactor;
79+
this.attributes = builder.attributes;
80+
this.maxConnections = builder.maxConnections;
81+
}
82+
83+
public static Builder builder() {
84+
return new Builder();
85+
}
86+
87+
public @Nullable Integer getReduceDim() {
88+
return reduceDim;
89+
}
90+
91+
public boolean isCas() {
92+
return cas;
93+
}
94+
95+
public QuantizationType getQuantization() {
96+
return quantization;
97+
}
98+
99+
public @Nullable Integer getEfBuildFactor() {
100+
return efBuildFactor;
101+
}
102+
103+
public @Nullable Map<String, Object> getAttributes() {
104+
return attributes;
105+
}
106+
107+
public @Nullable Integer getMaxConnections() {
108+
return maxConnections;
109+
}
110+
111+
public static class Builder {
112+
private @Nullable Integer reduceDim;
113+
private boolean cas = false;
114+
private QuantizationType quantization = QuantizationType.Q8;
115+
private @Nullable Integer efBuildFactor;
116+
private @Nullable Map<String, Object> attributes;
117+
private @Nullable Integer maxConnections;
118+
119+
private Builder() {}
120+
121+
public Builder reduceDim(@Nullable Integer reduceDim) {
122+
this.reduceDim = reduceDim;
123+
return this;
124+
}
125+
126+
public Builder cas(boolean cas) {
127+
this.cas = cas;
128+
return this;
129+
}
130+
131+
public Builder quantization(QuantizationType quantization) {
132+
this.quantization = quantization;
133+
return this;
134+
}
135+
136+
public Builder efBuildFactor(@Nullable Integer efBuildFactor) {
137+
this.efBuildFactor = efBuildFactor;
138+
return this;
139+
}
140+
141+
public Builder attributes(@Nullable Map<String, Object> attributes) {
142+
this.attributes = attributes;
143+
return this;
144+
}
145+
146+
public Builder attribute(String key, Object value) {
147+
if (this.attributes == null) {
148+
this.attributes = new HashMap<>();
149+
}
150+
this.attributes.put(key, value);
151+
return this;
152+
}
153+
154+
public Builder maxConnections(@Nullable Integer maxConnections) {
155+
this.maxConnections = maxConnections;
156+
return this;
157+
}
158+
159+
public VAddOptions build() {
160+
return new VAddOptions(this);
161+
}
162+
}
163+
164+
public enum QuantizationType {
165+
NOQUANT,
166+
Q8,
167+
BIN,
168+
}
169+
}
170+
}

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
9797
private final JedisClusterStreamCommands streamCommands = new JedisClusterStreamCommands(this);
9898
private final JedisClusterStringCommands stringCommands = new JedisClusterStringCommands(this);
9999
private final JedisClusterZSetCommands zSetCommands = new JedisClusterZSetCommands(this);
100+
private final JedisClusterVSetCommands vSetCommands = new JedisClusterVSetCommands(this);
100101

101102
private boolean closed;
102103

@@ -309,7 +310,10 @@ public RedisZSetCommands zSetCommands() {
309310
return zSetCommands;
310311
}
311312

312-
@Override
313+
@Override
314+
public RedisVectorSetCommands vectorSetCommands() { return vSetCommands; }
315+
316+
@Override
313317
public RedisScriptingCommands scriptingCommands() {
314318
return new JedisClusterScriptingCommands(this);
315319
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.redis.connection.jedis;
17+
18+
import org.jspecify.annotations.NonNull;
19+
import org.jspecify.annotations.NullUnmarked;
20+
import org.springframework.dao.DataAccessException;
21+
import org.springframework.data.redis.connection.RedisVectorSetCommands;
22+
import org.springframework.util.Assert;
23+
24+
import redis.clients.jedis.params.VAddParams;
25+
26+
/**
27+
* Cluster {@link RedisVectorSetCommands} implementation for Jedis.
28+
*
29+
* @author Anne Lee
30+
* @since 3.5
31+
*/
32+
@NullUnmarked
33+
class JedisClusterVSetCommands implements RedisVectorSetCommands {
34+
35+
36+
private final JedisClusterConnection connection;
37+
38+
JedisClusterVSetCommands(@NonNull JedisClusterConnection connection) {
39+
this.connection = connection;
40+
}
41+
42+
@Override
43+
public Boolean vAdd(byte @NonNull [] key, byte @NonNull [] values, byte @NonNull [] element,
44+
VAddOptions options) {
45+
Assert.notNull(key, "Key must not be null");
46+
Assert.notNull(values, "Values must not be null");
47+
Assert.notNull(element, "Element must not be null");
48+
49+
try {
50+
if (options == null) {
51+
return connection.getCluster().vaddFP32(key, values, element);
52+
}
53+
54+
VAddParams params = JedisConverters.toVAddParams(options);
55+
56+
if (options.getReduceDim() != null) {
57+
// With REDUCE dimension
58+
return connection.getCluster().vaddFP32(key, values, element, options.getReduceDim(), params);
59+
}
60+
61+
return connection.getCluster().vaddFP32(key, values, element, params);
62+
} catch (Exception ex) {
63+
throw convertJedisAccessException(ex);
64+
}
65+
}
66+
67+
@Override
68+
public Boolean vAdd(byte @NonNull [] key, double @NonNull [] values, byte @NonNull [] element,
69+
VAddOptions options) {
70+
Assert.notNull(key, "Key must not be null");
71+
Assert.notNull(values, "Values must not be null");
72+
Assert.notNull(element, "Element must not be null");
73+
74+
// Convert double[] to float[] since Jedis uses float[]
75+
float[] floatValues = new float[values.length];
76+
for (int i = 0; i < values.length; i++) {
77+
floatValues[i] = (float) values[i];
78+
}
79+
80+
try {
81+
if (options == null) {
82+
return connection.getCluster().vadd(key, floatValues, element);
83+
}
84+
85+
redis.clients.jedis.params.VAddParams params = JedisConverters.toVAddParams(options);
86+
87+
if (options.getReduceDim() != null) {
88+
// With REDUCE dimension
89+
return connection.getCluster().vadd(key, floatValues, element, options.getReduceDim(), params);
90+
}
91+
92+
return connection.getCluster().vadd(key, floatValues, element, params);
93+
} catch (Exception ex) {
94+
throw convertJedisAccessException(ex);
95+
}
96+
}
97+
98+
private DataAccessException convertJedisAccessException(Exception ex) {
99+
return connection.convertJedisAccessException(ex);
100+
}
101+
}

src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ public class JedisConnection extends AbstractRedisConnection {
109109
private final JedisStreamCommands streamCommands = new JedisStreamCommands(this);
110110
private final JedisStringCommands stringCommands = new JedisStringCommands(this);
111111
private final JedisZSetCommands zSetCommands = new JedisZSetCommands(this);
112+
private final JedisVectorSetCommands vectorSetCommands = new JedisVectorSetCommands(this);
112113

113114
private final Log LOGGER = LogFactory.getLog(getClass());
114115

@@ -284,6 +285,11 @@ public RedisServerCommands serverCommands() {
284285
return serverCommands;
285286
}
286287

288+
@Override
289+
public RedisVectorSetCommands vectorSetCommands() {
290+
return vectorSetCommands;
291+
}
292+
287293
@Override
288294
public Object execute(@NonNull String command, byte @NonNull []... args) {
289295

0 commit comments

Comments
 (0)