diff --git a/pom.xml b/pom.xml
index b20e7c9dcc..563f362e43 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-redis
- 2.5.0-SNAPSHOT
+ 2.5.0-GH-1794-SNAPSHOT
Spring Data Redis
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
index 90976449bb..277341d3ed 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultStringRedisConnection.java
@@ -1322,20 +1322,20 @@ public void watch(byte[]... keys) {
/*
* (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 tuples) {
- return convertAndReturn(delegate.zAdd(key, tuples), Converters.identityConverter());
+ public Long zAdd(byte[] key, Set tuples, ZAddArgs args) {
+ return convertAndReturn(delegate.zAdd(key, tuples, args), Converters.identityConverter());
}
/*
@@ -2686,20 +2686,20 @@ public Long touch(String... keys) {
/*
* (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 tuples) {
- return zAdd(serialize(key), stringTupleToTuple.convert(tuples));
+ public Long zAdd(String key, Set tuples, ZAddArgs args) {
+ return zAdd(serialize(key), stringTupleToTuple.convert(tuples), args);
}
/*
diff --git a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
index 41c50d7f18..2b09e98a6f 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -873,15 +873,15 @@ default Cursor sScan(byte[] key, ScanOptions options) {
/** @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 tuples) {
- return zSetCommands().zAdd(key, tuples);
+ default Long zAdd(byte[] key, Set tuples, ZAddArgs args) {
+ return zSetCommands().zAdd(key, tuples, args);
}
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java
index 27e24253cf..ed307120c2 100644
--- a/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/ReactiveZSetCommands.java
@@ -63,9 +63,11 @@ class ZAddCommand extends KeyCommand {
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 tuples, boolean upsert, boolean returnTotalChanged,
- boolean incr) {
+ boolean incr, boolean gt, boolean lt) {
super(key);
@@ -73,6 +75,8 @@ private ZAddCommand(@Nullable ByteBuffer key, List tuples, boolean upsert
this.upsert = upsert;
this.returnTotalChanged = returnTotalChanged;
this.incr = incr;
+ this.gt = gt;
+ this.lt = lt;
}
/**
@@ -98,7 +102,7 @@ public static ZAddCommand tuples(Collection extends Tuple> tuples) {
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 ZAddCommand to(ByteBuffer key) {
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 ZAddCommand to(ByteBuffer key) {
* @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 ZAddCommand xx() {
* @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 ZAddCommand nx() {
* @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 ZAddCommand ch() {
* @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 boolean isIncr() {
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
*/
diff --git a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
index d26e5af2f6..bb48bc551f 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
@@ -17,6 +17,7 @@
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 static Limit unlimited() {
}
}
+ /**
+ * {@code ZADD} specific arguments.
+ * Looking of the {@code INCR} flag? Use the {@code ZINCRBY} operation instead.
+ *
+ * @since 2.3
+ * @see Redis Documentation: ZADD
+ */
+ class ZAddArgs {
+
+ private static final ZAddArgs NONE = new ZAddArgs(Collections.emptySet());
+
+ private final Set flags;
+
+ private ZAddArgs(Set 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 static Limit unlimited() {
* @see Redis Documentation: ZADD
*/
@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 Redis Documentation: ZADD
+ */
+ @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 static Limit unlimited() {
* @see Redis Documentation: ZADD
*/
@Nullable
- Long zAdd(byte[] key, Set tuples);
+ default Long zAdd(byte[] key, Set 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 Redis Documentation: ZADD
+ */
+ Long zAdd(byte[] key, Set tuples, ZAddArgs args);
/**
* Remove {@code values} from sorted set. Return number of removed elements.
diff --git a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
index 8633ea3a57..b5bd7193e8 100644
--- a/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/StringRedisConnection.java
@@ -1102,7 +1102,24 @@ default Long lPos(String key, String element) {
* @see Redis Documentation: ZADD
* @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 Redis Documentation: ZADD
+ * @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 @@ default Long lPos(String key, String element) {
* @see Redis Documentation: ZADD
* @see RedisZSetCommands#zAdd(byte[], Set)
*/
- Long zAdd(String key, Set tuples);
+ default Long zAdd(String key, Set 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 Redis Documentation: ZADD
+ * @see RedisZSetCommands#zAdd(byte[], Set, ZAddArgs)
+ */
+ @Nullable
+ Long zAdd(String key, Set tuples, ZAddArgs args);
/**
* Remove {@code values} from sorted set. Return number of removed elements.
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java
index de3470c183..376f7c8b40 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java
@@ -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 @@ public Boolean zAdd(byte[] key, double score, byte[] value) {
/*
* (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 tuples) {
+ public Long zAdd(byte[] key, Set 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);
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
index 5e34e143e5..1346279dc4 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java
@@ -24,6 +24,7 @@
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.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;
@@ -651,6 +653,42 @@ public static GeoUnit toGeoUnit(Metric metric) {
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}.
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java
index 46c3efd722..2f6b4f67e7 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java
@@ -21,12 +21,14 @@
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 tuples) {
+ public Long zAdd(byte[] key, Set 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));
}
/*
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java
index 1cc6ae1c3f..8696c408f0 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveZSetCommands.java
@@ -102,6 +102,12 @@ public Flux> zAdd(Publisher co
} 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[] values = (ScoredValue[]) command.getTuples().stream()
diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java
index 9d011364ce..44ff5a0376 100644
--- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java
@@ -26,6 +26,7 @@
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 tuples) {
+ public Long zAdd(byte[] key, Set 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 @@ private static ZStoreArgs zStoreArgs(@Nullable Aggregate aggregate, Weights weig
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;
+ }
+
}
diff --git a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java
index 5baa3572ef..8ff3df2eee 100644
--- a/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/BoundZSetOperations.java
@@ -48,6 +48,18 @@ public interface BoundZSetOperations extends BoundKeyOperations {
@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 Redis Documentation: ZADD NX
+ */
+ @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 extends BoundKeyOperations {
@Nullable
Long add(Set> 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 Redis Documentation: ZADD NX
+ */
+ @Nullable
+ Long addIfAbsent(Set> tuples);
+
/**
* Remove {@code values} from sorted set. Return number of removed elements.
*
diff --git a/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java
index fc3b5f9b73..f9a4a42bee 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultBoundZSetOperations.java
@@ -60,6 +60,15 @@ public Boolean add(V value, double score) {
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 @@ public Long add(Set> tuples) {
return ops.add(getKey(), tuples);
}
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.redis.core.BoundZSetOperations#addIfAbsent(java.util.Set)
+ */
+ @Override
+ public Long addIfAbsent(Set> tuples) {
+ return ops.addIfAbsent(getKey(), tuples);
+ }
+
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.BoundZSetOperations#incrementScore(java.lang.Object, double)
diff --git a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java
index 1c14991326..5c88953240 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java
@@ -24,6 +24,8 @@
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 extends AbstractOperations 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 @@ public Boolean add(K key, V value, double score) {
*/
@Override
public Long add(K key, Set> 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> 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> tuples, ZAddArgs args) {
byte[] rawKey = rawKey(key);
Set rawValues = rawTupleValues(tuples);
- return execute(connection -> connection.zAdd(rawKey, rawValues), true);
+ return execute(connection -> connection.zAdd(rawKey, rawValues, args), true);
}
/*
diff --git a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java
index 4aa5c82634..822b8a47a7 100644
--- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java
@@ -47,6 +47,19 @@ interface TypedTuple extends Comparable> {
@Nullable
Double getScore();
+
+ /**
+ * Create a new {@link TypedTuple}.
+ *
+ * @param value must not be {@literal null}.
+ * @param score can be {@literal null}.
+ * @param
+ * @return new instance of {@link TypedTuple}.
+ * @since 2.5
+ */
+ static TypedTuple of(V value, @Nullable Double score) {
+ return new DefaultTypedTuple<>(value, score);
+ }
}
/**
@@ -61,6 +74,19 @@ interface TypedTuple extends Comparable> {
@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 Redis Documentation: ZADD NX
+ */
+ @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 @@ interface TypedTuple extends Comparable> {
@Nullable
Long add(K key, Set> 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 Redis Documentation: ZADD NX
+ */
+ @Nullable
+ Long addIfAbsent(K key, Set> tuples);
+
/**
* Remove {@code values} from sorted set. Return number of removed elements.
*
diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java
index 4fda95ea62..d9b9add8ad 100644
--- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java
+++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisZSet.java
@@ -288,6 +288,18 @@ public boolean add(E e, double score) {
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()
diff --git a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java
index e6af859e57..86fc8646ef 100644
--- a/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java
+++ b/src/main/java/org/springframework/data/redis/support/collections/RedisZSet.java
@@ -144,6 +144,27 @@ default Set reverseRangeByLex(Range range) {
*/
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.
diff --git a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
index 5899c66362..9b8a179d79 100644
--- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java
@@ -37,7 +37,6 @@
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;
@@ -57,6 +56,7 @@
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;
@@ -1312,8 +1312,7 @@ void testLSet() {
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
@@ -1489,8 +1488,7 @@ void testSAddMultiple() {
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
@@ -1562,8 +1560,7 @@ void testSPop() {
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
@@ -1643,8 +1640,7 @@ void testSUnion() {
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
@@ -1655,8 +1651,8 @@ void testSUnionStore() {
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
@@ -1666,8 +1662,7 @@ void testZAddAndZRange() {
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
@@ -1680,8 +1675,52 @@ void testZAddMultiple() {
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 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 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
@@ -1781,8 +1820,7 @@ void testZRangeByScore() {
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
@@ -1790,8 +1828,7 @@ void testZRangeByScoreOffsetCount() {
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
@@ -1799,8 +1836,8 @@ void testZRangeByScoreWithScores() {
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
@@ -1808,8 +1845,8 @@ void testZRangeByScoreWithScoresOffsetCount() {
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
@@ -1817,8 +1854,7 @@ void testZRevRange() {
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
@@ -1836,8 +1872,7 @@ void testZRevRangeByScoreOffsetCount() {
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
@@ -1845,8 +1880,7 @@ void testZRevRangeByScore() {
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
@@ -1854,8 +1888,8 @@ void testZRevRangeByScoreWithScoresOffsetCount() {
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
@@ -1884,8 +1918,7 @@ void testZRem() {
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
@@ -1925,7 +1958,8 @@ void testZRemRangeByLex() {
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
@@ -1935,8 +1969,7 @@ void testZRemRangeByScore() {
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
@@ -1979,8 +2012,8 @@ void testZUnionStoreAggWeights() {
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))) }));
}
@@ -2052,8 +2085,7 @@ void testHKeys() {
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
diff --git a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
index b5ac42a5db..0bce55b824 100644
--- a/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/DefaultStringRedisConnectionTests.java
@@ -1233,14 +1233,14 @@ public void testType() {
@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 void testZAdd() {
public void testZAddMultipleBytes() {
Set 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 void testZAddMultiple() {
tuples.add(new DefaultTuple(barBytes, 3.0));
Set 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));
}
diff --git a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
index b5a995fea0..76e5252dea 100644
--- a/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/connection/RedisConnectionUnitTests.java
@@ -255,8 +255,8 @@ public Object getNativeConnection() {
return delegate.getNativeConnection();
}
- public Long zAdd(byte[] key, Set tuples) {
- return delegate.zAdd(key, tuples);
+ public Long zAdd(byte[] key, Set tuples, ZAddArgs args) {
+ return delegate.zAdd(key, tuples, args);
}
public void subscribe(MessageListener listener, byte[]... channels) {
diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java
index 926d1138f7..dbc2df4367 100644
--- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java
+++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsUnitTests.java
@@ -17,9 +17,14 @@
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 @@ void delegatesRemoveRangeByLex() {
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()));
+ }
}
diff --git a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java
index 8e07251ca4..ee42ccb61c 100644
--- a/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java
+++ b/src/test/java/org/springframework/data/redis/support/collections/AbstractRedisZSetTestIntegration.java
@@ -47,6 +47,7 @@
* @author Thomas Darimont
* @author Mark Paluch
* @author Andrey Shlykov
+ * @author Christoph Strobl
*/
public abstract class AbstractRedisZSetTestIntegration extends AbstractRedisCollectionIntegrationTests {
@@ -677,4 +678,13 @@ void testScanWorksCorrectly() throws IOException {
cursor.close();
}
+
+ @ParameterizedRedisTest // GH-1794
+ void testZAddIfAbsentWorks() {
+
+ T t1 = getT();
+
+ assertThat(zSet.addIfAbsent(t1, 1)).isTrue();
+ assertThat(zSet.addIfAbsent(t1, 1)).isFalse();
+ }
}