Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
* @author Dennis Neufeld
* @author Shyngys Sapraliyev
* @author Jeonggyu Choi
* @author Anne Lee
*/
@NullUnmarked
@SuppressWarnings({ "ConstantConditions", "deprecation" })
Expand Down Expand Up @@ -246,7 +247,12 @@ public RedisZSetCommands zSetCommands() {
return this;
}

@Override
@Override
public RedisVectorSetCommands vectorSetCommands() {
return delegate.vectorSetCommands();
}

@Override
public Long append(byte[] key, byte[] value) {
return convertAndReturn(delegate.append(key, value), Converters.identityConverter());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* Provides access to {@link RedisCommands} and the segregated command interfaces.
*
* @author Mark Paluch
* @author Anne Lee
* @since 3.0
*/
public interface RedisCommandsProvider {
Expand Down Expand Up @@ -118,4 +119,12 @@ public interface RedisCommandsProvider {
* @since 2.0
*/
RedisZSetCommands zSetCommands();

/**
* Get {@link RedisVectorSetCommands}.
*
* @return never {@literal null}.
* @since 3.5
*/
RedisVectorSetCommands vectorSetCommands();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection;

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;

import java.util.HashMap;
import java.util.Map;

/**
* Vector Set-specific commands supported by Redis.
*
* @author Anne Lee
* @see RedisCommands
*/
@NullUnmarked
public interface RedisVectorSetCommands {

/**
* Add a vector to a vector set using FP32 binary format.
*
* @param key the key
* @param values the vector as FP32 binary blob
* @param element the element name
* @param options the options for the command
* @return true if the element was added, false if it already existed
*/
Boolean vAdd(byte @NonNull [] key, byte @NonNull [] values, byte @NonNull [] element, VAddOptions options);

/**
* Add a vector to a vector set using double array.
*
* @param key the key
* @param values the vector as double array
* @param element the element name
* @param options the options for the command
* @return true if the element was added, false if it already existed
*/
Boolean vAdd(byte @NonNull [] key, double @NonNull [] values, byte @NonNull [] element, VAddOptions options);

/**
* Options for the VADD command.
*
* Note on attributes:
* - Attributes are serialized to JSON and must be JavaScript/JSON compatible types
* - Supported types: String, Number (Integer, Long, Double, Float), Boolean, null
* - Collections (List, Map) are supported for nested structures
* - Custom objects require proper JSON serialization support
* - Date/Time objects should be converted to String or timestamp before use
*/
class VAddOptions {
private final @Nullable Integer reduceDim;
private final boolean cas;
private final QuantizationType quantization;
private final @Nullable Integer efBuildFactor;
private final @Nullable Map<String, Object> attributes;
private final @Nullable Integer maxConnections;

private VAddOptions(Builder builder) {
this.reduceDim = builder.reduceDim;
this.cas = builder.cas;
this.quantization = builder.quantization;
this.efBuildFactor = builder.efBuildFactor;
this.attributes = builder.attributes;
this.maxConnections = builder.maxConnections;
}

public static Builder builder() {
return new Builder();
}

public @Nullable Integer getReduceDim() {
return reduceDim;
}

public boolean isCas() {
return cas;
}

public QuantizationType getQuantization() {
return quantization;
}

public @Nullable Integer getEfBuildFactor() {
return efBuildFactor;
}

public @Nullable Map<String, Object> getAttributes() {
return attributes;
}

public @Nullable Integer getMaxConnections() {
return maxConnections;
}

public static class Builder {
private @Nullable Integer reduceDim;
private boolean cas = false;
private QuantizationType quantization = QuantizationType.Q8;
private @Nullable Integer efBuildFactor;
private @Nullable Map<String, Object> attributes;
private @Nullable Integer maxConnections;

private Builder() {}

public Builder reduceDim(@Nullable Integer reduceDim) {
this.reduceDim = reduceDim;
return this;
}

public Builder cas(boolean cas) {
this.cas = cas;
return this;
}

public Builder quantization(QuantizationType quantization) {
this.quantization = quantization;
return this;
}

public Builder efBuildFactor(@Nullable Integer efBuildFactor) {
this.efBuildFactor = efBuildFactor;
return this;
}

public Builder attributes(@Nullable Map<String, Object> attributes) {
this.attributes = attributes;
return this;
}

public Builder attribute(String key, Object value) {
if (this.attributes == null) {
this.attributes = new HashMap<>();
}
this.attributes.put(key, value);
return this;
}

public Builder maxConnections(@Nullable Integer maxConnections) {
this.maxConnections = maxConnections;
return this;
}

public VAddOptions build() {
return new VAddOptions(this);
}
}

public enum QuantizationType {
NOQUANT,
Q8,
BIN,
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
* @author Pavel Khokhlov
* @author Liming Deng
* @author John Blum
* @author Anne Lee
* @since 1.7
*/
@NullUnmarked
Expand All @@ -97,6 +98,7 @@ public class JedisClusterConnection implements RedisClusterConnection {
private final JedisClusterStreamCommands streamCommands = new JedisClusterStreamCommands(this);
private final JedisClusterStringCommands stringCommands = new JedisClusterStringCommands(this);
private final JedisClusterZSetCommands zSetCommands = new JedisClusterZSetCommands(this);
private final JedisClusterVSetCommands vSetCommands = new JedisClusterVSetCommands(this);

private boolean closed;

Expand Down Expand Up @@ -309,7 +311,10 @@ public RedisZSetCommands zSetCommands() {
return zSetCommands;
}

@Override
@Override
public RedisVectorSetCommands vectorSetCommands() { return vSetCommands; }

@Override
public RedisScriptingCommands scriptingCommands() {
return new JedisClusterScriptingCommands(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2025 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.redis.connection.jedis;

import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullUnmarked;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisVectorSetCommands;
import org.springframework.util.Assert;

import redis.clients.jedis.params.VAddParams;

/**
* Cluster {@link RedisVectorSetCommands} implementation for Jedis.
*
* @author Anne Lee
* @since 3.5
*/
@NullUnmarked
class JedisClusterVSetCommands implements RedisVectorSetCommands {


private final JedisClusterConnection connection;

JedisClusterVSetCommands(@NonNull JedisClusterConnection connection) {
this.connection = connection;
}

@Override
public Boolean vAdd(byte @NonNull [] key, byte @NonNull [] values, byte @NonNull [] element,
VAddOptions options) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(values, "Values must not be null");
Assert.notNull(element, "Element must not be null");

try {
if (options == null) {
return connection.getCluster().vaddFP32(key, values, element);
}

VAddParams params = JedisConverters.toVAddParams(options);

if (options.getReduceDim() != null) {
// With REDUCE dimension
return connection.getCluster().vaddFP32(key, values, element, options.getReduceDim(), params);
}

return connection.getCluster().vaddFP32(key, values, element, params);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}

@Override
public Boolean vAdd(byte @NonNull [] key, double @NonNull [] values, byte @NonNull [] element,
VAddOptions options) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(values, "Values must not be null");
Assert.notNull(element, "Element must not be null");

// Convert double[] to float[] since Jedis uses float[]
float[] floatValues = new float[values.length];
for (int i = 0; i < values.length; i++) {
floatValues[i] = (float) values[i];
}

try {
if (options == null) {
return connection.getCluster().vadd(key, floatValues, element);
}

redis.clients.jedis.params.VAddParams params = JedisConverters.toVAddParams(options);

if (options.getReduceDim() != null) {
// With REDUCE dimension
return connection.getCluster().vadd(key, floatValues, element, options.getReduceDim(), params);
}

return connection.getCluster().vadd(key, floatValues, element, params);
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
}

private DataAccessException convertJedisAccessException(Exception ex) {
return connection.convertJedisAccessException(ex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
* @author Guy Korland
* @author Dengliming
* @author John Blum
* @author Anne Lee
* @see redis.clients.jedis.Jedis
*/
@NullUnmarked
Expand Down Expand Up @@ -109,6 +110,7 @@ public class JedisConnection extends AbstractRedisConnection {
private final JedisStreamCommands streamCommands = new JedisStreamCommands(this);
private final JedisStringCommands stringCommands = new JedisStringCommands(this);
private final JedisZSetCommands zSetCommands = new JedisZSetCommands(this);
private final JedisVectorSetCommands vectorSetCommands = new JedisVectorSetCommands(this);

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

Expand Down Expand Up @@ -284,6 +286,11 @@ public RedisServerCommands serverCommands() {
return serverCommands;
}

@Override
public RedisVectorSetCommands vectorSetCommands() {
return vectorSetCommands;
}

@Override
public Object execute(@NonNull String command, byte @NonNull []... args) {

Expand Down
Loading