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 dd02f85661..0e86a7deef 100644
--- a/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/DefaultedRedisConnection.java
@@ -1146,6 +1146,20 @@ default Long zInterStore(byte[] destKey, byte[]... sets) {
return zSetCommands().zInterStore(destKey, sets);
}
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default Long zInterCard(byte[]... sets) {
+ return zSetCommands().zInterCard(sets);
+ }
+
+ /** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
+ @Override
+ @Deprecated
+ default Long zInterCard(long limit, byte[]... sets) {
+ return zSetCommands().zInterCard(limit, sets);
+ }
+
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
@Override
@Deprecated
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 2518918b09..716bf6ccc2 100644
--- a/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/RedisZSetCommands.java
@@ -42,6 +42,7 @@
* @author Mark Paluch
* @author Andrey Shlykov
* @author Shyngys Sapraliyev
+ * @author GyeongHoe Koo
* @see RedisCommands
*/
@NullUnmarked
@@ -1085,6 +1086,28 @@ default Long zInterStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate,
Long zInterStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate, @NonNull Weights weights,
byte @NonNull [] @NonNull... sets);
+ /**
+ * Get the number of members in the intersection of sorted {@code sets}.
+ *
+ * @param sets must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 4.0
+ * @see Redis Documentation: ZINTERCARD
+ */
+ Long zInterCard(byte @NonNull [] @NonNull... sets);
+
+ /**
+ * Get the number of members in the intersection of sorted {@code sets}.
+ *
+ * @param sets must not be {@literal null}.
+ * @param limit the maximum cardinality to compute. If the intersection has more than {@code limit} elements,
+ * the computation stops and returns {@code limit}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @since 4.0
+ * @see Redis Documentation: ZINTERCARD
+ */
+ Long zInterCard(long limit, byte @NonNull [] @NonNull... sets);
+
/**
* Union sorted {@code sets}.
*
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 352230a8b6..0c2990b6ea 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
@@ -1086,6 +1086,42 @@ public Long zUnionStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate,
throw new InvalidDataAccessApiUsageException("ZUNIONSTORE can only be executed when all keys map to the same slot");
}
+ @Override
+ public Long zInterCard(byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) {
+
+ try {
+ return connection.getCluster().zintercard(sets);
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ throw new InvalidDataAccessApiUsageException("ZINTERCARD can only be executed when all keys map to the same slot");
+ }
+
+ @Override
+ public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) {
+
+ try {
+ return connection.getCluster().zintercard(limit, sets);
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ }
+
+ throw new InvalidDataAccessApiUsageException("ZINTERCARD can only be executed when all keys map to the same slot");
+ }
+
@Override
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @NonNull ScanOptions options) {
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 a90cfbde86..b7d6c63600 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
@@ -564,6 +564,24 @@ public Long zUnionStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... s
return connection.invoke().just(Jedis::zunionstore, PipelineBinaryCommands::zunionstore, destKey, sets);
}
+ @Override
+ public Long zInterCard(byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ return connection.invoke().just(Jedis::zintercard, PipelineBinaryCommands::zintercard, sets);
+ }
+
+ @Override
+ public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ return connection.invoke().just(Jedis::zintercard, PipelineBinaryCommands::zintercard, limit, sets);
+ }
+
@Override
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @NonNull ScanOptions options) {
return zScan(key, CursorId.initial(), options);
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 e1a02dff78..15b96e9cc0 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
@@ -532,6 +532,24 @@ public Long zUnionStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... s
return connection.invoke().just(RedisSortedSetAsyncCommands::zunionstore, destKey, sets);
}
+ @Override
+ public Long zInterCard(byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ return connection.invoke().just(RedisSortedSetAsyncCommands::zintercard, sets);
+ }
+
+ @Override
+ public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
+
+ Assert.notNull(sets, "Sets must not be null");
+ Assert.noNullElements(sets, "Sets must not contain null elements");
+
+ return connection.invoke().just(RedisSortedSetAsyncCommands::zintercard, limit, sets);
+ }
+
@Override
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @Nullable ScanOptions options) {
return zScan(key, CursorId.initial(), options != null ? options : ScanOptions.NONE);
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 be4573e890..70057c4b0c 100644
--- a/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java
@@ -46,6 +46,7 @@
* @author Shyngys Sapraliyev
* @author John Blum
* @author Gunha Hwang
+ * @author GyeongHoe Koo
*/
@NullUnmarked
class DefaultZSetOperations extends AbstractOperations implements ZSetOperations {
@@ -588,6 +589,22 @@ public Long intersectAndStore(@NonNull K key, Collection<@NonNull K> otherKeys,
return execute(connection -> connection.zInterStore(rawDestKey, aggregate, weights, rawKeys));
}
+ @Override
+ public Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys) {
+
+ byte[][] rawKeys = rawKeys(key, otherKeys);
+
+ return execute(connection -> connection.zInterCard(rawKeys));
+ }
+
+ @Override
+ public Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, long limit) {
+
+ byte[][] rawKeys = rawKeys(key, otherKeys);
+
+ return execute(connection -> connection.zInterCard(limit, rawKeys));
+ }
+
@Override
public Set union(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys) {
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 c5a640bb87..fd906fbcbe 100644
--- a/src/main/java/org/springframework/data/redis/core/ZSetOperations.java
+++ b/src/main/java/org/springframework/data/redis/core/ZSetOperations.java
@@ -44,6 +44,7 @@
* @author Andrey Shlykov
* @author Shyngys Sapraliyev
* @author Gunha Hwang
+ * @author GyeongHoe Koo
*/
@NullUnmarked
public interface ZSetOperations {
@@ -766,6 +767,40 @@ default Long intersectAndStore(@NonNull K key, @NonNull Collection<@NonNull K> o
Long intersectAndStore(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, @NonNull K destKey,
@NonNull Aggregate aggregate, @NonNull Weights weights);
+ /**
+ * Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
+ *
+ * @param key must not be {@literal null}.
+ * @param otherKey must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: ZINTERCARD
+ */
+ default Long intersectSize(@NonNull K key, @NonNull K otherKey) {
+ return intersectSize(key, Collections.singleton(otherKey));
+ }
+
+ /**
+ * Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
+ *
+ * @param key must not be {@literal null}.
+ * @param otherKeys must not be {@literal null}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: ZINTERCARD
+ */
+ Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys);
+
+ /**
+ * Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
+ *
+ * @param key must not be {@literal null}.
+ * @param otherKeys must not be {@literal null}.
+ * @param limit the maximum cardinality to compute. If the intersection has more than {@code limit} elements,
+ * the computation stops and returns {@code limit}.
+ * @return {@literal null} when used in pipeline / transaction.
+ * @see Redis Documentation: ZINTERCARD
+ */
+ Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, long limit);
+
/**
* Union sorted {@code sets}.
*
diff --git a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java
index 0fe579b153..f141bf5e78 100644
--- a/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java
+++ b/src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java
@@ -24,6 +24,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -608,6 +609,55 @@ void testZsetIntersectWithAggregateWeights() {
assertThat(zSetOps.score(key1, value1)).isCloseTo(6.0, offset(0.1));
}
+ @Test // GH-3253
+ @EnabledOnCommand("ZINTERCARD")
+ void testZsetIntersectSize() {
+
+ K key1 = keyFactory.instance();
+ K key2 = keyFactory.instance();
+ K key3 = keyFactory.instance();
+
+ V value1 = valueFactory.instance();
+ V value2 = valueFactory.instance();
+ V value3 = valueFactory.instance();
+
+ zSetOps.add(key1, value1, 1.0);
+ zSetOps.add(key1, value2, 2.0);
+ zSetOps.add(key1, value3, 3.0);
+
+ zSetOps.add(key2, value2, 4.0);
+ zSetOps.add(key2, value3, 5.0);
+
+ zSetOps.add(key3, value3, 6.0);
+
+ // Test basic intersectSize
+ assertThat(zSetOps.intersectSize(key1, key2)).isEqualTo(2L);
+ assertThat(zSetOps.intersectSize(key1, Collections.singletonList(key2))).isEqualTo(2L);
+
+ // Test with 3 sets
+ assertThat(zSetOps.intersectSize(key1, List.of(key2, key3))).isEqualTo(1L);
+
+ // Test with limit
+ assertThat(zSetOps.intersectSize(key1, Collections.singletonList(key2), 1L)).isEqualTo(1L);
+ }
+
+ @Test // GH-3253
+ @EnabledOnCommand("ZINTERCARD")
+ void testZsetIntersectSizeEmptySet() {
+
+ K key1 = keyFactory.instance();
+ K key2 = keyFactory.instance();
+
+ V value1 = valueFactory.instance();
+ V value2 = valueFactory.instance();
+
+ zSetOps.add(key1, value1, 1.0);
+ zSetOps.add(key2, value2, 2.0);
+
+ // No intersection
+ assertThat(zSetOps.intersectSize(key1, key2)).isEqualTo(0L);
+ }
+
@Test // GH-2042
@EnabledOnCommand("ZUNION")
void testZsetUnion() {