From d5555126b3079b8f90dfc32487e604356d7dc90b Mon Sep 17 00:00:00 2001 From: annemayor Date: Sun, 26 May 2024 23:19:05 +0900 Subject: [PATCH] =?UTF-8?q?Add=20`exists(=E2=80=A6)`=20accepting=20multipl?= =?UTF-8?q?e=20keys=20to=20Reactive=20API.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #2883 Original pull request: #2918 Signed-off-by: Anne Lee --- .../redis/connection/ReactiveKeyCommands.java | 8 ++++++ .../lettuce/LettuceReactiveKeyCommands.java | 8 ++++++ .../redis/core/ReactiveRedisOperations.java | 10 ++++++++ .../redis/core/ReactiveRedisTemplate.java | 20 +++++++++++++++ ...ceReactiveKeyCommandsIntegrationTests.java | 22 +++++++++++++--- ...ReactiveRedisTemplateIntegrationTests.java | 25 ++++++++++++++++--- 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java index 62be2393e..249b23d41 100644 --- a/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/ReactiveKeyCommands.java @@ -177,6 +177,14 @@ public interface ReactiveKeyCommands { return exists(Mono.just(new KeyCommand(key))).next().map(BooleanResponse::getOutput); } + /** + * Determine the number of given {@literal keys} that exist. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return {@link Mono} emitting {@literal the number of existing keys}. + * @see Redis Documentation: EXISTS + */ + Mono exists(List keys); /** * Determine if given {@literal key} exists. diff --git a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java index 3dbb44c69..5dde44bc6 100644 --- a/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java +++ b/src/main/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommands.java @@ -344,6 +344,14 @@ class LettuceReactiveKeyCommands implements ReactiveKeyCommands { return connection.execute(cmd -> cmd.objectRefcount(key)).next(); } + @Override + public Mono exists(List keys) { + Assert.notNull(keys, "Key list must not be null"); + Assert.notEmpty(keys, "Key list must not be empty"); + + return connection.execute(cmd -> cmd.exists(keys.toArray(ByteBuffer[]::new))).next(); + } + private static ExpireArgs getExpireArgs(ExpirationOptions options) { return new ExpireArgs() { diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java index 996ddbc58..81c910ca5 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisOperations.java @@ -22,6 +22,7 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; @@ -421,6 +422,15 @@ public interface ReactiveRedisOperations { */ Mono getExpire(K key); + /** + * Get the number of given {@code keys} that exists. + * + * @param keys must not be {@literal null} or {@literal empty}. + * @return the number of existing keys in redis. 0 if there are no existing keys. + * @see Redis Documentation: EXISTS + */ + Mono countExistingKeys(Collection keys); + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java index 0649072b4..b6238bc3e 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveRedisTemplate.java @@ -23,6 +23,7 @@ import java.nio.ByteBuffer; import java.time.Duration; import java.time.Instant; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.stream.Collectors; @@ -505,6 +506,14 @@ public class ReactiveRedisTemplate implements ReactiveRedisOperations connection.keyCommands().move(rawKey(key), dbIndex)); } + @Override + public Mono countExistingKeys(Collection keys) { + Assert.notNull(keys, "Keys must not be null"); + + ByteBuffer[] rawKeys = rawKeys(keys); + return doCreateMono(connection -> connection.keyCommands().exists(Arrays.asList(rawKeys))); + } + // ------------------------------------------------------------------------- // Methods dealing with Redis Lua scripts // ------------------------------------------------------------------------- @@ -688,6 +697,17 @@ public class ReactiveRedisTemplate implements ReactiveRedisOperations keys) { + final ByteBuffer[] rawKeys = new ByteBuffer[keys.size()]; + + int i = 0; + for (K key : keys) { + rawKeys[i++] = rawKey(key); + } + + return rawKeys; + } + @Nullable private K readKey(ByteBuffer buffer) { return getSerializationContext().getKeySerializationPair().getReader().read(buffer); diff --git a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java index bc16ae8a4..d2bbc633d 100644 --- a/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/lettuce/LettuceReactiveKeyCommandsIntegrationTests.java @@ -70,6 +70,22 @@ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveC connection.keyCommands().exists(KEY_1_BBUFFER).as(StepVerifier::create).expectNext(false).verifyComplete(); } + @ParameterizedRedisTest + void existsKeyReturnsKeyCount() { + nativeCommands.set(KEY_1, "1000"); + nativeCommands.set(KEY_2, "2000"); + nativeCommands.set(KEY_3, "3000"); + + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void existsKeyReturnsZeroWhenKeyDoesNotExist() { + connection.keyCommands().exists(List.of(KEY_1_BBUFFER, KEY_2_BBUFFER, KEY_3_BBUFFER)).as(StepVerifier::create) + .expectNext(0L).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-525 void typeShouldReturnTypeCorrectly() { @@ -168,7 +184,7 @@ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveC connection.keyCommands().rename(KEY_1_BBUFFER, KEY_2_BBUFFER).as(StepVerifier::create).expectNext(true) .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -187,7 +203,7 @@ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveC .verifyComplete(); assertThat(nativeCommands.exists(KEY_2)).isEqualTo(1L); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-525 @@ -428,7 +444,7 @@ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveC .expectNext(true) // .expectComplete() // .verify(); - assertThat(nativeCommands.exists(KEY_1)).isEqualTo(0L); + assertThat(nativeCommands.exists(KEY_1)).isZero(); } @ParameterizedRedisTest // DATAREDIS-694 diff --git a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java index f3c55deb5..40830cd46 100644 --- a/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/ReactiveRedisTemplateIntegrationTests.java @@ -24,10 +24,7 @@ import reactor.test.StepVerifier; import java.time.Duration; import java.time.Instant; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.function.Function; import org.junit.jupiter.api.BeforeEach; @@ -582,4 +579,24 @@ public class ReactiveRedisTemplateIntegrationTests { .thenCancel() // .verify(Duration.ofSeconds(3)); } + + @ParameterizedRedisTest + void countExistingKeysIfValidKeyExists() { + + K key = keyFactory.instance(); + K key2 = keyFactory.instance(); + K key3 = keyFactory.instance(); + + redisTemplate.opsForValue().set(key, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key2, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + redisTemplate.opsForValue().set(key3, valueFactory.instance()).as(StepVerifier::create).expectNext(true).verifyComplete(); + + redisTemplate.countExistingKeys(Arrays.asList(key, key2, key3)).as(StepVerifier::create).expectNext(3L).verifyComplete(); + } + + @ParameterizedRedisTest + void countExistingKeysIfNotValidKeyExists() { + K key = keyFactory.instance(); + redisTemplate.countExistingKeys(List.of(key)).as(StepVerifier::create).expectNext(0L).verifyComplete(); + } }