DATAREDIS-407 - Add zRangeByLex to ZSetOperations.

We now offer methods for ZRANGEBYLEX on ZSetOperations, BoundZSetOperations as well as via RedisZSet.

Original Pull Request: #166
This commit is contained in:
Mark Paluch
2016-02-05 12:44:15 +01:00
committed by Christoph Strobl
parent 008836066f
commit 4131f501a6
9 changed files with 287 additions and 1 deletions

View File

@@ -19,6 +19,7 @@ package org.springframework.data.redis.core;
import java.util.Collection;
import java.util.Set;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
@@ -26,6 +27,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
*/
public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
@@ -51,6 +53,10 @@ public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
Set<TypedTuple<V>> reverseRangeByScoreWithScores(double min, double max);
Set<V> rangeByLex(RedisZSetCommands.Range range);
Set<V> rangeByLex(RedisZSetCommands.Range range, RedisZSetCommands.Limit limit);
void removeRange(long start, long end);
void removeRangeByScore(double min, double max);

View File

@@ -20,6 +20,7 @@ import java.util.Collection;
import java.util.Set;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
@@ -27,6 +28,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
*/
class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> implements BoundZSetOperations<K, V> {
@@ -36,7 +38,7 @@ class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> impl
* Constructs a new <code>DefaultBoundZSetOperations</code> instance.
*
* @param key
* @param oeprations
* @param operations
*/
public DefaultBoundZSetOperations(K key, RedisOperations<K, V> operations) {
super(key, operations);
@@ -95,6 +97,16 @@ class DefaultBoundZSetOperations<K, V> extends DefaultBoundKeyOperations<K> impl
return ops.reverseRangeWithScores(getKey(), start, end);
}
@Override
public Set<V> rangeByLex(RedisZSetCommands.Range range) {
return ops.rangeByLex(getKey(), range);
}
@Override
public Set<V> rangeByLex(RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {
return ops.rangeByLex(getKey(), range, limit);
}
public Long rank(Object o) {
return ops.rank(getKey(), o);
}

View File

@@ -22,6 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
/**
@@ -31,6 +32,7 @@ import org.springframework.data.redis.connection.RedisZSetCommands.Tuple;
* @author Christoph Strobl
* @author Thomas Darimont
* @author David Liu
* @author Mark Paluch
*/
class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZSetOperations<K, V> {
@@ -141,6 +143,36 @@ class DefaultZSetOperations<K, V> extends AbstractOperations<K, V> implements ZS
return deserializeTupleValues(rawValues);
}
@Override
public Set<V> rangeByLex(K key, final RedisZSetCommands.Range range) {
final byte[] rawKey = rawKey(key);
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
public Set<byte[]> doInRedis(RedisConnection connection) {
return connection.zRangeByLex(rawKey, range);
}
}, true);
return deserializeValues(rawValues);
}
@Override
public Set<V> rangeByLex(K key, final RedisZSetCommands.Range range, final RedisZSetCommands.Limit limit) {
final byte[] rawKey = rawKey(key);
Set<byte[]> rawValues = execute(new RedisCallback<Set<byte[]>>() {
public Set<byte[]> doInRedis(RedisConnection connection) {
return connection.zRangeByLex(rawKey, range, limit);
}
}, true);
return deserializeValues(rawValues);
}
public Set<V> rangeByScore(K key, final double min, final double max) {
final byte[] rawKey = rawKey(key);

View File

@@ -19,11 +19,14 @@ package org.springframework.data.redis.core;
import java.util.Collection;
import java.util.Set;
import org.springframework.data.redis.connection.RedisZSetCommands;
/**
* Redis ZSet/sorted set specific operations.
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
*/
public interface ZSetOperations<K, V> {
@@ -52,6 +55,10 @@ public interface ZSetOperations<K, V> {
Set<TypedTuple<V>> reverseRangeWithScores(K key, long start, long end);
Set<V> rangeByLex(K key, RedisZSetCommands.Range range);
Set<V> rangeByLex(K key, RedisZSetCommands.Range range, RedisZSetCommands.Limit limit);
Set<V> rangeByScore(K key, double min, double max);
Set<V> rangeByScore(K key, double min, double max, long offset, long count);

View File

@@ -22,6 +22,7 @@ import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.redis.connection.DataType;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.ConvertingCursor;
import org.springframework.data.redis.core.Cursor;
@@ -35,6 +36,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
*
* @author Costin Leau
* @author Christoph Strobl
* @author Mark Paluch
*/
public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements RedisZSet<E> {
@@ -114,6 +116,16 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
return boundZSetOps.reverseRange(start, end);
}
@Override
public Set<E> rangeByLex(RedisZSetCommands.Range range) {
return boundZSetOps.rangeByLex(range);
}
@Override
public Set<E> rangeByLex(RedisZSetCommands.Range range, RedisZSetCommands.Limit limit) {
return boundZSetOps.rangeByLex(range, limit);
}
public Set<E> rangeByScore(double min, double max) {
return boundZSetOps.rangeByScore(min, max);
}

View File

@@ -22,6 +22,7 @@ import java.util.NoSuchElementException;
import java.util.Set;
import java.util.SortedSet;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
/**
@@ -31,6 +32,7 @@ import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
* Since using a {@link Comparator} does not apply, a ZSet implements the {@link SortedSet} methods where applicable.
*
* @author Costin Leau
* @author Mark Paluch
*/
public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
@@ -46,6 +48,10 @@ public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
Set<E> reverseRange(long start, long end);
Set<E> rangeByLex(RedisZSetCommands.Range range);
Set<E> rangeByLex(RedisZSetCommands.Range range, RedisZSetCommands.Limit limit);
Set<E> rangeByScore(double min, double max);
Set<E> reverseRangeByScore(double min, double max);

View File

@@ -17,7 +17,9 @@ package org.springframework.data.redis.core;
import static org.hamcrest.core.AnyOf.*;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeThat;
import static org.springframework.data.redis.matcher.RedisTestMatchers.*;
import java.util.Collection;
@@ -34,8 +36,13 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.data.redis.DoubleAsStringObjectFactory;
import org.springframework.data.redis.DoubleObjectFactory;
import org.springframework.data.redis.LongAsStringObjectFactory;
import org.springframework.data.redis.LongObjectFactory;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.test.annotation.IfProfileValue;
@@ -45,6 +52,7 @@ import org.springframework.test.annotation.IfProfileValue;
*
* @author Jennifer Hickey
* @author Christoph Strobl
* @author Mark Paluch
* @param <K> Key type
* @param <V> Value type
*/
@@ -165,6 +173,104 @@ public class DefaultZSetOperationsTests<K, V> {
assertThat(tuple, isEqual(new DefaultTypedTuple<V>(value2, 3.7)));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexUnbounded() {
assumeThat(valueFactory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
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);
Set<V> tuples = zSetOps.rangeByLex(key, RedisZSetCommands.Range.unbounded());
assertEquals(3, tuples.size());
V tuple = tuples.iterator().next();
assertThat(tuple, isEqual(value1));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexBounded() {
assumeThat(valueFactory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
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);
Set<V> tuples = zSetOps.rangeByLex(key, RedisZSetCommands.Range.range().gt(value1).lt(value3));
assertEquals(1, tuples.size());
V tuple = tuples.iterator().next();
assertThat(tuple, isEqual(value2));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexUnboundedWithLimit() {
assumeThat(valueFactory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
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);
Set<V> tuples = zSetOps.rangeByLex(key, RedisZSetCommands.Range.unbounded(),
RedisZSetCommands.Limit.limit().count(1).offset(1));
assertEquals(1, tuples.size());
V tuple = tuples.iterator().next();
assertThat(tuple, isEqual(value2));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexBoundedWithLimit() {
assumeThat(valueFactory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
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);
Set<V> tuples = zSetOps.rangeByLex(key, RedisZSetCommands.Range.range().gte(value1),
RedisZSetCommands.Limit.limit().count(1).offset(1));
assertEquals(1, tuples.size());
V tuple = tuples.iterator().next();
assertThat(tuple, isEqual(value2));
}
@Test
public void testAddMultiple() {
K key = keyFactory.instance();

View File

@@ -28,9 +28,14 @@ import java.util.Set;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.data.redis.DoubleAsStringObjectFactory;
import org.springframework.data.redis.DoubleObjectFactory;
import org.springframework.data.redis.LongAsStringObjectFactory;
import org.springframework.data.redis.LongObjectFactory;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.ConnectionUtils;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
@@ -46,6 +51,7 @@ import org.springframework.test.annotation.IfProfileValue;
* @author Costin Leau
* @author Jennifer Hickey
* @author Thomas Darimont
* @author Mark Paluch
*/
public abstract class AbstractRedisZSetTest<T> extends AbstractRedisCollectionTests<T> {
@@ -320,6 +326,100 @@ public abstract class AbstractRedisZSetTest<T> extends AbstractRedisCollectionTe
assertThat(tuple2.getScore(), isEqual(Double.valueOf(1)));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexUnbounded() {
assumeThat(factory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
T t1 = getT();
T t2 = getT();
T t3 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
Set<T> tuples = zSet.rangeByLex(RedisZSetCommands.Range.unbounded());
assertEquals(3, tuples.size());
T tuple = tuples.iterator().next();
assertThat(tuple, isEqual(t1));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexBounded() {
assumeThat(factory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
T t1 = getT();
T t2 = getT();
T t3 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
Set<T> tuples = zSet.rangeByLex(RedisZSetCommands.Range.range().gt(t1).lt(t3));
assertEquals(1, tuples.size());
T tuple = tuples.iterator().next();
assertThat(tuple, isEqual(t2));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexUnboundedWithLimit() {
assumeThat(factory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
T t1 = getT();
T t2 = getT();
T t3 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
Set<T> tuples = zSet.rangeByLex(RedisZSetCommands.Range.unbounded(),
RedisZSetCommands.Limit.limit().count(1).offset(1));
assertEquals(1, tuples.size());
T tuple = tuples.iterator().next();
assertThat(tuple, isEqual(t2));
}
/**
* @see DATAREDIS-407
*/
@Test
public void testRangeByLexBoundedWithLimit() {
assumeThat(factory, anyOf(instanceOf(DoubleObjectFactory.class), instanceOf(DoubleAsStringObjectFactory.class),
instanceOf(LongAsStringObjectFactory.class), instanceOf(LongObjectFactory.class)));
T t1 = getT();
T t2 = getT();
T t3 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
Set<T> tuples = zSet.rangeByLex(RedisZSetCommands.Range.range().gte(t1),
RedisZSetCommands.Limit.limit().count(1).offset(1));
assertEquals(1, tuples.size());
T tuple = tuples.iterator().next();
assertThat(tuple, isEqual(t2));
}
@Test
public void testReverseRangeByScore() {
assumeTrue(!ConnectionUtils.isJredis(template.getConnectionFactory()));

View File

@@ -18,6 +18,7 @@ package org.springframework.data.redis.support.collections;
import java.util.Arrays;
import java.util.Collection;
import org.springframework.data.redis.DoubleAsStringObjectFactory;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.Person;
import org.springframework.data.redis.PersonObjectFactory;
@@ -40,6 +41,7 @@ import org.springframework.oxm.xstream.XStreamMarshaller;
/**
* @author Costin Leau
* @author Thomas Darimont
* @author Mark Paluch
*/
public abstract class CollectionTestParams {
@@ -58,6 +60,7 @@ public abstract class CollectionTestParams {
// create Jedis Factory
ObjectFactory<String> stringFactory = new StringObjectFactory();
ObjectFactory<String> doubleAsStringObjectFactory = new DoubleAsStringObjectFactory();
ObjectFactory<Person> personFactory = new PersonObjectFactory();
ObjectFactory<byte[]> rawFactory = new RawObjectFactory();
@@ -214,6 +217,7 @@ public abstract class CollectionTestParams {
return Arrays.asList(new Object[][] {
{ stringFactory, stringTemplate },
{ doubleAsStringObjectFactory, stringTemplate },
{ personFactory, personTemplate },
{ stringFactory, xstreamStringTemplate },
{ personFactory, xstreamPersonTemplate },
@@ -237,6 +241,7 @@ public abstract class CollectionTestParams {
{ rawFactory, rawTemplateSRP },
// lettuce
{ stringFactory, stringTemplateLtc }, { personFactory, personTemplateLtc },
{ doubleAsStringObjectFactory, stringTemplateLtc }, { personFactory, personTemplateLtc },
{ stringFactory, xstreamStringTemplateLtc }, { personFactory, xstreamPersonTemplateLtc },
{ personFactory, jsonPersonTemplateLtc }, { personFactory, jackson2JsonPersonTemplateLtc },
{ rawFactory, rawTemplateLtc } });