Skip to content

Commit 4680684

Browse files
committed
Support ZINTERCARD
Resolves: #3253 Signed-off-by: heka1024 <[email protected]>
1 parent 9787a5a commit 4680684

File tree

8 files changed

+211
-0
lines changed

8 files changed

+211
-0
lines changed

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,6 +1146,20 @@ default Long zInterStore(byte[] destKey, byte[]... sets) {
11461146
return zSetCommands().zInterStore(destKey, sets);
11471147
}
11481148

1149+
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
1150+
@Override
1151+
@Deprecated
1152+
default Long zInterCard(byte[]... sets) {
1153+
return zSetCommands().zInterCard(sets);
1154+
}
1155+
1156+
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
1157+
@Override
1158+
@Deprecated
1159+
default Long zInterCard(long limit, byte[]... sets) {
1160+
return zSetCommands().zInterCard(limit, sets);
1161+
}
1162+
11491163
/** @deprecated in favor of {@link RedisConnection#zSetCommands()}}. */
11501164
@Override
11511165
@Deprecated

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @author Mark Paluch
4343
* @author Andrey Shlykov
4444
* @author Shyngys Sapraliyev
45+
* @author GyeongHoe Koo
4546
* @see RedisCommands
4647
*/
4748
@NullUnmarked
@@ -1085,6 +1086,28 @@ default Long zInterStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate,
10851086
Long zInterStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate, @NonNull Weights weights,
10861087
byte @NonNull [] @NonNull... sets);
10871088

1089+
/**
1090+
* Get the number of members in the intersection of sorted {@code sets}.
1091+
*
1092+
* @param sets must not be {@literal null}.
1093+
* @return {@literal null} when used in pipeline / transaction.
1094+
* @since 4.0
1095+
* @see <a href="https://redis.io/commands/zintercard">Redis Documentation: ZINTERCARD</a>
1096+
*/
1097+
Long zInterCard(byte @NonNull [] @NonNull... sets);
1098+
1099+
/**
1100+
* Get the number of members in the intersection of sorted {@code sets}.
1101+
*
1102+
* @param sets must not be {@literal null}.
1103+
* @param limit the maximum cardinality to compute. If the intersection has more than {@code limit} elements,
1104+
* the computation stops and returns {@code limit}.
1105+
* @return {@literal null} when used in pipeline / transaction.
1106+
* @since 4.0
1107+
* @see <a href="https://redis.io/commands/zintercard">Redis Documentation: ZINTERCARD</a>
1108+
*/
1109+
Long zInterCard(long limit, byte @NonNull [] @NonNull... sets);
1110+
10881111
/**
10891112
* Union sorted {@code sets}.
10901113
*

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,42 @@ public Long zUnionStore(byte @NonNull [] destKey, @NonNull Aggregate aggregate,
10861086
throw new InvalidDataAccessApiUsageException("ZUNIONSTORE can only be executed when all keys map to the same slot");
10871087
}
10881088

1089+
@Override
1090+
public Long zInterCard(byte @NonNull [] @NonNull... sets) {
1091+
1092+
Assert.notNull(sets, "Sets must not be null");
1093+
Assert.noNullElements(sets, "Sets must not contain null elements");
1094+
1095+
if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) {
1096+
1097+
try {
1098+
return connection.getCluster().zintercard(sets);
1099+
} catch (Exception ex) {
1100+
throw convertJedisAccessException(ex);
1101+
}
1102+
}
1103+
1104+
throw new InvalidDataAccessApiUsageException("ZINTERCARD can only be executed when all keys map to the same slot");
1105+
}
1106+
1107+
@Override
1108+
public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
1109+
1110+
Assert.notNull(sets, "Sets must not be null");
1111+
Assert.noNullElements(sets, "Sets must not contain null elements");
1112+
1113+
if (ClusterSlotHashUtil.isSameSlotForAllKeys(sets)) {
1114+
1115+
try {
1116+
return connection.getCluster().zintercard(limit, sets);
1117+
} catch (Exception ex) {
1118+
throw convertJedisAccessException(ex);
1119+
}
1120+
}
1121+
1122+
throw new InvalidDataAccessApiUsageException("ZINTERCARD can only be executed when all keys map to the same slot");
1123+
}
1124+
10891125
@Override
10901126
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @NonNull ScanOptions options) {
10911127

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,24 @@ public Long zUnionStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... s
564564
return connection.invoke().just(Jedis::zunionstore, PipelineBinaryCommands::zunionstore, destKey, sets);
565565
}
566566

567+
@Override
568+
public Long zInterCard(byte @NonNull [] @NonNull... sets) {
569+
570+
Assert.notNull(sets, "Sets must not be null");
571+
Assert.noNullElements(sets, "Sets must not contain null elements");
572+
573+
return connection.invoke().just(Jedis::zintercard, PipelineBinaryCommands::zintercard, sets);
574+
}
575+
576+
@Override
577+
public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
578+
579+
Assert.notNull(sets, "Sets must not be null");
580+
Assert.noNullElements(sets, "Sets must not contain null elements");
581+
582+
return connection.invoke().just(Jedis::zintercard, PipelineBinaryCommands::zintercard, limit, sets);
583+
}
584+
567585
@Override
568586
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @NonNull ScanOptions options) {
569587
return zScan(key, CursorId.initial(), options);

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceZSetCommands.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,6 +532,24 @@ public Long zUnionStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull... s
532532
return connection.invoke().just(RedisSortedSetAsyncCommands::zunionstore, destKey, sets);
533533
}
534534

535+
@Override
536+
public Long zInterCard(byte @NonNull [] @NonNull... sets) {
537+
538+
Assert.notNull(sets, "Sets must not be null");
539+
Assert.noNullElements(sets, "Sets must not contain null elements");
540+
541+
return connection.invoke().just(RedisSortedSetAsyncCommands::zintercard, sets);
542+
}
543+
544+
@Override
545+
public Long zInterCard(long limit, byte @NonNull [] @NonNull... sets) {
546+
547+
Assert.notNull(sets, "Sets must not be null");
548+
Assert.noNullElements(sets, "Sets must not contain null elements");
549+
550+
return connection.invoke().just(RedisSortedSetAsyncCommands::zintercard, limit, sets);
551+
}
552+
535553
@Override
536554
public Cursor<@NonNull Tuple> zScan(byte @NonNull [] key, @Nullable ScanOptions options) {
537555
return zScan(key, CursorId.initial(), options != null ? options : ScanOptions.NONE);

src/main/java/org/springframework/data/redis/core/DefaultZSetOperations.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
* @author Shyngys Sapraliyev
4747
* @author John Blum
4848
* @author Gunha Hwang
49+
* @author GyeongHoe Koo
4950
*/
5051
@NullUnmarked
5152
class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZSetOperations<K, V> {
@@ -588,6 +589,22 @@ public Long intersectAndStore(@NonNull K key, Collection<@NonNull K> otherKeys,
588589
return execute(connection -> connection.zInterStore(rawDestKey, aggregate, weights, rawKeys));
589590
}
590591

592+
@Override
593+
public Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys) {
594+
595+
byte[][] rawKeys = rawKeys(key, otherKeys);
596+
597+
return execute(connection -> connection.zInterCard(rawKeys));
598+
}
599+
600+
@Override
601+
public Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, long limit) {
602+
603+
byte[][] rawKeys = rawKeys(key, otherKeys);
604+
605+
return execute(connection -> connection.zInterCard(limit, rawKeys));
606+
}
607+
591608
@Override
592609
public Set<V> union(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys) {
593610

src/main/java/org/springframework/data/redis/core/ZSetOperations.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
* @author Andrey Shlykov
4545
* @author Shyngys Sapraliyev
4646
* @author Gunha Hwang
47+
* @author GyeongHoe Koo
4748
*/
4849
@NullUnmarked
4950
public interface ZSetOperations<K, V> {
@@ -766,6 +767,40 @@ default Long intersectAndStore(@NonNull K key, @NonNull Collection<@NonNull K> o
766767
Long intersectAndStore(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, @NonNull K destKey,
767768
@NonNull Aggregate aggregate, @NonNull Weights weights);
768769

770+
/**
771+
* Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
772+
*
773+
* @param key must not be {@literal null}.
774+
* @param otherKey must not be {@literal null}.
775+
* @return {@literal null} when used in pipeline / transaction.
776+
* @see <a href="https://redis.io/commands/zintercard">Redis Documentation: ZINTERCARD</a>
777+
*/
778+
default Long intersectSize(@NonNull K key, @NonNull K otherKey) {
779+
return intersectSize(key, Collections.singleton(otherKey));
780+
}
781+
782+
/**
783+
* Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
784+
*
785+
* @param key must not be {@literal null}.
786+
* @param otherKeys must not be {@literal null}.
787+
* @return {@literal null} when used in pipeline / transaction.
788+
* @see <a href="https://redis.io/commands/zintercard">Redis Documentation: ZINTERCARD</a>
789+
*/
790+
Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys);
791+
792+
/**
793+
* Returns numbers of members in the sorted set resulting from the intersection of the sorted sets stored at {@code key} and {@code otherKey}.
794+
*
795+
* @param key must not be {@literal null}.
796+
* @param otherKeys must not be {@literal null}.
797+
* @param limit the maximum cardinality to compute. If the intersection has more than {@code limit} elements,
798+
* the computation stops and returns {@code limit}.
799+
* @return {@literal null} when used in pipeline / transaction.
800+
* @see <a href="https://redis.io/commands/zintercard">Redis Documentation: ZINTERCARD</a>
801+
*/
802+
Long intersectSize(@NonNull K key, @NonNull Collection<@NonNull K> otherKeys, long limit);
803+
769804
/**
770805
* Union sorted {@code sets}.
771806
*

src/test/java/org/springframework/data/redis/core/DefaultZSetOperationsIntegrationTests.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Collection;
2525
import java.util.Collections;
2626
import java.util.HashSet;
27+
import java.util.List;
2728
import java.util.Set;
2829
import java.util.concurrent.TimeUnit;
2930

@@ -608,6 +609,55 @@ void testZsetIntersectWithAggregateWeights() {
608609
assertThat(zSetOps.score(key1, value1)).isCloseTo(6.0, offset(0.1));
609610
}
610611

612+
@Test // GH-3253
613+
@EnabledOnCommand("ZINTERCARD")
614+
void testZsetIntersectSize() {
615+
616+
K key1 = keyFactory.instance();
617+
K key2 = keyFactory.instance();
618+
K key3 = keyFactory.instance();
619+
620+
V value1 = valueFactory.instance();
621+
V value2 = valueFactory.instance();
622+
V value3 = valueFactory.instance();
623+
624+
zSetOps.add(key1, value1, 1.0);
625+
zSetOps.add(key1, value2, 2.0);
626+
zSetOps.add(key1, value3, 3.0);
627+
628+
zSetOps.add(key2, value2, 4.0);
629+
zSetOps.add(key2, value3, 5.0);
630+
631+
zSetOps.add(key3, value3, 6.0);
632+
633+
// Test basic intersectSize
634+
assertThat(zSetOps.intersectSize(key1, key2)).isEqualTo(2L);
635+
assertThat(zSetOps.intersectSize(key1, Collections.singletonList(key2))).isEqualTo(2L);
636+
637+
// Test with 3 sets
638+
assertThat(zSetOps.intersectSize(key1, List.of(key2, key3))).isEqualTo(1L);
639+
640+
// Test with limit
641+
assertThat(zSetOps.intersectSize(key1, Collections.singletonList(key2), 1L)).isEqualTo(1L);
642+
}
643+
644+
@Test // GH-3253
645+
@EnabledOnCommand("ZINTERCARD")
646+
void testZsetIntersectSizeEmptySet() {
647+
648+
K key1 = keyFactory.instance();
649+
K key2 = keyFactory.instance();
650+
651+
V value1 = valueFactory.instance();
652+
V value2 = valueFactory.instance();
653+
654+
zSetOps.add(key1, value1, 1.0);
655+
zSetOps.add(key2, value2, 2.0);
656+
657+
// No intersection
658+
assertThat(zSetOps.intersectSize(key1, key2)).isEqualTo(0L);
659+
}
660+
611661
@Test // GH-2042
612662
@EnabledOnCommand("ZUNION")
613663
void testZsetUnion() {

0 commit comments

Comments
 (0)