From bd20c6d6f7e3b37591680eeafc3b8d100e1ad0e8 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 20 Aug 2024 15:16:53 +0200 Subject: [PATCH] Polishing. Add getFirst/getLast to Reactive and Bound Operations. Simplify getLast implementation. Reorder methods, tweak Javadoc, add since tags. Original pull request: #2966 See #2937 --- .../data/redis/core/BoundListOperations.java | 18 ++++++ .../data/redis/core/ListOperations.java | 60 +++++++++---------- .../redis/core/ReactiveListOperations.java | 25 ++++++++ .../support/collections/DefaultRedisList.java | 4 +- ...OperationsIntegrationIntegrationTests.java | 58 +++++++++--------- ...eactiveListOperationsIntegrationTests.java | 56 ++++++++++------- 6 files changed, 136 insertions(+), 85 deletions(-) diff --git a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java index 9c429deba..a33fab90d 100644 --- a/src/main/java/org/springframework/data/redis/core/BoundListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/BoundListOperations.java @@ -212,6 +212,24 @@ public interface BoundListOperations extends BoundKeyOperations { @Nullable Long remove(long count, Object value); + /** + * Returns the first element from the list at the bound {@code key}. + * + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + V getFirst(); + + /** + * Returns the last element from the list at the bound {@code key}. + * + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + V getLast(); + /** * Get element at {@code index} form list at the bound key. * diff --git a/src/main/java/org/springframework/data/redis/core/ListOperations.java b/src/main/java/org/springframework/data/redis/core/ListOperations.java index 853f8863c..5a946c62d 100644 --- a/src/main/java/org/springframework/data/redis/core/ListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ListOperations.java @@ -368,6 +368,30 @@ public interface ListOperations { @Nullable Long remove(K key, long count, Object value); + /** + * Returns the first element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + default V getFirst(K key) { + return index(key, 0); + } + + /** + * Returns the last element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return {@literal null} when used in pipeline / transaction. + * @since 3.4 + */ + @Nullable + default V getLast(K key) { + return index(key, -1); + } + /** * Get element at {@code index} form list at {@code key}. * @@ -526,7 +550,8 @@ public interface ListOperations { V rightPopAndLeftPush(K sourceKey, K destinationKey); /** - * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its value.
+ * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its + * value.
* Blocks connection until element available or {@code timeout} reached. * * @param sourceKey must not be {@literal null}. @@ -540,7 +565,8 @@ public interface ListOperations { V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit); /** - * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its value.
+ * Remove the last element from list at {@code sourceKey}, append it to {@code destinationKey} and return its + * value.
* Blocks connection until element available or {@code timeout} reached. * * @param sourceKey must not be {@literal null}. @@ -560,35 +586,5 @@ public interface ListOperations { return rightPopAndLeftPush(sourceKey, destinationKey, TimeoutUtils.toSeconds(timeout), TimeUnit.SECONDS); } - /** - * Returns the first element from list at {@code key}. - * - * @implSpec - * The implementation in this interface returns the result of calling {@code index(key, 0)}. - * - * @param key must not be {@literal null}. - * @return {@literal null} when used in pipeline / transaction. - */ - @Nullable - default V getFirst(K key) { - return index(key, 0); - } - - /** - * Returns the last element from list at {@code key}. - * - * @implSpec - * If the result of calling {@code size(key)} is not null, The implementation in this interface returns the - * result of calling {@code index(key, size - 1)}. Otherwise, it returns null. - * - * @param key must not be {@literal null}. - * @return {@literal null} when used in pipeline / transaction. - */ - @Nullable - default V getLast(K key) { - Long size = size(key); - return size != null ? index(key, size - 1) : null; - } - RedisOperations getOperations(); } diff --git a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java index 6a701c2ea..73ff7efa8 100644 --- a/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java +++ b/src/main/java/org/springframework/data/redis/core/ReactiveListOperations.java @@ -26,6 +26,7 @@ import java.util.Collection; import org.springframework.data.redis.core.ListOperations.MoveFrom; import org.springframework.data.redis.core.ListOperations.MoveTo; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -276,6 +277,30 @@ public interface ReactiveListOperations { */ Mono remove(K key, long count, Object value); + /** + * Returns the first element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return + * @since 3.4 + */ + @Nullable + default Mono getFirst(K key) { + return index(key, 0); + } + + /** + * Returns the last element from the list at {@code key}. + * + * @param key must not be {@literal null}. + * @return + * @since 3.4 + */ + @Nullable + default Mono getLast(K key) { + return index(key, -1); + } + /** * Get element at {@code index} form list at {@code key}. * diff --git a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java index fd2da300c..8c9aa5f8d 100644 --- a/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java +++ b/src/main/java/org/springframework/data/redis/support/collections/DefaultRedisList.java @@ -349,7 +349,7 @@ public class DefaultRedisList extends AbstractRedisCollection implements R @Override @Nullable public E peek() { - return listOps.index(0); + return listOps.getFirst(); } @Override @@ -426,7 +426,7 @@ public class DefaultRedisList extends AbstractRedisCollection implements R @Override @Nullable public E peekLast() { - return listOps.index(-1); + return listOps.getLast(); } @Override diff --git a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java index 1280bfd80..7ee0b5d40 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultListOperationsIntegrationIntegrationTests.java @@ -56,8 +56,7 @@ public class DefaultListOperationsIntegrationIntegrationTests { private final ListOperations listOps; public DefaultListOperationsIntegrationIntegrationTests(RedisTemplate redisTemplate, - ObjectFactory keyFactory, - ObjectFactory valueFactory) { + ObjectFactory keyFactory, ObjectFactory valueFactory) { this.redisTemplate = redisTemplate; this.keyFactory = keyFactory; @@ -347,6 +346,34 @@ public class DefaultListOperationsIntegrationIntegrationTests { assertThat(listOps.range(target, 0, -1)).containsExactly(v4, v1); } + @ParameterizedRedisTest // GH-2937 + void getFirst() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getFirst(key)).isEqualTo(v1); + } + + @ParameterizedRedisTest // GH-2937 + void getLast() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOps.rightPush(key, v1); + listOps.rightPush(key, v2); + listOps.rightPush(key, v3); + assertThat(listOps.getLast(key)).isEqualTo(v3); + } + @ParameterizedRedisTest // DATAREDIS-1196 @EnabledOnCommand("LPOS") void indexOf() { @@ -378,31 +405,4 @@ public class DefaultListOperationsIntegrationIntegrationTests { assertThat(listOps.lastIndexOf(key, v1)).isEqualTo(2); } - @ParameterizedRedisTest // GH-2937 - void getFirst() { - - K key = keyFactory.instance(); - V v1 = valueFactory.instance(); - V v2 = valueFactory.instance(); - V v3 = valueFactory.instance(); - - listOps.rightPush(key, v1); - listOps.rightPush(key, v2); - listOps.rightPush(key, v3); - assertThat(listOps.getFirst(key)).isEqualTo(v1); - } - - @ParameterizedRedisTest // GH-2937 - void getLast() { - - K key = keyFactory.instance(); - V v1 = valueFactory.instance(); - V v2 = valueFactory.instance(); - V v3 = valueFactory.instance(); - - listOps.rightPush(key, v1); - listOps.rightPush(key, v2); - listOps.rightPush(key, v3); - assertThat(listOps.getLast(key)).isEqualTo(v3); - } } diff --git a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java index 92fcd6d78..34eaf7cb5 100644 --- a/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java +++ b/src/test/java/org/springframework/data/redis/core/DefaultReactiveListOperationsIntegrationTests.java @@ -15,8 +15,10 @@ */ package org.springframework.data.redis.core; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.assertj.core.api.Assumptions.assumeThat; +import static org.assertj.core.api.Assertions.*; +import static org.assertj.core.api.Assumptions.*; + +import reactor.test.StepVerifier; import java.time.Duration; import java.util.Collection; @@ -33,8 +35,6 @@ import org.springframework.data.redis.test.condition.EnabledOnCommand; import org.springframework.data.redis.test.extension.parametrized.MethodSource; import org.springframework.data.redis.test.extension.parametrized.ParameterizedRedisTest; -import reactor.test.StepVerifier; - /** * Integration tests for {@link DefaultReactiveListOperations}. * @@ -417,6 +417,32 @@ public class DefaultReactiveListOperationsIntegrationTests { listOperations.index(key, 1).as(StepVerifier::create).expectNext(value2).verifyComplete(); } + @ParameterizedRedisTest // GH-2937 + void getFirst() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOperations.rightPushAll(key, v1, v2, v3).as(StepVerifier::create).expectNext(3L).verifyComplete(); + + listOperations.getFirst(key).as(StepVerifier::create).expectNext(v1).verifyComplete(); + } + + @ParameterizedRedisTest // GH-2937 + void getLast() { + + K key = keyFactory.instance(); + V v1 = valueFactory.instance(); + V v2 = valueFactory.instance(); + V v3 = valueFactory.instance(); + + listOperations.rightPushAll(key, v1, v2, v3).as(StepVerifier::create).expectNext(3L).verifyComplete(); + + listOperations.getLast(key).as(StepVerifier::create).expectNext(v3).verifyComplete(); + } + @ParameterizedRedisTest // DATAREDIS-1196 @EnabledOnCommand("LPOS") void indexOf() { @@ -469,16 +495,9 @@ public class DefaultReactiveListOperationsIntegrationTests { V value2 = valueFactory.instance(); V value3 = valueFactory.instance(); - listOperations.leftPushAll(key, value1, value2, value3) - .as(StepVerifier::create) - .expectNext(3L) - .verifyComplete(); + listOperations.leftPushAll(key, value1, value2, value3).as(StepVerifier::create).expectNext(3L).verifyComplete(); - listOperations.leftPop(key, 2) - .as(StepVerifier::create) - .expectNext(value3) - .expectNext(value2) - .verifyComplete(); + listOperations.leftPop(key, 2).as(StepVerifier::create).expectNext(value3).expectNext(value2).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-602 @@ -505,16 +524,9 @@ public class DefaultReactiveListOperationsIntegrationTests { V value2 = valueFactory.instance(); V value3 = valueFactory.instance(); - listOperations.rightPushAll(key, value3, value2, value1) - .as(StepVerifier::create) - .expectNext(3L) - .verifyComplete(); + listOperations.rightPushAll(key, value3, value2, value1).as(StepVerifier::create).expectNext(3L).verifyComplete(); - listOperations.rightPop(key, 2) - .as(StepVerifier::create) - .expectNext(value1) - .expectNext(value2) - .verifyComplete(); + listOperations.rightPop(key, 2).as(StepVerifier::create).expectNext(value1).expectNext(value2).verifyComplete(); } @ParameterizedRedisTest // DATAREDIS-602