diff --git a/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java b/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java index 85f2abe8f..be8ffc198 100644 --- a/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java +++ b/src/main/java/org/springframework/data/redis/core/KeyScanOptions.java @@ -17,6 +17,7 @@ package org.springframework.data.redis.core; import java.util.StringJoiner; +import org.springframework.data.redis.connection.DataType; import org.springframework.lang.Nullable; import org.springframework.util.StringUtils; @@ -24,6 +25,7 @@ import org.springframework.util.StringUtils; * Options to be used for with {@literal SCAN} commands. * * @author Mark Paluch + * @author Christoph Strobl * @since 2.6 */ public class KeyScanOptions extends ScanOptions { @@ -35,7 +37,7 @@ public class KeyScanOptions extends ScanOptions { private final @Nullable String type; - private KeyScanOptions(@Nullable Long count, @Nullable String pattern, @Nullable byte[] bytePattern, + KeyScanOptions(@Nullable Long count, @Nullable String pattern, @Nullable byte[] bytePattern, @Nullable String type) { super(count, pattern, bytePattern); @@ -43,12 +45,13 @@ public class KeyScanOptions extends ScanOptions { } /** - * Static factory method that returns a new {@link KeyScanOptionsBuilder}. + * Static factory method that returns a new {@link ScanOptionsBuilder}. * + * @param type * @return */ - public static KeyScanOptionsBuilder scanOptions() { - return new KeyScanOptionsBuilder(); + public static ScanOptionsBuilder scanOptions(DataType type) { + return new ScanOptionsBuilder().type(type); } @Nullable @@ -71,68 +74,4 @@ public class KeyScanOptions extends ScanOptions { return joiner.toString(); } - - public static class KeyScanOptionsBuilder extends ScanOptionsBuilder { - - private @Nullable String type; - - private KeyScanOptionsBuilder() {} - - /** - * Returns the current {@link KeyScanOptionsBuilder} configured with the given {@code count}. - * - * @param count - * @return - */ - @Override - public KeyScanOptionsBuilder count(long count) { - super.count(count); - return this; - } - - /** - * Returns the current {@link KeyScanOptionsBuilder} configured with the given {@code pattern}. - * - * @param pattern - * @return - */ - @Override - public KeyScanOptionsBuilder match(String pattern) { - super.match(pattern); - return this; - } - - /** - * Returns the current {@link KeyScanOptionsBuilder} configured with the given {@code pattern}. - * - * @param pattern - * @return - */ - @Override - public KeyScanOptionsBuilder match(byte[] pattern) { - super.match(pattern); - return this; - } - - /** - * Returns the current {@link KeyScanOptionsBuilder} configured with the given {@code type}. - * - * @param type - * @return - */ - public KeyScanOptionsBuilder type(String type) { - this.type = type; - return this; - } - - /** - * Builds a new {@link KeyScanOptions} objects. - * - * @return a new {@link KeyScanOptions} objects. - */ - @Override - public KeyScanOptions build() { - return new KeyScanOptions(count, pattern, bytePattern, type); - } - } } diff --git a/src/main/java/org/springframework/data/redis/core/ScanOptions.java b/src/main/java/org/springframework/data/redis/core/ScanOptions.java index e36849879..4cba64c4b 100644 --- a/src/main/java/org/springframework/data/redis/core/ScanOptions.java +++ b/src/main/java/org/springframework/data/redis/core/ScanOptions.java @@ -17,7 +17,9 @@ package org.springframework.data.redis.core; import java.util.StringJoiner; +import org.springframework.data.redis.connection.DataType; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -41,6 +43,7 @@ public class ScanOptions { private final @Nullable byte[] bytePattern; ScanOptions(@Nullable Long count, @Nullable String pattern, @Nullable byte[] bytePattern) { + this.count = count; this.pattern = pattern; this.bytePattern = bytePattern; @@ -110,6 +113,7 @@ public class ScanOptions { @Nullable Long count; @Nullable String pattern; @Nullable byte[] bytePattern; + @Nullable DataType type; ScanOptionsBuilder() {} @@ -117,7 +121,7 @@ public class ScanOptions { * Returns the current {@link ScanOptionsBuilder} configured with the given {@code count}. * * @param count - * @return + * @return this. */ public ScanOptionsBuilder count(long count) { this.count = count; @@ -128,7 +132,7 @@ public class ScanOptions { * Returns the current {@link ScanOptionsBuilder} configured with the given {@code pattern}. * * @param pattern - * @return + * @return this. */ public ScanOptionsBuilder match(String pattern) { this.pattern = pattern; @@ -139,7 +143,7 @@ public class ScanOptions { * Returns the current {@link ScanOptionsBuilder} configured with the given {@code pattern}. * * @param pattern - * @return + * @return this. * @since 2.6 */ public ScanOptionsBuilder match(byte[] pattern) { @@ -147,12 +151,46 @@ public class ScanOptions { return this; } + /** + * Returns the current {@link ScanOptionsBuilder} configured with the given {@code type}.
+ * Please verify the the targeted command supports the + * TYPE option before use. + * + * @param type must not be {@literal null}. Either do not set or use {@link DataType#NONE}. + * @return this. + * @since 2.6 + */ + public ScanOptionsBuilder type(DataType type) { + + Assert.notNull(type, "Type must not be null! Use NONE instead."); + + this.type = type; + return this; + } + + /** + * Returns the current {@link ScanOptionsBuilder} configured with the given {@code type}. + * + * @param type the textual representation of {@link DataType#fromCode(String)}. Must not be {@literal null}. + * @return this. + * @throws IllegalArgumentException if given type is {@literal null} or unknown. + */ + public ScanOptionsBuilder type(String type) { + + Assert.notNull(type, "Type must not be null!"); + return type(DataType.fromCode(type)); + } + /** * Builds a new {@link ScanOptions} objects. * * @return a new {@link ScanOptions} objects. */ public ScanOptions build() { + + if (type != null && !DataType.NONE.equals(type)) { + return new KeyScanOptions(count, pattern, bytePattern, type.code()); + } return new ScanOptions(count, pattern, bytePattern); } } 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 3a8767db0..debe99ae1 100644 --- a/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/connection/AbstractConnectionIntegrationTests.java @@ -2598,7 +2598,7 @@ public abstract class AbstractConnectionIntegrationTests { @Test // GH-2089 @EnabledOnRedisDriver(RedisDriver.LETTUCE) - @EnabledOnRedisVersion("6.2") + @EnabledOnRedisVersion("6.0") void scanWithType() { assumeThat(connection.isPipelined() || connection.isQueueing()) 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 7dc460894..f3de761ae 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 @@ -35,8 +35,10 @@ import org.springframework.data.redis.connection.DataType; import org.springframework.data.redis.connection.ReactiveRedisConnection.KeyCommand; import org.springframework.data.redis.connection.ReactiveRedisConnection.NumericResponse; import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding; +import org.springframework.data.redis.core.KeyScanOptions; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.test.condition.EnabledOnCommand; +import org.springframework.data.redis.test.condition.EnabledOnRedisVersion; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; /** @@ -115,6 +117,30 @@ public class LettuceReactiveKeyCommandsIntegrationTests extends LettuceReactiveC .verifyComplete(); } + @ParameterizedRedisTest // GH-2089 + @EnabledOnRedisVersion("6.0") + void scanWithType() { + + nativeCommands.set(KEY_1, VALUE_1); + nativeCommands.lpush(KEY_2, VALUE_2); + nativeCommands.sadd(KEY_3, VALUE_3); + + connection.keyCommands().scan(KeyScanOptions.scanOptions(DataType.STRING).build()) // + .as(StepVerifier::create) // + .expectNext(KEY_1_BBUFFER) // + .verifyComplete(); + + connection.keyCommands().scan(KeyScanOptions.scanOptions(DataType.SET).build()) // + .as(StepVerifier::create) // + .expectNext(KEY_3_BBUFFER) // + .verifyComplete(); + + connection.keyCommands().scan(KeyScanOptions.scanOptions(DataType.NONE).build()) // + .as(StepVerifier::create) // + .expectNextCount(3) // + .verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-525 void randomKeyShouldReturnAnyKey() {