Support ZADD params for Reactive-/RedisConnection and add addIfAbsent methods.

Closes: #1794
Original pull request: #1988.
This commit is contained in:
Christoph Strobl
2021-02-23 14:15:03 +01:00
committed by Mark Paluch
parent 102ee017aa
commit 4d77392c9d
21 changed files with 666 additions and 91 deletions

View File

@@ -1322,20 +1322,20 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[])
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[], org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Boolean zAdd(byte[] key, double score, byte[] value) {
return convertAndReturn(delegate.zAdd(key, score, value), Converters.identityConverter());
public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
return convertAndReturn(delegate.zAdd(key, score, value, args), Converters.identityConverter());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Long zAdd(byte[] key, Set<Tuple> tuples) {
return convertAndReturn(delegate.zAdd(key, tuples), Converters.identityConverter());
public Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
return convertAndReturn(delegate.zAdd(key, tuples, args), Converters.identityConverter());
}
/*
@@ -2686,20 +2686,20 @@ public class DefaultStringRedisConnection implements StringRedisConnection, Deco
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#zAdd(java.lang.String, double, java.lang.String)
* @see org.springframework.data.redis.connection.StringRedisConnection#zAdd(java.lang.String, double, java.lang.String, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Boolean zAdd(String key, double score, String value) {
return zAdd(serialize(key), score, serialize(value));
public Boolean zAdd(String key, double score, String value, ZAddArgs args) {
return zAdd(serialize(key), score, serialize(value), args);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.StringRedisConnection#zAdd(java.lang.String, java.util.Set)
* @see org.springframework.data.redis.connection.StringRedisConnection#zAdd(java.lang.String, java.util.Set, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Long zAdd(String key, Set<StringTuple> tuples) {
return zAdd(serialize(key), stringTupleToTuple.convert(tuples));
public Long zAdd(String key, Set<StringTuple> tuples, ZAddArgs args) {
return zAdd(serialize(key), stringTupleToTuple.convert(tuples), args);
}
/*

View File

@@ -873,15 +873,15 @@ public interface DefaultedRedisConnection extends RedisConnection {
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
@Override
@Deprecated
default Boolean zAdd(byte[] key, double score, byte[] value) {
return zSetCommands().zAdd(key, score, value);
default Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
return zSetCommands().zAdd(key, score, value, args);
}
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
@Override
@Deprecated
default Long zAdd(byte[] key, Set<Tuple> tuples) {
return zSetCommands().zAdd(key, tuples);
default Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
return zSetCommands().zAdd(key, tuples, args);
}
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */

View File

@@ -63,9 +63,11 @@ public interface ReactiveZSetCommands {
private final boolean upsert;
private final boolean returnTotalChanged;
private final boolean incr;
private final boolean gt;
private final boolean lt;
private ZAddCommand(@Nullable ByteBuffer key, List<Tuple> tuples, boolean upsert, boolean returnTotalChanged,
boolean incr) {
boolean incr, boolean gt, boolean lt) {
super(key);
@@ -73,6 +75,8 @@ public interface ReactiveZSetCommands {
this.upsert = upsert;
this.returnTotalChanged = returnTotalChanged;
this.incr = incr;
this.gt = gt;
this.lt = lt;
}
/**
@@ -98,7 +102,7 @@ public interface ReactiveZSetCommands {
Assert.notNull(tuples, "Tuples must not be null!");
return new ZAddCommand(null, new ArrayList<>(tuples), false, false, false);
return new ZAddCommand(null, new ArrayList<>(tuples), false, false, false, false, false);
}
/**
@@ -111,7 +115,7 @@ public interface ReactiveZSetCommands {
Assert.notNull(key, "Key must not be null!");
return new ZAddCommand(key, tuples, upsert, returnTotalChanged, incr);
return new ZAddCommand(key, tuples, upsert, returnTotalChanged, incr, gt, lt);
}
/**
@@ -121,7 +125,7 @@ public interface ReactiveZSetCommands {
* @return a new {@link ZAddCommand} with {@literal xx} applied.
*/
public ZAddCommand xx() {
return new ZAddCommand(getKey(), tuples, false, returnTotalChanged, incr);
return new ZAddCommand(getKey(), tuples, false, returnTotalChanged, incr, gt, lt);
}
/**
@@ -131,7 +135,7 @@ public interface ReactiveZSetCommands {
* @return a new {@link ZAddCommand} with {@literal nx} applied.
*/
public ZAddCommand nx() {
return new ZAddCommand(getKey(), tuples, true, returnTotalChanged, incr);
return new ZAddCommand(getKey(), tuples, true, returnTotalChanged, incr, gt, lt);
}
/**
@@ -141,7 +145,7 @@ public interface ReactiveZSetCommands {
* @return a new {@link ZAddCommand} with {@literal ch} applied.
*/
public ZAddCommand ch() {
return new ZAddCommand(getKey(), tuples, upsert, true, incr);
return new ZAddCommand(getKey(), tuples, upsert, true, incr, gt, lt);
}
/**
@@ -151,7 +155,29 @@ public interface ReactiveZSetCommands {
* @return a new {@link ZAddCommand} with {@literal incr} applied.
*/
public ZAddCommand incr() {
return new ZAddCommand(getKey(), tuples, upsert, upsert, true);
return new ZAddCommand(getKey(), tuples, upsert, upsert, true, gt, lt);
}
/**
* Applies {@literal GT} mode. Constructs a new command
* instance with all previously configured properties.
*
* @return a new {@link ZAddCommand} with {@literal incr} applied.
* @since 2.5
*/
public ZAddCommand gt() {
return new ZAddCommand(getKey(), tuples, upsert, upsert, incr, true, lt);
}
/**
* Applies {@literal LT} mode. Constructs a new command
* instance with all previously configured properties.
*
* @return a new {@link ZAddCommand} with {@literal incr} applied.
* @since 2.5
*/
public ZAddCommand lt() {
return new ZAddCommand(getKey(), tuples, upsert, upsert, incr, gt, true);
}
/**
@@ -175,6 +201,23 @@ public interface ReactiveZSetCommands {
return incr;
}
/**
*
* @return {@literal true} if {@literal GT} is set.
* @since 2.5
*/
public boolean isGt() {
return gt;
}
/**
* @return {@literal true} if {@literal LT} is set.
* @since 2.5
*/
public boolean isLt() {
return lt;
}
/**
* @return
*/

View File

@@ -17,6 +17,7 @@ package org.springframework.data.redis.connection;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.DoubleUnaryOperator;
@@ -396,6 +397,171 @@ public interface RedisZSetCommands {
}
}
/**
* {@code ZADD} specific arguments. <br />
* Looking of the {@code INCR} flag? Use the {@code ZINCRBY} operation instead.
*
* @since 2.3
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
*/
class ZAddArgs {
private static final ZAddArgs NONE = new ZAddArgs(Collections.emptySet());
private final Set<Flag> flags;
private ZAddArgs(Set<Flag> flags) {
this.flags = flags;
}
/**
* @return immutable {@link ZAddArgs}.
*/
public static ZAddArgs none() {
return NONE;
}
/**
* @return new instance of {@link ZAddArgs} without any flags set.
*/
public static ZAddArgs empty() {
return new ZAddArgs(new LinkedHashSet<>(5));
}
/**
* @return new instance of {@link ZAddArgs} without {@link Flag#NX} set.
*/
public static ZAddArgs ifNotExists() {
return empty().nx();
}
/**
* @return new instance of {@link ZAddArgs} without {@link Flag#NX} set.
*/
public static ZAddArgs ifExists() {
return empty().xx();
}
/**
* Only update elements that already exist.
*
* @return this.
*/
public ZAddArgs nx() {
flags.add(Flag.NX);
return this;
}
/**
* Don't update already existing elements.
*
* @return this.
*/
public ZAddArgs xx() {
flags.add(Flag.XX);
return this;
}
/**
* Only update existing elements if the new score is less than the current score.
*
* @return this.
*/
public ZAddArgs lt() {
flags.add(Flag.LT);
return this;
}
/**
* Only update existing elements if the new score is greater than the current score.
*
* @return this.
*/
public ZAddArgs gt() {
flags.add(Flag.GT);
return this;
}
/**
* Only update elements that already exist.
*
* @return this.
*/
public ZAddArgs ch() {
flags.add(Flag.CH);
return this;
}
/**
* Only update elements that already exist.
*
* @return this.
*/
public boolean contains(Flag flag) {
return flags.contains(flag);
}
/**
* @return {@literal true} if no flags set.
*/
public boolean isEmpty() {
return !flags.isEmpty();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ZAddArgs zAddArgs = (ZAddArgs) o;
return ObjectUtils.nullSafeEquals(flags, zAddArgs.flags);
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(flags);
}
public enum Flag {
/**
* Only update elements that already exist.
*/
XX,
/**
* Don't update already existing elements.
*/
NX,
/**
* Only update existing elements if the new score is greater than the current score.
*/
GT,
/**
* Only update existing elements if the new score is less than the current score.
*/
LT,
/**
* Modify the return value from the number of new elements added, to the total number of elements changed.
*/
CH
}
}
/**
* Add {@code value} to a sorted set at {@code key}, or update its {@code score} if it already exists.
*
@@ -406,7 +572,24 @@ public interface RedisZSetCommands {
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
*/
@Nullable
Boolean zAdd(byte[] key, double score, byte[] value);
default Boolean zAdd(byte[] key, double score, byte[] value) {
return zAdd(key, score, value, ZAddArgs.none());
}
/**
* Add {@code value} to a sorted set at {@code key}, or update its {@code score} depending on the given
* {@link ZAddArgs args}.
*
* @param key must not be {@literal null}.
* @param score the score.
* @param value the value.
* @param args must not be {@literal null} use {@link ZAddArgs#none()} instead.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
*/
@Nullable
Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args);
/**
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} if it already exists.
@@ -417,7 +600,22 @@ public interface RedisZSetCommands {
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
*/
@Nullable
Long zAdd(byte[] key, Set<Tuple> tuples);
default Long zAdd(byte[] key, Set<Tuple> tuples) {
return zAdd(key, tuples, ZAddArgs.none());
}
/**
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} depending on the given
* {@link ZAddArgs args}.
*
* @param key must not be {@literal null}.
* @param tuples must not be {@literal null}.
* @param args must not be {@literal null} use {@link ZAddArgs#none()} instead.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
*/
Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args);
/**
* Remove {@code values} from sorted set. Return number of removed elements.

View File

@@ -1102,7 +1102,24 @@ public interface StringRedisConnection extends RedisConnection {
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
* @see RedisZSetCommands#zAdd(byte[], double, byte[])
*/
Boolean zAdd(String key, double score, String value);
default Boolean zAdd(String key, double score, String value) {
return zAdd(key, score, value, ZAddArgs.none());
}
/**
* Add the {@code value} to a sorted set at {@code key}, or update its {@code score} depending on the given
* {@link ZAddArgs args}.
*
* @param key must not be {@literal null}.
* @param score must not be {@literal null}.
* @param value must not be {@literal null}.
* @param args must not be {@literal null} use {@link ZAddArgs#none()} instead.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
* @see RedisZSetCommands#zAdd(byte[], double, byte[], ZAddArgs)
*/
Boolean zAdd(String key, double score, String value, ZAddArgs args);
/**
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} if it already exists.
@@ -1113,7 +1130,24 @@ public interface StringRedisConnection extends RedisConnection {
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
* @see RedisZSetCommands#zAdd(byte[], Set)
*/
Long zAdd(String key, Set<StringTuple> tuples);
default Long zAdd(String key, Set<StringTuple> tuples) {
return zAdd(key, tuples, ZAddArgs.none());
}
/**
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} depending on the given
* {@link ZAddArgs args}.
*
* @param key must not be {@literal null}.
* @param tuples must not be {@literal null}.
* @param args must not be {@literal null} use {@link ZAddArgs#none()} instead.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD</a>
* @see RedisZSetCommands#zAdd(byte[], Set, ZAddArgs)
*/
@Nullable
Long zAdd(String key, Set<StringTuple> tuples, ZAddArgs args);
/**
* Remove {@code values} from sorted set. Return number of removed elements.

View File

@@ -54,13 +54,13 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[])
*/
@Override
public Boolean zAdd(byte[] key, double score, byte[] value) {
public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
try {
return JedisConverters.toBoolean(connection.getCluster().zadd(key, score, value));
return JedisConverters.toBoolean(connection.getCluster().zadd(key, score, value, JedisConverters.toZAddParams(args)));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}
@@ -68,16 +68,16 @@ class JedisClusterZSetCommands implements RedisZSetCommands {
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Long zAdd(byte[] key, Set<Tuple> tuples) {
public Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(tuples, "Tuples must not be null!");
try {
return connection.getCluster().zadd(key, JedisConverters.toTupleMap(tuples));
return connection.getCluster().zadd(key, JedisConverters.toTupleMap(tuples), JedisConverters.toZAddParams(args));
} catch (Exception ex) {
throw convertJedisAccessException(ex);
}

View File

@@ -24,6 +24,7 @@ import redis.clients.jedis.ScanParams;
import redis.clients.jedis.SortingParams;
import redis.clients.jedis.params.GeoRadiusParam;
import redis.clients.jedis.params.SetParams;
import redis.clients.jedis.params.ZAddParams;
import redis.clients.jedis.util.SafeEncoder;
import java.nio.ByteBuffer;
@@ -61,6 +62,7 @@ import org.springframework.data.redis.connection.RedisStringCommands.BitOperatio
import org.springframework.data.redis.connection.RedisStringCommands.SetOption;
import org.springframework.data.redis.connection.RedisZSetCommands.Range.Boundary;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs;
import org.springframework.data.redis.connection.SortParameters;
import org.springframework.data.redis.connection.SortParameters.Order;
import org.springframework.data.redis.connection.SortParameters.Range;
@@ -652,6 +654,42 @@ public abstract class JedisConverters extends Converters {
return ObjectUtils.caseInsensitiveValueOf(GeoUnit.values(), metricToUse.getAbbreviation());
}
/**
* Convert {@link ZAddArgs} to {@link ZAddParams}.
*
* @param source must not be {@literal null}.
* @return new instance of {@link ZAddParams}.
* @since 2.5
*/
static ZAddParams toZAddParams(ZAddArgs source) {
if (!source.isEmpty()) {
return new ZAddParams();
}
ZAddParams target = new ZAddParams() {
{
if (source.contains(ZAddArgs.Flag.GT)) {
addParam("gt");
}
if (source.contains(ZAddArgs.Flag.LT)) {
addParam("lt");
}
}
};
if (source.contains(ZAddArgs.Flag.XX)) {
target.xx();
}
if (source.contains(ZAddArgs.Flag.NX)) {
target.nx();
}
if (source.contains(ZAddArgs.Flag.CH)) {
target.ch();
}
return target;
}
/**
* Convert {@link GeoRadiusCommandArgs} into {@link GeoRadiusParam}.

View File

@@ -21,12 +21,14 @@ import redis.clients.jedis.MultiKeyPipelineBase;
import redis.clients.jedis.ScanParams;
import redis.clients.jedis.ScanResult;
import redis.clients.jedis.ZParams;
import redis.clients.jedis.params.ZAddParams;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashSet;
import java.util.Set;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs.Flag;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.KeyBoundCursor;
import org.springframework.data.redis.core.ScanIteration;
@@ -53,27 +55,27 @@ class JedisZSetCommands implements RedisZSetCommands {
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[])
*/
@Override
public Boolean zAdd(byte[] key, double score, byte[] value) {
public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
return connection.invoke().from(BinaryJedis::zadd, MultiKeyPipelineBase::zadd, key, score, value)
return connection.invoke().from(BinaryJedis::zadd, MultiKeyPipelineBase::zadd, key, score, value, JedisConverters.toZAddParams(args))
.get(JedisConverters::toBoolean);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Long zAdd(byte[] key, Set<Tuple> tuples) {
public Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(tuples, "Tuples must not be null!");
return connection.invoke().just(BinaryJedis::zadd, MultiKeyPipelineBase::zadd, key,
JedisConverters.toTupleMap(tuples));
JedisConverters.toTupleMap(tuples), JedisConverters.toZAddParams(args));
}
/*

View File

@@ -102,6 +102,12 @@ class LettuceReactiveZSetCommands implements ReactiveZSetCommands {
} else {
args = ZAddArgs.Builder.xx();
}
if(command.isGt()) {
args = args == null ? ZAddArgs.Builder.gt() : args.gt();
}
if(command.isLt()) {
args = args == null ? ZAddArgs.Builder.lt() : args.lt();
}
}
ScoredValue<ByteBuffer>[] values = (ScoredValue<ByteBuffer>[]) command.getTuples().stream()

View File

@@ -26,6 +26,7 @@ import java.util.List;
import java.util.Set;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs.Flag;
import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.KeyBoundCursor;
@@ -50,29 +51,29 @@ class LettuceZSetCommands implements RedisZSetCommands {
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[])
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], double, byte[], org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Boolean zAdd(byte[] key, double score, byte[] value) {
public Boolean zAdd(byte[] key, double score, byte[] value, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(value, "Value must not be null!");
return connection.invoke().from(RedisSortedSetAsyncCommands::zadd, key, score, value)
return connection.invoke().from(RedisSortedSetAsyncCommands::zadd, key, LettuceZSetCommands.toZAddArgs(args), score, value)
.get(LettuceConverters.longToBoolean());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set)
* @see org.springframework.data.redis.connection.RedisZSetCommands#zAdd(byte[], java.util.Set, org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs)
*/
@Override
public Long zAdd(byte[] key, Set<Tuple> tuples) {
public Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(tuples, "Tuples must not be null!");
return connection.invoke().just(RedisSortedSetAsyncCommands::zadd, key,
return connection.invoke().just(RedisSortedSetAsyncCommands::zadd, key, LettuceZSetCommands.toZAddArgs(args),
LettuceConverters.toObjects(tuples).toArray());
}
@@ -568,4 +569,37 @@ class LettuceZSetCommands implements RedisZSetCommands {
return args;
}
/**
* Convert {@link ZAddArgs} to {@link io.lettuce.core.ZAddArgs}.
*
* @param source must not be {@literal null}.
* @return never {@literal null}.
* @since 2.5
*/
private static io.lettuce.core.ZAddArgs toZAddArgs(ZAddArgs source) {
io.lettuce.core.ZAddArgs target = new io.lettuce.core.ZAddArgs();
if(!source.isEmpty()) {
return target;
}
if(source.contains(Flag.XX)) {
target.xx();
}
if(source.contains(Flag.NX)) {
target.nx();
}
if(source.contains(Flag.GT)) {
target.gt();
}
if(source.contains(Flag.LT)) {
target.lt();
}
if(source.contains(Flag.CH)) {
target.ch();
}
return target;
}
}

View File

@@ -48,6 +48,18 @@ public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
@Nullable
Boolean add(V value, double score);
/**
* Add {@code value} to a sorted set at {@code key} if it does not already exists.
*
* @param score the score.
* @param value the value.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD NX</a>
*/
@Nullable
Boolean addIfAbsent(V value, double score);
/**
* Add {@code tuples} to a sorted set at the bound key, or update its {@code score} if it already exists.
*
@@ -58,6 +70,17 @@ public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
@Nullable
Long add(Set<TypedTuple<V>> tuples);
/**
* Add {@code tuples} to a sorted set at {@code key} if it does not already exists.
*
* @param tuples must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD NX</a>
*/
@Nullable
Long addIfAbsent(Set<TypedTuple<V>> tuples);
/**
* Remove {@code values} from sorted set. Return number of removed elements.
*

View File

@@ -60,6 +60,15 @@ class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> impl
return ops.add(getKey(), value, score);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#addIfAbsent(java.lang.Object, double)
*/
@Override
public Boolean addIfAbsent(V value, double score) {
return ops.addIfAbsent(getKey(), value, score);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#add(java.util.Set)
@@ -69,6 +78,15 @@ class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> impl
return ops.add(getKey(), tuples);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#addIfAbsent(java.util.Set)
*/
@Override
public Long addIfAbsent(Set<TypedTuple<V>> tuples) {
return ops.addIfAbsent(getKey(), tuples);
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#incrementScore(java.lang.Object, double)

View File

@@ -24,6 +24,8 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs;
import org.springframework.lang.Nullable;
/**
* Default implementation of {@link ZSetOperations}.
@@ -48,10 +50,31 @@ class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZS
*/
@Override
public Boolean add(K key, V value, double score) {
return add(key, value, score, ZAddArgs.none());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ZSetOperations#addIfAbsent(java.lang.Object, java.lang.Object, double)
*/
@Override
public Boolean addIfAbsent(K key, V value, double score) {
return add(key, value, score, ZAddArgs.ifNotExists());
}
/**
* @param key must not be {@literal null}.
* @param value must not be {@literal null}.
* @param args never {@literal null}.
* @return can be {@literal null}.
* @since 2.5
*/
@Nullable
protected Boolean add(K key, V value, double score, ZAddArgs args) {
byte[] rawKey = rawKey(key);
byte[] rawValue = rawValue(value);
return execute(connection -> connection.zAdd(rawKey, score, rawValue), true);
return execute(connection -> connection.zAdd(rawKey, score, rawValue, args), true);
}
/*
@@ -60,10 +83,31 @@ class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZS
*/
@Override
public Long add(K key, Set<TypedTuple<V>> tuples) {
return add(key, tuples, ZAddArgs.none());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ZSetOperations#addIfAbsent(java.lang.Object, java.util.Set)
*/
@Override
public Long addIfAbsent(K key, Set<TypedTuple<V>> tuples) {
return add(key, tuples, ZAddArgs.ifNotExists());
}
/**
* @param key must not be {@literal null}.
* @param tuples must not be {@literal null}.
* @param args never {@literal null}.
* @return can be {@literal null}.
* @since 2.5
*/
@Nullable
protected Long add(K key, Set<TypedTuple<V>> tuples, ZAddArgs args) {
byte[] rawKey = rawKey(key);
Set<Tuple> rawValues = rawTupleValues(tuples);
return execute(connection -> connection.zAdd(rawKey, rawValues), true);
return execute(connection -> connection.zAdd(rawKey, rawValues, args), true);
}
/*

View File

@@ -47,6 +47,19 @@ public interface ZSetOperations<K, V> {
@Nullable
Double getScore();
/**
* Create a new {@link TypedTuple}.
*
* @param value must not be {@literal null}.
* @param score can be {@literal null}.
* @param <V>
* @return new instance of {@link TypedTuple}.
* @since 2.5
*/
static <V> TypedTuple<V> of(V value, @Nullable Double score) {
return new DefaultTypedTuple<>(value, score);
}
}
/**
@@ -61,6 +74,19 @@ public interface ZSetOperations<K, V> {
@Nullable
Boolean add(K key, V value, double score);
/**
* Add {@code value} to a sorted set at {@code key} if it does not already exists.
*
* @param key must not be {@literal null}.
* @param score the score.
* @param value the value.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD NX</a>
*/
@Nullable
Boolean addIfAbsent(K key, V value, double score);
/**
* Add {@code tuples} to a sorted set at {@code key}, or update its {@code score} if it already exists.
*
@@ -72,6 +98,18 @@ public interface ZSetOperations<K, V> {
@Nullable
Long add(K key, Set<TypedTuple<V>> tuples);
/**
* Add {@code tuples} to a sorted set at {@code key} if it does not already exists.
*
* @param key must not be {@literal null}.
* @param tuples must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.5
* @see <a href="https://redis.io/commands/zadd">Redis Documentation: ZADD NX</a>
*/
@Nullable
Long addIfAbsent(K key, Set<TypedTuple<V>> tuples);
/**
* Remove {@code values} from sorted set. Return number of removed elements.
*

View File

@@ -288,6 +288,18 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
return result;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#addIfAbsent(java.lang.Object, double)
*/
@Override
public boolean addIfAbsent(E e, double score) {
Boolean result = boundZSetOps.addIfAbsent(e, score);
checkResult(result);
return result;
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#clear()

View File

@@ -144,6 +144,27 @@ public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
*/
boolean add(E e);
/**
* Adds an element to the set using the {@link #getDefaultScore() default score} if the element does not already exists.
*
* @param e element to add
* @return true if a new element was added, false otherwise (only the score has been updated)
* @since 2.5
*/
default boolean addIfAbsent(E e) {
return addIfAbsent(e, getDefaultScore());
}
/**
* Adds an element to the set with the given score if the element does not already exists.
*
* @param e element to add
* @param score element score
* @return true if a new element was added, false otherwise (only the score has been updated)
* @since 2.5
*/
boolean addIfAbsent(E e, double score);
/**
* Count number of elements within sorted set with value between {@code Range#min} and {@code Range#max} applying
* lexicographical ordering.

View File

@@ -39,7 +39,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Range.Bound;
@@ -59,6 +58,7 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs;
import org.springframework.data.redis.connection.SortParameters.Order;
import org.springframework.data.redis.connection.StringRedisConnection.StringTuple;
import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding;
@@ -1314,8 +1314,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.rPush("PopList", "world"));
connection.lSet("PopList", 1, "cruel");
actual.add(connection.lRange("PopList", 0, -1));
verifyResults(
Arrays.asList(1L, 2L, 3L, Arrays.asList("hello", "cruel", "world")));
verifyResults(Arrays.asList(1L, 2L, 3L, Arrays.asList("hello", "cruel", "world")));
}
@Test
@@ -1491,8 +1490,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.sAdd("myset", "foo", "bar"));
actual.add(connection.sAdd("myset", "baz"));
actual.add(connection.sMembers("myset"));
verifyResults(
Arrays.asList(new Object[] { 2L, 1L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
verifyResults(Arrays.asList(new Object[] { 2L, 1L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
}
@Test
@@ -1564,8 +1562,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.sAdd("myset", "foo"));
actual.add(connection.sAdd("myset", "bar"));
actual.add(connection.sPop("myset"));
assertThat(new HashSet<>(Arrays.asList("foo", "bar")).contains((String) getResults().get(2)))
.isTrue();
assertThat(new HashSet<>(Arrays.asList("foo", "bar")).contains((String) getResults().get(2))).isTrue();
}
@Test // DATAREDIS-688
@@ -1645,8 +1642,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.sAdd("otherset", "bar"));
actual.add(connection.sAdd("otherset", "baz"));
actual.add(connection.sUnion("myset", "otherset"));
verifyResults(Arrays
.asList(new Object[] { 1L, 1L, 1L, 1L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
verifyResults(Arrays.asList(new Object[] { 1L, 1L, 1L, 1L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
}
@Test
@@ -1657,8 +1653,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.sAdd("otherset", "baz"));
actual.add(connection.sUnionStore("thirdset", "myset", "otherset"));
actual.add(connection.sMembers("thirdset"));
verifyResults(Arrays.asList(
new Object[] { 1L, 1L, 1L, 1L, 3L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
verifyResults(
Arrays.asList(new Object[] { 1L, 1L, 1L, 1L, 3L, new HashSet<>(Arrays.asList("foo", "bar", "baz")) }));
}
// ZSet
@@ -1668,8 +1664,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRange("myset", 0, -1));
verifyResults(Arrays
.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("James", "Bob")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("James", "Bob")) }));
}
@Test
@@ -1682,8 +1677,52 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", strTuples));
actual.add(connection.zAdd("myset".getBytes(), tuples));
actual.add(connection.zRange("myset", 0, -1));
verifyResults(Arrays
.asList(new Object[] { 2L, 1L, new LinkedHashSet<>(Arrays.asList("James", "Bob", "Joe")) }));
verifyResults(Arrays.asList(new Object[] { 2L, 1L, new LinkedHashSet<>(Arrays.asList("James", "Bob", "Joe")) }));
}
@Test // GH-1794
void zAddNX() {
actual.add(connection.zAdd("myset", 1, "Bob", ZAddArgs.ifNotExists()));
actual.add(connection.zAdd("myset", 2, "Bob", ZAddArgs.ifNotExists()));
verifyResults(Arrays.asList(true, false));
}
@Test // GH-1794
void zAddXX() {
actual.add(connection.zAdd("myset", 1, "Bob", ZAddArgs.ifExists()));
actual.add(connection.zAdd("myset", 1, "Bob"));
actual.add(connection.zAdd("myset", 2, "Bob", ZAddArgs.ifExists()));
verifyResults(Arrays.asList(false, true, false));
}
@Test // GH-1794
void testZAddMultipleNX() {
actual.add(connection.zAdd("myset", 1, "James"));
Set<StringTuple> strTuples = new HashSet<>();
strTuples.add(new DefaultStringTuple("Bob".getBytes(), "Bob", 2.0));
strTuples.add(new DefaultStringTuple("James".getBytes(), "James", 1.0));
actual.add(connection.zAdd("myset", strTuples, ZAddArgs.ifNotExists()));
verifyResults(Arrays.asList(true, 1L));
}
@Test // GH-1794
void testZAddMultipleXX() {
actual.add(connection.zAdd("myset", 1, "James"));
Set<StringTuple> strTuples = new HashSet<>();
strTuples.add(new DefaultStringTuple("Bob".getBytes(), "Bob", 2.0));
strTuples.add(new DefaultStringTuple("James".getBytes(), "James", 2.0));
actual.add(connection.zAdd("myset", strTuples, ZAddArgs.ifNotExists()));
verifyResults(Arrays.asList(true, 1L));
}
@Test
@@ -1783,8 +1822,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRangeByScore("myset", 1, 1));
verifyResults(
Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("James")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("James")) }));
}
@Test
@@ -1792,8 +1830,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRangeByScore("myset", 1d, 3d, 1, -1));
verifyResults(
Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob")) }));
}
@Test
@@ -1801,8 +1838,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRangeByScoreWithScores("myset", 2d, 5d));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(
Arrays.asList(new DefaultStringTuple("Bob".getBytes(), "Bob", 2d))) }));
verifyResults(Arrays.asList(new Object[] { true, true,
new LinkedHashSet<>(Arrays.asList(new DefaultStringTuple("Bob".getBytes(), "Bob", 2d))) }));
}
@Test
@@ -1810,8 +1847,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRangeByScoreWithScores("myset", 1d, 5d, 0, 1));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(
Arrays.asList(new DefaultStringTuple("James".getBytes(), "James", 1d))) }));
verifyResults(Arrays.asList(new Object[] { true, true,
new LinkedHashSet<>(Arrays.asList(new DefaultStringTuple("James".getBytes(), "James", 1d))) }));
}
@Test
@@ -1819,8 +1856,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 2, "Bob"));
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRevRange("myset", 0, -1));
verifyResults(Arrays
.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
}
@Test
@@ -1838,8 +1874,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset".getBytes(), 2, "Bob".getBytes()));
actual.add(connection.zAdd("myset".getBytes(), 1, "James".getBytes()));
actual.add(connection.zRevRangeByScore("myset", 0d, 3d, 0, 5));
verifyResults(Arrays
.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
}
@Test
@@ -1847,8 +1882,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset".getBytes(), 2, "Bob".getBytes()));
actual.add(connection.zAdd("myset".getBytes(), 1, "James".getBytes()));
actual.add(connection.zRevRangeByScore("myset", 0d, 3d));
verifyResults(Arrays
.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("Bob", "James")) }));
}
@Test
@@ -1856,8 +1890,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset".getBytes(), 2, "Bob".getBytes()));
actual.add(connection.zAdd("myset".getBytes(), 1, "James".getBytes()));
actual.add(connection.zRevRangeByScoreWithScores("myset", 0d, 3d, 0, 1));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(
Arrays.asList(new DefaultStringTuple("Bob".getBytes(), "Bob", 2d))) }));
verifyResults(Arrays.asList(new Object[] { true, true,
new LinkedHashSet<>(Arrays.asList(new DefaultStringTuple("Bob".getBytes(), "Bob", 2d))) }));
}
@Test
@@ -1886,8 +1920,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("myset", 1, "James"));
actual.add(connection.zRem("myset", "James"));
actual.add(connection.zRange("myset", 0L, -1L));
verifyResults(
Arrays.asList(new Object[] { true, true, 1L, new LinkedHashSet<>(Arrays.asList("Bob")) }));
verifyResults(Arrays.asList(new Object[] { true, true, 1L, new LinkedHashSet<>(Arrays.asList("Bob")) }));
}
@Test
@@ -1927,7 +1960,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zRemRangeByLex("myset", Range.range().gte("alpha").lte("omega")));
actual.add(connection.zRange("myset", 0L, -1L));
verifyResults(Arrays.asList( true, true, true, true, true, true, true, true, true, true, 6L, new LinkedHashSet<>(Arrays.asList("ALPHA", "aaaa", "zap", "zip"))));
verifyResults(Arrays.asList(true, true, true, true, true, true, true, true, true, true, 6L,
new LinkedHashSet<>(Arrays.asList("ALPHA", "aaaa", "zap", "zip"))));
}
@Test
@@ -1937,8 +1971,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zRemRangeByScore("myset", 0d, 1d));
actual.add(connection.zRange("myset", 0L, -1L));
verifyResults(
Arrays.asList(new Object[] { true, true, 1L, new LinkedHashSet<>(Collections
.singletonList("Bob")) }));
Arrays.asList(new Object[] { true, true, 1L, new LinkedHashSet<>(Collections.singletonList("Bob")) }));
}
@Test
@@ -1981,8 +2014,8 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.zAdd("otherset", 4, "James"));
actual.add(connection.zUnionStore("thirdset", Aggregate.MAX, new int[] { 2, 3 }, "myset", "otherset"));
actual.add(connection.zRangeWithScores("thirdset", 0, -1));
verifyResults(Arrays.asList(new Object[] { true, true, true, true, true, 3L, new LinkedHashSet<>(Arrays.asList(
new DefaultStringTuple("Bob".getBytes(), "Bob", 4d),
verifyResults(Arrays.asList(new Object[] { true, true, true, true, true, 3L,
new LinkedHashSet<>(Arrays.asList(new DefaultStringTuple("Bob".getBytes(), "Bob", 4d),
new DefaultStringTuple("Joe".getBytes(), "Joe", 8d),
new DefaultStringTuple("James".getBytes(), "James", 12d))) }));
}
@@ -2054,8 +2087,7 @@ public abstract class AbstractConnectionIntegrationTests {
actual.add(connection.hSet("test", "key", "2"));
actual.add(connection.hSet("test", "key2", "2"));
actual.add(connection.hKeys("test"));
verifyResults(
Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("key", "key2")) }));
verifyResults(Arrays.asList(new Object[] { true, true, new LinkedHashSet<>(Arrays.asList("key", "key2")) }));
}
@Test

View File

@@ -1233,14 +1233,14 @@ public class DefaultStringRedisConnectionTests {
@Test
public void testZAddBytes() {
doReturn(true).when(nativeConnection).zAdd(fooBytes, 3d, barBytes);
doReturn(true).when(nativeConnection).zAdd(eq(fooBytes), eq(3d), eq(barBytes), any());
actual.add(connection.zAdd(fooBytes, 3d, barBytes));
verifyResults(Collections.singletonList(true));
}
@Test
public void testZAdd() {
doReturn(true).when(nativeConnection).zAdd(fooBytes, 3d, barBytes);
doReturn(true).when(nativeConnection).zAdd(eq(fooBytes), eq(3d), eq(barBytes), any());
actual.add(connection.zAdd(foo, 3d, bar));
verifyResults(Collections.singletonList(true));
}
@@ -1249,7 +1249,7 @@ public class DefaultStringRedisConnectionTests {
public void testZAddMultipleBytes() {
Set<Tuple> tuples = new HashSet<>();
tuples.add(new DefaultTuple(barBytes, 3.0));
doReturn(1L).when(nativeConnection).zAdd(fooBytes, tuples);
doReturn(1L).when(nativeConnection).zAdd(eq(fooBytes), eq(tuples), any());
actual.add(connection.zAdd(fooBytes, tuples));
verifyResults(Collections.singletonList(1L));
}
@@ -1260,7 +1260,7 @@ public class DefaultStringRedisConnectionTests {
tuples.add(new DefaultTuple(barBytes, 3.0));
Set<StringTuple> strTuples = new HashSet<>();
strTuples.add(new DefaultStringTuple(barBytes, bar, 3.0));
doReturn(1L).when(nativeConnection).zAdd(fooBytes, tuples);
doReturn(1L).when(nativeConnection).zAdd(eq(fooBytes), eq(tuples), any());
actual.add(connection.zAdd(foo, strTuples));
verifyResults(Collections.singletonList(1L));
}

View File

@@ -255,8 +255,8 @@ class RedisConnectionUnitTests {
return delegate.getNativeConnection();
}
public Long zAdd(byte[] key, Set<Tuple> tuples) {
return delegate.zAdd(key, tuples);
public Long zAdd(byte[] key, Set<Tuple> tuples, ZAddArgs args) {
return delegate.zAdd(key, tuples, args);
}
public void subscribe(MessageListener listener, byte[]... channels) {

View File

@@ -17,9 +17,14 @@ package org.springframework.data.redis.core;
import static org.mockito.ArgumentMatchers.*;
import java.util.Collections;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.redis.connection.RedisZSetCommands.Range;
import org.springframework.data.redis.connection.RedisZSetCommands.ZAddArgs;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
* Unit tests for {@link DefaultZSetOperations}.
@@ -46,4 +51,21 @@ class DefaultZSetOperationsUnitTests {
template.verify().zRemRangeByLex(eq(template.serializeKey("key")), eq(range));
}
@Test // GH-1794
void delegatesAddIfAbsent() {
zSetOperations.addIfAbsent("key", "value", 1D);
template.verify().zAdd(eq(template.serializeKey("key")), eq(1D), eq(template.serializeKey("value")),
eq(ZAddArgs.ifNotExists()));
}
@Test // GH-1794
void delegatesAddIfAbsentForTuples() {
zSetOperations.addIfAbsent("key", Collections.singleton(TypedTuple.of("value", 1D)));
template.verify().zAdd(eq(template.serializeKey("key")), any(Set.class), eq(ZAddArgs.ifNotExists()));
}
}

View File

@@ -47,6 +47,7 @@ import org.springframework.data.redis.test.extension.parametrized.ParameterizedR
* @author Thomas Darimont
* @author Mark Paluch
* @author Andrey Shlykov
* @author Christoph Strobl
*/
public abstract class AbstractRedisZSetTestIntegration<T> extends AbstractRedisCollectionIntegrationTests<T> {
@@ -677,4 +678,13 @@ public abstract class AbstractRedisZSetTestIntegration<T> extends AbstractRedisC
cursor.close();
}
@ParameterizedRedisTest // GH-1794
void testZAddIfAbsentWorks() {
T t1 = getT();
assertThat(zSet.addIfAbsent(t1, 1)).isTrue();
assertThat(zSet.addIfAbsent(t1, 1)).isFalse();
}
}