From 1a0d934efb8c4f25a03f21af183909f2b2b950b7 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 15 Mar 2016 08:21:48 +0100 Subject: [PATCH 1/2] DATAREDIS-479 - Upgrade to jedis to 2.8.1. Prepare issue branch. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index e2e0f007c6..52a92e9207 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-redis - 1.7.0.BUILD-SNAPSHOT + 1.7.0.DATAREDIS-479-SNAPSHOT Spring Data Redis From 8c1d0230b44dda5d1657f0f4dd3973929cf35654 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 15 Mar 2016 09:13:05 +0100 Subject: [PATCH 2/2] DATAREDIS-479 - Upgrade to jedis to 2.8.1. Update jedis driver to 2.8.1. Use binary method for sscan and implement zscan and hscan methods. Move ScanOptions to ScanParams conversion to JedisConverters. --- pom.xml | 2 +- .../jedis/JedisClusterConnection.java | 31 ++++++++--- .../connection/jedis/JedisConnection.java | 23 ++------ .../connection/jedis/JedisConverters.java | 24 +++++++++ .../jedis/JedisClusterConnectionTests.java | 54 +++++++++++++++++++ 5 files changed, 107 insertions(+), 27 deletions(-) diff --git a/pom.xml b/pom.xml index 52a92e9207..474950c8e3 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 1.4.8 2.2 3.4.2.Final - 2.8.0 + 2.8.1 0.7 06052013 1.01 diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java index b1c3aaa577..f7a87624b8 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * Copyright 2015-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -74,6 +74,7 @@ import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisCluster; import redis.clients.jedis.JedisPool; +import redis.clients.jedis.ScanParams; import redis.clients.jedis.ZParams; /** @@ -1532,10 +1533,9 @@ public Cursor sScan(final byte[] key, ScanOptions options) { @Override protected ScanIteration doScan(long cursorId, ScanOptions options) { - redis.clients.jedis.ScanResult result = cluster.sscan(JedisConverters.toString(key), - Long.toString(cursorId)); - return new ScanIteration(Long.valueOf(result.getCursor()), - JedisConverters.stringListToByteList().convert(result.getResult())); + ScanParams params = JedisConverters.toScanParams(options); + redis.clients.jedis.ScanResult result = cluster.sscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration(Long.valueOf(result.getStringCursor()), result.getResult()); } }.open(); } @@ -2158,8 +2158,19 @@ public Long zInterStore(byte[] destKey, Aggregate aggregate, int[] weights, byte } @Override - public Cursor zScan(byte[] key, ScanOptions options) { - throw new UnsupportedOperationException("Jedis does currently not support binary zscan command."); + public Cursor zScan(final byte[] key, final ScanOptions options) { + return new ScanCursor(options) { + + @Override + protected ScanIteration doScan(long cursorId, ScanOptions options) { + + ScanParams params = JedisConverters.toScanParams(options); + + redis.clients.jedis.ScanResult result = cluster.zscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters + .tuplesToTuples().convert(result.getResult())); + } + }.open(); } /* @@ -2387,7 +2398,11 @@ public Cursor> hScan(final byte[] key, ScanOptions options @Override protected ScanIteration> doScan(long cursorId, ScanOptions options) { - throw new UnsupportedOperationException("Jedis does currently not support binary hscan"); + + ScanParams params = JedisConverters.toScanParams(options); + + redis.clients.jedis.ScanResult> result = cluster.hscan(key, JedisConverters.toBytes(cursorId), params); + return new ScanIteration>(Long.valueOf(result.getStringCursor()), result.getResult()); } }.open(); } diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java index be7094d98d..94ffd486a1 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConnection.java @@ -60,7 +60,6 @@ import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; import redis.clients.jedis.BinaryJedis; import redis.clients.jedis.BinaryJedisPubSub; @@ -92,6 +91,7 @@ * @author Konstantin Shchepanovskyi * @author David Liu * @author Milan Agatonovic + * @author Mark Paluch */ public class JedisConnection extends AbstractRedisConnection { @@ -3315,7 +3315,7 @@ protected ScanIteration doScan(long cursorId, ScanOptions options) { throw new UnsupportedOperationException("'SCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = jedis.scan(Long.toString(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters.stringListToByteList() .convert(result.getResult())); @@ -3352,7 +3352,7 @@ protected ScanIteration doScan(byte[] key, long cursorId, ScanOptions opt throw new UnsupportedOperationException("'ZSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); ScanResult result = jedis.zscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), JedisConverters @@ -3389,7 +3389,7 @@ protected ScanIteration doScan(byte[] key, long cursorId, ScanOptions op throw new UnsupportedOperationException("'SSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); redis.clients.jedis.ScanResult result = jedis.sscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration(Long.valueOf(result.getStringCursor()), result.getResult()); @@ -3424,7 +3424,7 @@ protected ScanIteration> doScan(byte[] key, long cursorId, throw new UnsupportedOperationException("'HSCAN' cannot be called in pipeline / transaction mode."); } - ScanParams params = prepareScanParams(options); + ScanParams params = JedisConverters.toScanParams(options); ScanResult> result = jedis.hscan(key, JedisConverters.toBytes(cursorId), params); return new ScanIteration>(Long.valueOf(result.getStringCursor()), result.getResult()); @@ -3432,19 +3432,6 @@ protected ScanIteration> doScan(byte[] key, long cursorId, }.open(); } - private ScanParams prepareScanParams(ScanOptions options) { - ScanParams sp = new ScanParams(); - if (!options.equals(ScanOptions.NONE)) { - if (options.getCount() != null) { - sp.count(options.getCount().intValue()); - } - if (StringUtils.hasText(options.getPattern())) { - sp.match(options.getPattern()); - } - } - return sp; - } - /** * Specifies if pipelined results should be converted to the expected data type. If false, results of * {@link #closePipeline()} and {@link #exec()} will be of the type returned by the Jedis driver diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java index 6913073aee..c241c60a6c 100644 --- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java +++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java @@ -42,6 +42,7 @@ import org.springframework.data.redis.connection.convert.MapConverter; import org.springframework.data.redis.connection.convert.SetConverter; import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.types.Expiration; import org.springframework.data.redis.core.types.RedisClientInfo; import org.springframework.util.Assert; @@ -51,6 +52,7 @@ import redis.clients.jedis.BinaryClient.LIST_POSITION; import redis.clients.jedis.BitOP; +import redis.clients.jedis.ScanParams; import redis.clients.jedis.SortingParams; import redis.clients.util.SafeEncoder; @@ -432,4 +434,26 @@ private static byte[] boundaryToBytes(Boundary boundary, byte[] inclPrefix, byte return buffer.array(); } + + /** + * Convert {@link ScanOptions} to Jedis {@link ScanParams}. + * + * @param options + * @return + */ + public static ScanParams toScanParams(ScanOptions options) { + + ScanParams sp = new ScanParams(); + + if (!options.equals(ScanOptions.NONE)) { + if (options.getCount() != null) { + sp.count(options.getCount().intValue()); + } + if (StringUtils.hasText(options.getPattern())) { + sp.match(options.getPattern()); + } + } + return sp; + } + } diff --git a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java index da207dab9a..527e3c1a03 100644 --- a/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java +++ b/src/test/java/org/springframework/data/redis/connection/jedis/JedisClusterConnectionTests.java @@ -20,6 +20,7 @@ import static org.hamcrest.number.IsCloseTo.*; import static org.junit.Assert.*; import static org.springframework.data.redis.connection.ClusterTestVariables.*; +import static org.springframework.data.redis.core.ScanOptions.scanOptions; import java.io.IOException; import java.util.Arrays; @@ -1769,6 +1770,33 @@ public void zInterStoreShouldWorkForSameSlotKeys() { public void zInterStoreShouldThrowExceptionWhenKeysDoNotMapToSameSlots() { clusterConnection.zInterStore(KEY_3_BYTES, KEY_1_BYTES, KEY_2_BYTES); } + + + /** + * @see DATAREDIS-479 + */ + @Test + public void zScanShouldReadEntireValueRange() { + + nativeConnection.zadd(KEY_1_BYTES, 2, VALUE_1_BYTES); + nativeConnection.zadd(KEY_1_BYTES, 1, VALUE_2_BYTES); + nativeConnection.zadd(KEY_1_BYTES, 4, VALUE_3_BYTES); + + Cursor tuples = clusterConnection.zScan(KEY_1_BYTES, ScanOptions.NONE); + + int count = 0; + while (tuples.hasNext()) { + + Tuple tuple = tuples.next(); + + assertThat(tuple.getValue(), anyOf(equalTo(VALUE_1_BYTES), equalTo(VALUE_2_BYTES), equalTo(VALUE_3_BYTES))); + assertThat(tuple.getScore(), anyOf(equalTo(1D), equalTo(2D), equalTo(4D))); + + count++; + } + + assertThat(count, equalTo(3)); + } /** * @see DATAREDIS-315 @@ -1952,6 +1980,32 @@ public void hGetAllShouldRetrieveEntriesCorrectly() { assertThat(hGetAll.containsKey(KEY_2_BYTES), is(true)); assertThat(hGetAll.containsKey(KEY_3_BYTES), is(true)); } + + /** + * @see DATAREDIS-479 + */ + @Test + public void hScanShouldReadEntireValueRange() { + + clusterConnection.hSet(KEY_1_BYTES, KEY_1_BYTES, VALUE_1_BYTES); + clusterConnection.hSet(KEY_1_BYTES, KEY_2_BYTES, VALUE_2_BYTES); + clusterConnection.hSet(KEY_1_BYTES, KEY_3_BYTES, VALUE_3_BYTES); + + Cursor> cursor = clusterConnection + .hScan(KEY_1_BYTES, scanOptions().match("key*").build()); + + int i = 0; + while (cursor.hasNext()) { + + byte[] key = cursor.next().getKey(); + + assertThat(key, anyOf(equalTo(KEY_1_BYTES), equalTo(KEY_2_BYTES), equalTo(KEY_3_BYTES))); + + i++; + } + + assertThat(i, is(3)); + } /** * @see DATAREDIS-315