DATAREDIS-305 - Add support for HSCAN.

HSCAN is directly supported by jedis and can be emulated for lettuce via eval.

SRP and JRedis will throw UnsupportedOperationException.

We have modified the ScanCursor implementation to take Collection instead of List.

Currently naming for scan commands is not consistent for scan, sscan and hscan. There’s a clean up task for those commands when done with all scan related commands.

Original pull request: #78.
This commit is contained in:
Christoph Strobl
2014-06-02 10:39:48 +02:00
committed by Thomas Darimont
parent 1831ae9db7
commit ba88249188
16 changed files with 372 additions and 39 deletions

View File

@@ -1979,6 +1979,44 @@ public abstract class AbstractConnectionIntegrationTests {
assertThat(i, is(6));
}
/**
* @see DATAREDIS-305
*/
@Test
@IfProfileValue(name = "redisVersion", value = "2.8+")
public void hScanShouldReadEntireValueRange() {
if (!ConnectionUtils.isJedis(connectionFactory) && !ConnectionUtils.isLettuce(connectionFactory)) {
throw new AssumptionViolatedException("HSCAN is only available for jedis and lettuce");
}
if (connection.isPipelined() || connection.isQueueing()) {
throw new AssumptionViolatedException("HSCAN is only available in non pipeline | queue mode.");
}
connection.hSet("hscankey", "bar", "foobar");
connection.hSet("hscankey", "foo-1", "v-1");
connection.hSet("hscankey", "foo-2", "v-2");
connection.hSet("hscankey", "foo-3", "v-3");
Cursor<Map.Entry<String, String>> cursor = connection
.hScan("hscankey", scanOptions().count(2).match("fo*").build());
int i = 0;
while (cursor.hasNext()) {
String key = cursor.next().getKey();
assertThat(key, not(containsString("bar")));
assertThat(key, containsString("foo"));
i++;
}
assertThat(i, is(3));
}
protected void verifyResults(List<Object> expected) {
assertEquals(expected, getResults());
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2013 the original author or authors.
* Copyright 2013-2014 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.
@@ -15,13 +15,12 @@
*/
package org.springframework.data.redis.core;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.springframework.data.redis.matcher.RedisTestMatchers.isEqual;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Iterator;
import java.util.Map;
import org.junit.After;
@@ -35,12 +34,14 @@ import org.springframework.data.redis.RawObjectFactory;
import org.springframework.data.redis.SettingsUtils;
import org.springframework.data.redis.StringObjectFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.srp.SrpConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.test.annotation.IfProfileValue;
/**
* Integration test of {@link DefaultHashOperations}
*
* @author Jennifer Hickey
* @author Christoph Strobl
* @param <K> Key type
* @param <HK> Hash key type
* @param <HV> Hash value type
@@ -70,17 +71,17 @@ public class DefaultHashOperationsTests<K, HK, HV> {
ObjectFactory<String> stringFactory = new StringObjectFactory();
ObjectFactory<byte[]> rawFactory = new RawObjectFactory();
SrpConnectionFactory srConnFactory = new SrpConnectionFactory();
srConnFactory.setPort(SettingsUtils.getPort());
srConnFactory.setHostName(SettingsUtils.getHost());
srConnFactory.afterPropertiesSet();
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setPort(SettingsUtils.getPort());
jedisConnectionFactory.setHostName(SettingsUtils.getHost());
jedisConnectionFactory.afterPropertiesSet();
RedisTemplate<String, String> stringTemplate = new StringRedisTemplate();
stringTemplate.setConnectionFactory(srConnFactory);
stringTemplate.setConnectionFactory(jedisConnectionFactory);
stringTemplate.afterPropertiesSet();
RedisTemplate<byte[], byte[]> rawTemplate = new RedisTemplate<byte[], byte[]>();
rawTemplate.setConnectionFactory(srConnFactory);
rawTemplate.setConnectionFactory(jedisConnectionFactory);
rawTemplate.setEnableDefaultSerializer(false);
rawTemplate.afterPropertiesSet();
@@ -112,10 +113,11 @@ public class DefaultHashOperationsTests<K, HK, HV> {
HV val2 = hashValueFactory.instance();
hashOps.put(key, key1, val1);
hashOps.put(key, key2, val2);
Map<HK, HV> expected = new LinkedHashMap<HK, HV>();
expected.put(key1, val1);
expected.put(key2, val2);
assertThat(hashOps.entries(key), isEqual(expected));
for (Map.Entry<HK, HV> entry : hashOps.entries(key).entrySet()) {
assertThat(entry.getKey(), anyOf(equalTo(key1), equalTo(key2)));
assertThat(entry.getValue(), anyOf(equalTo(val1), equalTo(val2)));
}
}
@Test
@@ -130,4 +132,33 @@ public class DefaultHashOperationsTests<K, HK, HV> {
hashOps.delete(key, key1, key2);
assertTrue(hashOps.keys(key).isEmpty());
}
/**
* @see DATAREDIS-305
*/
@Test
@IfProfileValue(name = "redisVersion", value = "2.8+")
public void testHScanReadsValuesFully() {
K key = keyFactory.instance();
HK key1 = hashKeyFactory.instance();
HV val1 = hashValueFactory.instance();
HK key2 = hashKeyFactory.instance();
HV val2 = hashValueFactory.instance();
hashOps.put(key, key1, val1);
hashOps.put(key, key2, val2);
Iterator<Map.Entry<HK, HV>> it = hashOps.scan(key, ScanOptions.scanOptions().count(1).build());
long count = 0;
while (it.hasNext()) {
Map.Entry<HK, HV> entry = it.next();
assertThat(entry.getKey(), anyOf(equalTo(key1), equalTo(key2)));
assertThat(entry.getValue(), anyOf(equalTo(val1), equalTo(val2)));
count++;
}
assertThat(count, is(hashOps.size(key)));
}
}