From 6f148f6443aff2c0cf2479c55014beb977adbe03 Mon Sep 17 00:00:00 2001 From: Nikita Date: Thu, 24 May 2018 14:57:38 +0300 Subject: [PATCH] RScoredSortedSet.pollFirst and pollLast with count parameter added --- .../org/redisson/RedissonScoredSortedSet.java | 44 ++++++++++--- .../org/redisson/api/RScoredSortedSet.java | 52 ++++++++++++++- .../redisson/api/RScoredSortedSetAsync.java | 66 ++++++++++++++++++- .../client/protocol/RedisCommands.java | 9 ++- .../ObjectFirstResultReplayDecoder.java | 41 ------------ .../ObjectFirstScoreReplayDecoder.java | 3 + .../redisson/RedissonScoredSortedSetTest.java | 32 +++++++++ 7 files changed, 190 insertions(+), 57 deletions(-) delete mode 100644 redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java diff --git a/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java b/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java index 36a03e64d..2decb2198 100644 --- a/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java +++ b/redisson/src/main/java/org/redisson/RedissonScoredSortedSet.java @@ -36,6 +36,7 @@ import org.redisson.client.codec.Codec; import org.redisson.client.codec.DoubleCodec; import org.redisson.client.codec.LongCodec; import org.redisson.client.codec.ScanCodec; +import org.redisson.client.protocol.RedisCommand; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.ScoredEntry; import org.redisson.client.protocol.decoder.ListScanResult; @@ -89,25 +90,52 @@ public class RedissonScoredSortedSet extends RedissonExpirable implements RSc return get(pollLastAsync()); } + @Override + public Collection pollFirst(int count) { + return get(pollFirstAsync(count)); + } + + @Override + public Collection pollLast(int count) { + return get(pollLastAsync(count)); + } + + @Override + public RFuture> pollFirstAsync(int count) { + if (count <= 0) { + return RedissonPromise.>newSucceededFuture(Collections.emptyList()); + } + + return poll(0, count-1, RedisCommands.EVAL_LIST); + } + + @Override + public RFuture> pollLastAsync(int count) { + if (count <= 0) { + return RedissonPromise.>newSucceededFuture(Collections.emptyList()); + } + return poll(-count, -1, RedisCommands.EVAL_LIST); + } + @Override public RFuture pollFirstAsync() { - return poll(0); + return poll(0, 0, RedisCommands.EVAL_FIRST_LIST); } @Override public RFuture pollLastAsync() { - return poll(-1); + return poll(-1, -1, RedisCommands.EVAL_FIRST_LIST); } - private RFuture poll(int index) { - return commandExecutor.evalWriteAsync(getName(), codec, RedisCommands.EVAL_OBJECT, + private RFuture poll(int from, int to, RedisCommand command) { + return commandExecutor.evalWriteAsync(getName(), codec, command, "local v = redis.call('zrange', KEYS[1], ARGV[1], ARGV[2]); " - + "if v[1] ~= nil then " + + "if #v > 0 then " + "redis.call('zremrangebyrank', KEYS[1], ARGV[1], ARGV[2]); " - + "return v[1]; " + + "return v; " + "end " - + "return nil;", - Collections.singletonList(getName()), index, index); + + "return v;", + Collections.singletonList(getName()), from, to); } @Override diff --git a/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java b/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java index 518178702..7b107bd84 100644 --- a/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java +++ b/redisson/src/main/java/org/redisson/api/RScoredSortedSet.java @@ -45,16 +45,64 @@ public interface RScoredSortedSet extends RScoredSortedSetAsync, Iterable< */ RCollectionMapReduce mapReduce(); + /** + * Removes and returns the head elements or {@code null} if this sorted set is empty. + * + * @param count - elements amount + * @return the head element, + * or {@code null} if this sorted set is empty + */ + Collection pollFirst(int count); + + /** + * Removes and returns the tail elements or {@code null} if this sorted set is empty. + * + * @param count - elements amount + * @return the tail element or {@code null} if this sorted set is empty + */ + Collection pollLast(int count); + + /** + * Removes and returns the head element or {@code null} if this sorted set is empty. + * + * @return the head element, + * or {@code null} if this sorted set is empty + */ V pollFirst(); + /** + * Removes and returns the tail element or {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ V pollLast(); + /** + * Returns the head element or {@code null} if this sorted set is empty. + * + * @return the head element or {@code null} if this sorted set is empty + */ V first(); + /** + * Returns the tail element or {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ V last(); - + + /** + * Returns score of the tail element or returns {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ Double firstScore(); - + + /** + * Returns score of the head element or returns {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ Double lastScore(); Long addAll(Map objects); diff --git a/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java b/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java index c2abadf63..87d9e67ec 100644 --- a/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java +++ b/redisson/src/main/java/org/redisson/api/RScoredSortedSetAsync.java @@ -30,16 +30,64 @@ import org.redisson.client.protocol.ScoredEntry; */ public interface RScoredSortedSetAsync extends RExpirableAsync, RSortableAsync> { - RFuture pollLastAsync(); + /** + * Removes and returns the head elements or {@code null} if this sorted set is empty. + * + * @param count - elements amount + * @return the head element, + * or {@code null} if this sorted set is empty + */ + RFuture> pollFirstAsync(int count); + + /** + * Removes and returns the tail elements or {@code null} if this sorted set is empty. + * + * @param count - elements amount + * @return the tail element or {@code null} if this sorted set is empty + */ + RFuture> pollLastAsync(int count); + /** + * Removes and returns the head element or {@code null} if this sorted set is empty. + * + * @return the head element, + * or {@code null} if this sorted set is empty + */ RFuture pollFirstAsync(); + /** + * Removes and returns the tail element or {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ + RFuture pollLastAsync(); + + /** + * Returns the head element or {@code null} if this sorted set is empty. + * + * @return the head element or {@code null} if this sorted set is empty + */ RFuture firstAsync(); + /** + * Returns the tail element or {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ RFuture lastAsync(); - + + /** + * Returns score of the head element or returns {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ RFuture firstScoreAsync(); - + + /** + * Returns score of the tail element or returns {@code null} if this sorted set is empty. + * + * @return the tail element or {@code null} if this sorted set is empty + */ RFuture lastScoreAsync(); RFuture addAllAsync(Map objects); @@ -48,8 +96,20 @@ public interface RScoredSortedSetAsync extends RExpirableAsync, RSortableAsyn RFuture removeRangeByRankAsync(int startIndex, int endIndex); + /** + * Returns rank of value, with the scores ordered from low to high. + * + * @param o - object + * @return rank or null if value does not exist + */ RFuture rankAsync(V o); + /** + * Returns rank of value, with the scores ordered from high to low. + * + * @param o - object + * @return rank or null if value does not exist + */ RFuture revRankAsync(V o); /** diff --git a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java index 7b225ddaa..ea2efa058 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java +++ b/redisson/src/main/java/org/redisson/client/protocol/RedisCommands.java @@ -36,13 +36,14 @@ import org.redisson.client.protocol.convertor.DoubleNullSafeReplayConvertor; import org.redisson.client.protocol.convertor.DoubleReplayConvertor; import org.redisson.client.protocol.convertor.IntegerReplayConvertor; import org.redisson.client.protocol.convertor.KeyValueConvertor; -import org.redisson.client.protocol.convertor.TimeObjectDecoder; import org.redisson.client.protocol.convertor.LongReplayConvertor; +import org.redisson.client.protocol.convertor.TimeObjectDecoder; import org.redisson.client.protocol.convertor.TrueReplayConvertor; import org.redisson.client.protocol.convertor.TypeConvertor; import org.redisson.client.protocol.convertor.VoidReplayConvertor; import org.redisson.client.protocol.decoder.ClusterNodesDecoder; import org.redisson.client.protocol.decoder.KeyValueObjectDecoder; +import org.redisson.client.protocol.decoder.ListFirstObjectDecoder; import org.redisson.client.protocol.decoder.ListMultiDecoder; import org.redisson.client.protocol.decoder.ListResultReplayDecoder; import org.redisson.client.protocol.decoder.ListScanResult; @@ -51,7 +52,6 @@ import org.redisson.client.protocol.decoder.Long2MultiDecoder; import org.redisson.client.protocol.decoder.LongMultiDecoder; import org.redisson.client.protocol.decoder.MapScanResult; import org.redisson.client.protocol.decoder.MapScanResultReplayDecoder; -import org.redisson.client.protocol.decoder.ObjectFirstResultReplayDecoder; import org.redisson.client.protocol.decoder.ObjectFirstScoreReplayDecoder; import org.redisson.client.protocol.decoder.ObjectListReplayDecoder; import org.redisson.client.protocol.decoder.ObjectMapEntryReplayDecoder; @@ -119,9 +119,11 @@ public interface RedisCommands { RedisStrictCommand ZSCORE = new RedisStrictCommand("ZSCORE", new DoubleReplayConvertor()); RedisCommand ZRANK_INT = new RedisCommand("ZRANK", new IntegerReplayConvertor()); RedisCommand ZREVRANK_INT = new RedisCommand("ZREVRANK", new IntegerReplayConvertor()); - RedisCommand ZRANGE_SINGLE = new RedisCommand("ZRANGE", new ObjectFirstResultReplayDecoder()); + RedisCommand ZRANGE_SINGLE = new RedisCommand("ZRANGE", new ListFirstObjectDecoder()); RedisStrictCommand ZRANGE_SINGLE_SCORE = new RedisStrictCommand("ZRANGE", new ObjectFirstScoreReplayDecoder()); RedisCommand> ZRANGE = new RedisCommand>("ZRANGE", new ObjectListReplayDecoder()); + RedisCommand> ZPOPMIN = new RedisCommand>("ZPOPMIN", new ObjectListReplayDecoder()); + RedisCommand> ZPOPMAX = new RedisCommand>("ZPOPMAX", new ObjectListReplayDecoder()); RedisStrictCommand ZREMRANGEBYRANK = new RedisStrictCommand("ZREMRANGEBYRANK", new IntegerReplayConvertor()); RedisStrictCommand ZREMRANGEBYSCORE = new RedisStrictCommand("ZREMRANGEBYSCORE", new IntegerReplayConvertor()); RedisStrictCommand ZREMRANGEBYLEX = new RedisStrictCommand("ZREMRANGEBYLEX", new IntegerReplayConvertor()); @@ -228,6 +230,7 @@ public interface RedisCommands { RedisStrictCommand EVAL_LONG = new RedisStrictCommand("EVAL"); RedisStrictCommand EVAL_LONG_SAFE = new RedisStrictCommand("EVAL", new LongReplayConvertor()); RedisStrictCommand EVAL_VOID = new RedisStrictCommand("EVAL", new VoidReplayConvertor()); + RedisCommand EVAL_FIRST_LIST = new RedisCommand("EVAL", new ListFirstObjectDecoder()); RedisCommand> EVAL_LIST = new RedisCommand>("EVAL", new ObjectListReplayDecoder()); RedisCommand> EVAL_SET = new RedisCommand>("EVAL", new ObjectSetReplayDecoder()); RedisCommand EVAL_OBJECT = new RedisCommand("EVAL"); diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java deleted file mode 100644 index 1466652a4..000000000 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstResultReplayDecoder.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright 2018 Nikita Koksharov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.redisson.client.protocol.decoder; - -import java.util.List; - -import org.redisson.client.handler.State; -import org.redisson.client.protocol.Decoder; - -/** - * - * @author Nikita Koksharov - * - * @param type - */ -public class ObjectFirstResultReplayDecoder implements MultiDecoder { - - @Override - public T decode(List parts, State state) { - return (T) parts.get(0); - } - - @Override - public Decoder getDecoder(int paramNum, State state) { - return null; - } - -} diff --git a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java index a6cc0ff0a..3e886c541 100644 --- a/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java +++ b/redisson/src/main/java/org/redisson/client/protocol/decoder/ObjectFirstScoreReplayDecoder.java @@ -39,6 +39,9 @@ public class ObjectFirstScoreReplayDecoder implements MultiDecoder { @Override public Double decode(List parts, State state) { + if (parts.isEmpty()) { + return null; + } return (Double) parts.get(1); } diff --git a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java index fbc23b52d..21405cfdb 100644 --- a/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java +++ b/redisson/src/test/java/org/redisson/RedissonScoredSortedSetTest.java @@ -249,7 +249,33 @@ public class RedissonScoredSortedSetTest extends BaseTest { Assert.assertEquals("c", set.pollLast()); assertThat(set).containsExactly("a", "b"); } + + @Test + public void testPollLastAmount() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + assertThat(set.pollLast(2)).isEmpty(); + + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + + assertThat(set.pollLast(2)).containsExactly("b", "c"); + assertThat(set).containsExactly("a"); + } + + @Test + public void testPollFistAmount() { + RScoredSortedSet set = redisson.getScoredSortedSet("simple"); + assertThat(set.pollFirst(2)).isEmpty(); + + set.add(0.1, "a"); + set.add(0.2, "b"); + set.add(0.3, "c"); + assertThat(set.pollFirst(2)).containsExactly("a", "b"); + assertThat(set).containsExactly("c"); + } + @Test public void testPollFirst() { RScoredSortedSet set = redisson.getScoredSortedSet("simple"); @@ -271,6 +297,9 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.3, "c"); set.add(0.4, "d"); + RScoredSortedSet set2 = redisson.getScoredSortedSet("simple2"); + assertThat(set2.first()).isNull(); + assertThat(set2.last()).isNull(); Assert.assertEquals("a", set.first()); Assert.assertEquals("d", set.last()); } @@ -283,6 +312,9 @@ public class RedissonScoredSortedSetTest extends BaseTest { set.add(0.3, "c"); set.add(0.4, "d"); + RScoredSortedSet set2 = redisson.getScoredSortedSet("simple2"); + assertThat(set2.firstScore()).isNull(); + assertThat(set2.lastScore()).isNull(); assertThat(set.firstScore()).isEqualTo(0.1); assertThat(set.lastScore()).isEqualTo(0.4); }