Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
* @author Andrey Shlykov
* @author Shyngys Sapraliyev
* @author John Blum
* @author Kim Sumin
*/
class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZSetOperations<K, V> {

Expand Down Expand Up @@ -638,6 +639,48 @@ public Cursor<TypedTuple<V>> scan(K key, ScanOptions options) {
return new ConvertingCursor<>(cursor, this::deserializeTuple);
}

@Override
public Set<TypedTuple<V>> rangeByScoreWithScores(K key, Range<Double> range, Limit limit) {

Assert.notNull(key, "Key must not be null!");
Assert.notNull(range, "Range must not be null!");

byte[] rawKey = rawKey(key);

return execute(connection -> {
Set<Tuple> result;

if (limit.isUnlimited()) {
result = connection.zRangeByScoreWithScores(rawKey, range);
} else {
result = connection.zRangeByScoreWithScores(rawKey, range, limit);
}

return deserializeTupleValues(result);
});
}

@Override
public Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, Range<Double> range, Limit limit) {

Assert.notNull(key, "Key must not be null!");
Assert.notNull(range, "Range must not be null!");

byte[] rawKey = rawKey(key);

return execute(connection -> {
Set<Tuple> result;

if (limit.isUnlimited()) {
result = connection.zRevRangeByScoreWithScores(rawKey, range);
} else {
result = connection.zRevRangeByScoreWithScores(rawKey, range, limit);
}

return deserializeTupleValues(result);
});
}

public Set<byte[]> rangeByScore(K key, String min, String max) {

byte[] rawKey = rawKey(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
* @author Wongoo (望哥)
* @author Andrey Shlykov
* @author Shyngys Sapraliyev
* @author Kim Sumin
*/
public interface ZSetOperations<K, V> {

Expand Down Expand Up @@ -1271,4 +1272,63 @@ default Long reverseRangeAndStoreByScore(K srcKey, K dstKey, Range<? extends Num
* @return never {@literal null}.
*/
RedisOperations<K, V> getOperations();


/**
* Get set of {@link TypedTuple}s where score is between the values defined by the
* {@link Range} from sorted set.
*
* @param key must not be {@literal null}.
* @param range must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
* @since 3.5 (or next version number)
*/
@Nullable
default Set<TypedTuple<V>> rangeByScoreWithScores(K key, Range<Double> range) {
return rangeByScoreWithScores(key, range, Limit.unlimited());
}

/**
* Get set of {@link TypedTuple}s where score is between the values defined by the
* {@link Range} and limited by the {@link Limit} from sorted set.
*
* @param key must not be {@literal null}.
* @param range must not be {@literal null}.
* @param limit can be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/zrangebyscore">Redis Documentation: ZRANGEBYSCORE</a>
* @since 3.5 (or next version number)
*/
@Nullable
Set<TypedTuple<V>> rangeByScoreWithScores(K key, Range<Double> range, Limit limit);

/**
* Get set of {@link TypedTuple}s where score is between the values defined by the
* {@link Range} from sorted set ordered from high to low.
*
* @param key must not be {@literal null}.
* @param range must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/zrevrangebyscore">Redis Documentation: ZREVRANGEBYSCORE</a>
* @since 3.5 (or next version number)
*/
@Nullable
default Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, Range<Double> range) {
return reverseRangeByScoreWithScores(key, range, Limit.unlimited());
}

/**
* Get set of {@link TypedTuple}s where score is between the values defined by the
* {@link Range} and limited by the {@link Limit} from sorted set ordered from high to low.
*
* @param key must not be {@literal null}.
* @param range must not be {@literal null}.
* @param limit can be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @see <a href="https://redis.io/commands/zrevrangebyscore">Redis Documentation: ZREVRANGEBYSCORE</a>
* @since 3.5 (or next version number)
*/
@Nullable
Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, Range<Double> range, Limit limit);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
Expand Down Expand Up @@ -53,6 +54,7 @@
* @author Mark Paluch
* @author Wongoo (望哥)
* @author Andrey Shlykov
* @author Kim Sumin
* @param <K> Key type
* @param <V> Value type
*/
Expand Down Expand Up @@ -661,4 +663,100 @@ void testZsetUnionWithAggregateWeights() {
assertThat(zSetOps.score(key1, value1)).isCloseTo(6.0, offset(0.1));
}

@ParameterizedRedisTest // GH-3139
void testRangeByScoreWithScoresWithRange() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();
V value3 = valueFactory.instance();

zSetOps.add(key, value1, 1.9);
zSetOps.add(key, value2, 3.7);
zSetOps.add(key, value3, 5.8);

Range<Double> range = Range.of(Range.Bound.inclusive(1.5), Range.Bound.exclusive(5.0));

Set<TypedTuple<V>> results = zSetOps.rangeByScoreWithScores(key, range);

assertThat(results).hasSize(2)
.contains(new DefaultTypedTuple<>(value1, 1.9))
.contains(new DefaultTypedTuple<>(value2, 3.7));
}

@ParameterizedRedisTest // GH-3139
void testRangeByScoreWithScoresWithRangeAndLimit() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();
V value3 = valueFactory.instance();
V value4 = valueFactory.instance();

zSetOps.add(key, value1, 1.0);
zSetOps.add(key, value2, 2.0);
zSetOps.add(key, value3, 3.0);
zSetOps.add(key, value4, 4.0);

Range<Double> range = Range.of(Range.Bound.unbounded(), Range.Bound.inclusive(4.0));
Limit limit = Limit.limit().offset(1).count(2);

Set<TypedTuple<V>> results = zSetOps.rangeByScoreWithScores(key, range, limit);

assertThat(results).hasSize(2)
.contains(new DefaultTypedTuple<>(value2, 2.0))
.contains(new DefaultTypedTuple<>(value3, 3.0));
}

@ParameterizedRedisTest // GH-3139
void testReverseRangeByScoreWithScoresWithRange() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();
V value3 = valueFactory.instance();

zSetOps.add(key, value1, 1.9);
zSetOps.add(key, value2, 3.7);
zSetOps.add(key, value3, 5.8);

Range<Double> range = Range.of(Range.Bound.inclusive(1.5), Range.Bound.exclusive(5.0));

Set<TypedTuple<V>> results = zSetOps.reverseRangeByScoreWithScores(key, range);

assertThat(results).hasSize(2)
.contains(new DefaultTypedTuple<>(value1, 1.9))
.contains(new DefaultTypedTuple<>(value2, 3.7));

assertThat(new ArrayList<>(results).get(0).getValue()).isEqualTo(value2);
assertThat(new ArrayList<>(results).get(1).getValue()).isEqualTo(value1);
}

@ParameterizedRedisTest // GH-3139
void testReverseRangeByScoreWithScoresWithRangeAndLimit() {

K key = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();
V value3 = valueFactory.instance();
V value4 = valueFactory.instance();

zSetOps.add(key, value1, 1.0);
zSetOps.add(key, value2, 2.0);
zSetOps.add(key, value3, 3.0);
zSetOps.add(key, value4, 4.0);

Range<Double> range = Range.of(Range.Bound.inclusive(1.0), Range.Bound.inclusive(4.0));
Limit limit = Limit.limit().offset(1).count(2);

Set<TypedTuple<V>> results = zSetOps.reverseRangeByScoreWithScores(key, range, limit);

assertThat(results).hasSize(2)
.contains(new DefaultTypedTuple<>(value2, 2.0))
.contains(new DefaultTypedTuple<>(value3, 3.0));

assertThat(new ArrayList<>(results).get(0).getValue()).isEqualTo(value3);
assertThat(new ArrayList<>(results).get(1).getValue()).isEqualTo(value2);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* Unit tests for {@link DefaultZSetOperations}.
*
* @author Christoph Strobl
* @author Kim Sumin
*/
class DefaultZSetOperationsUnitTests {

Expand Down Expand Up @@ -69,4 +70,42 @@ void delegatesAddIfAbsentForTuples() {

template.verify().zAdd(eq(template.serializeKey("key")), any(Set.class), eq(ZAddArgs.ifNotExists()));
}

@Test // GH-3139
void delegatesRangeByScoreWithScoresWithRange() {

Range<Double> range = Range.closed(1.0, 3.0);
zSetOperations.rangeByScoreWithScores("key", range);

template.verify().zRangeByScoreWithScores(eq(template.serializeKey("key")), eq(range));
}

@Test // GH-3139
void delegatesRangeByScoreWithScoresWithRangeAndLimit() {

Range<Double> range = Range.closed(1.0, 3.0);
org.springframework.data.redis.connection.Limit limit = org.springframework.data.redis.connection.Limit.limit().offset(1).count(2);
zSetOperations.rangeByScoreWithScores("key", range, limit);

template.verify().zRangeByScoreWithScores(eq(template.serializeKey("key")), eq(range), eq(limit));
}

@Test // GH-3139
void delegatesReverseRangeByScoreWithScoresWithRange() {

Range<Double> range = Range.closed(1.0, 3.0);
zSetOperations.reverseRangeByScoreWithScores("key", range);

template.verify().zRevRangeByScoreWithScores(eq(template.serializeKey("key")), eq(range));
}

@Test // GH-3139
void delegatesReverseRangeByScoreWithScoresWithRangeAndLimit() {

Range<Double> range = Range.closed(1.0, 3.0);
org.springframework.data.redis.connection.Limit limit = org.springframework.data.redis.connection.Limit.limit().offset(1).count(2);
zSetOperations.reverseRangeByScoreWithScores("key", range, limit);

template.verify().zRevRangeByScoreWithScores(eq(template.serializeKey("key")), eq(range), eq(limit));
}
}