keySet, values and entrySet methods with pattern added to RMap objects. #978

pull/1027/head
Nikita 8 years ago
parent 7fb7462df7
commit ecbc0b751d

@ -667,18 +667,18 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
}
@Override
public Set<K> keySet() {
return new KeySet();
public Set<K> keySet(String pattern) {
return new KeySet(pattern);
}
@Override
public Collection<V> values() {
return new Values();
public Collection<V> values(String keyPattern) {
return new Values(keyPattern);
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new EntrySet();
public Set<java.util.Map.Entry<K, V>> entrySet(String keyPattern) {
return new EntrySet(keyPattern);
}
private Iterator<Map.Entry<K,V>> cacheEntrySetIterator() {
@ -732,9 +732,15 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final class KeySet extends AbstractSet<K> {
private final String pattern;
public KeySet(String pattern) {
this.pattern = pattern;
}
@Override
public Iterator<K> iterator() {
return new CompositeIterable<K>(cacheKeySetIterator(), RedissonLocalCachedMap.super.keySet().iterator()) {
return new CompositeIterable<K>(cacheKeySetIterator(), RedissonLocalCachedMap.super.keySet(pattern).iterator()) {
@Override
boolean isCacheContains(Object object) {
@ -769,9 +775,15 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final class Values extends AbstractCollection<V> {
private final String keyPattern;
public Values(String keyPattern) {
this.keyPattern = keyPattern;
}
@Override
public Iterator<V> iterator() {
final Iterator<Map.Entry<K, V>> iter = RedissonLocalCachedMap.this.entrySet().iterator();
final Iterator<Map.Entry<K, V>> iter = RedissonLocalCachedMap.this.entrySet(keyPattern).iterator();
return new Iterator<V>() {
@Override
@ -810,8 +822,14 @@ public class RedissonLocalCachedMap<K, V> extends RedissonMap<K, V> implements R
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
private final String keyPattern;
public EntrySet(String keyPattern) {
this.keyPattern = keyPattern;
}
public final Iterator<Map.Entry<K,V>> iterator() {
return new CompositeIterable<Map.Entry<K,V>>(cacheEntrySetIterator(), RedissonLocalCachedMap.super.entrySet().iterator()) {
return new CompositeIterable<Map.Entry<K,V>>(cacheEntrySetIterator(), RedissonLocalCachedMap.super.entrySet(keyPattern).iterator()) {
@Override
boolean isCacheContains(Map.Entry<K,V> entry) {

@ -15,7 +15,6 @@
*/
package org.redisson;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.util.AbstractCollection;
@ -322,17 +321,29 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public Set<K> keySet() {
return new KeySet();
return keySet(null);
}
public Set<K> keySet(String pattern) {
return new KeySet(pattern);
}
@Override
public Collection<V> values() {
return new Values();
return values(null);
}
public Collection<V> values(String keyPattern) {
return new Values(keyPattern);
}
@Override
public Set<java.util.Map.Entry<K, V>> entrySet() {
return new EntrySet();
return entrySet(null);
}
public Set<java.util.Map.Entry<K, V>> entrySet(String keyPattern) {
return new EntrySet(keyPattern);
}
@Override
@ -938,9 +949,14 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
return get(fastRemoveAsync(keys));
}
MapScanResult<ScanObjectEntry, ScanObjectEntry> scanIterator(String name, InetSocketAddress client, long startPos) {
MapScanResult<ScanObjectEntry, ScanObjectEntry> scanIterator(String name, InetSocketAddress client, long startPos, String pattern) {
if (pattern == null) {
RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>> f
= commandExecutor.readAsync(client, name, new MapScanCodec(codec), RedisCommands.HSCAN, name, startPos);
return get(f);
}
RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>> f
= commandExecutor.readAsync(client, name, new MapScanCodec(codec), RedisCommands.HSCAN, name, startPos);
= commandExecutor.readAsync(client, name, new MapScanCodec(codec), RedisCommands.HSCAN, name, startPos, "MATCH", pattern);
return get(f);
}
@ -1020,8 +1036,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
return h;
}
protected Iterator<K> keyIterator() {
return new RedissonMapIterator<K, V, K>(RedissonMap.this) {
protected Iterator<K> keyIterator(String pattern) {
return new RedissonMapIterator<K, V, K>(RedissonMap.this, pattern) {
@Override
protected K getValue(java.util.Map.Entry<ScanObjectEntry, ScanObjectEntry> entry) {
return (K) entry.getKey().getObj();
@ -1029,11 +1045,17 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
};
}
final class KeySet extends AbstractSet<K> {
class KeySet extends AbstractSet<K> {
private final String pattern;
public KeySet(String pattern) {
this.pattern = pattern;
}
@Override
public Iterator<K> iterator() {
return keyIterator();
return keyIterator(pattern);
}
@Override
@ -1048,6 +1070,13 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public int size() {
if (pattern != null) {
int size = 0;
for (K val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
@ -1058,8 +1087,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
}
protected Iterator<V> valueIterator() {
return new RedissonMapIterator<K, V, V>(RedissonMap.this) {
protected Iterator<V> valueIterator(String pattern) {
return new RedissonMapIterator<K, V, V>(RedissonMap.this, pattern) {
@Override
protected V getValue(java.util.Map.Entry<ScanObjectEntry, ScanObjectEntry> entry) {
return (V) entry.getValue().getObj();
@ -1069,9 +1098,15 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
final class Values extends AbstractCollection<V> {
private final String keyPattern;
public Values(String keyPattern) {
this.keyPattern = keyPattern;
}
@Override
public Iterator<V> iterator() {
return valueIterator();
return valueIterator(keyPattern);
}
@Override
@ -1081,6 +1116,14 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
@Override
public int size() {
if (keyPattern != null) {
int size = 0;
for (V val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}
@ -1091,8 +1134,8 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
}
protected Iterator<Map.Entry<K,V>> entryIterator() {
return new RedissonMapIterator<K, V, Map.Entry<K, V>>(RedissonMap.this);
protected Iterator<Map.Entry<K,V>> entryIterator(String pattern) {
return new RedissonMapIterator<K, V, Map.Entry<K, V>>(RedissonMap.this, pattern);
}
private void loadValue(final K key, final RPromise<V> result, final boolean replaceValue) {
@ -1179,8 +1222,14 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
private final String keyPattern;
public EntrySet(String keyPattern) {
this.keyPattern = keyPattern;
}
public final Iterator<Map.Entry<K,V>> iterator() {
return entryIterator();
return entryIterator(keyPattern);
}
public final boolean contains(Object o) {
@ -1203,6 +1252,14 @@ public class RedissonMap<K, V> extends RedissonExpirable implements RMap<K, V> {
}
public final int size() {
if (keyPattern != null) {
int size = 0;
for (Entry val : this) {
size++;
}
return size;
}
return RedissonMap.this.size();
}

@ -922,17 +922,29 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
}
@Override
MapScanResult<ScanObjectEntry, ScanObjectEntry> scanIterator(String name, InetSocketAddress client, long startPos) {
return get(scanIteratorAsync(name, client, startPos));
MapScanResult<ScanObjectEntry, ScanObjectEntry> scanIterator(String name, InetSocketAddress client, long startPos, String pattern) {
return get(scanIteratorAsync(name, client, startPos, pattern));
}
public RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>> scanIteratorAsync(final String name, InetSocketAddress client, long startPos) {
public RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>> scanIteratorAsync(final String name, InetSocketAddress client, long startPos, String pattern) {
List<Object> params = new ArrayList<Object>();
params.add(System.currentTimeMillis());
params.add(startPos);
if (pattern != null) {
params.add(pattern);
}
RedisCommand<MapCacheScanResult<Object, Object>> EVAL_HSCAN = new RedisCommand<MapCacheScanResult<Object, Object>>("EVAL",
new ListMultiDecoder(new LongMultiDecoder(), new ObjectMapDecoder(new MapScanCodec(codec)), new ObjectListDecoder(codec), new MapCacheScanResultReplayDecoder()), ValueType.MAP);
RFuture<MapCacheScanResult<ScanObjectEntry, ScanObjectEntry>> f = commandExecutor.evalReadAsync(client, name, codec, EVAL_HSCAN,
"local result = {}; "
+ "local idleKeys = {}; "
+ "local res = redis.call('hscan', KEYS[1], ARGV[2]); "
+ "local res; "
+ "if (#ARGV == 3) then "
+ " res = redis.call('hscan', KEYS[1], ARGV[2], 'match', ARGV[3]); "
+ "else "
+ " res = redis.call('hscan', KEYS[1], ARGV[2]); "
+ "end;"
+ "local currentTime = tonumber(ARGV[1]); "
+ "for i, value in ipairs(res[2]) do "
+ "if i % 2 == 0 then "
@ -960,7 +972,9 @@ public class RedissonMapCache<K, V> extends RedissonMap<K, V> implements RMapCac
+ "end; "
+ "end; "
+ "end;"
+ "return {res[1], result, idleKeys};", Arrays.<Object>asList(name, getTimeoutSetName(name), getIdleSetName(name)), System.currentTimeMillis(), startPos);
+ "return {res[1], result, idleKeys};",
Arrays.<Object>asList(name, getTimeoutSetName(name), getIdleSetName(name)),
params.toArray());
f.addListener(new FutureListener<MapCacheScanResult<ScanObjectEntry, ScanObjectEntry>>() {
@Override

@ -20,16 +20,26 @@ import java.util.Map.Entry;
import org.redisson.client.protocol.decoder.MapScanResult;
import org.redisson.client.protocol.decoder.ScanObjectEntry;
/**
*
* @author Nikita Koksharov
*
* @param <K> key type
* @param <V> value type
* @param <M> loaded value type
*/
public class RedissonMapIterator<K, V, M> extends RedissonBaseMapIterator<K, V, M> {
private final RedissonMap<K, V> map;
private final String pattern;
public RedissonMapIterator(RedissonMap<K, V> map) {
public RedissonMapIterator(RedissonMap<K, V> map, String pattern) {
this.map = map;
this.pattern = pattern;
}
protected MapScanResult<ScanObjectEntry, ScanObjectEntry> iterator() {
return map.scanIterator(map.getName(), client, nextIterPos);
return map.scanIterator(map.getName(), client, nextIterPos, pattern);
}
protected void removeKey() {

@ -187,7 +187,7 @@ public interface RKeys extends RKeysAsync {
String randomKey();
/**
* Find keys by key search pattern at once
* Find keys by key search pattern at once using KEYS command.
*
* Supported glob-style patterns:
* h?llo subscribes to hello, hallo and hxllo

@ -291,22 +291,80 @@ public interface RMap<K, V> extends ConcurrentMap<K, V>, RExpirable, RMapAsync<K
/**
* Returns key set.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllKeySet()} does.
*
* @return key set
*/
@Override
Set<K> keySet();
/**
* Returns values collections.
* Returns key set matches pattern.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllKeySet()} does.
*
* Supported glob-style patterns:
* <p>
* h?llo subscribes to hello, hallo and hxllo
* <p>
* h*llo subscribes to hllo and heeeello
* <p>
* h[ae]llo subscribes to hello and hallo, but not hillo
*
* @param pattern - key pattern
* @return key set
*/
Set<K> keySet(String pattern);
/**
* Returns values collection.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllValues()} does.
*
* @return value collection
*/
@Override
Collection<V> values();
/**
* Returns values collections.
* Returns values collection matches key pattern.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllValues()} does.
*
* Supported glob-style patterns:
* <p>
* h?llo subscribes to hello, hallo and hxllo
* <p>
* h*llo subscribes to hllo and heeeello
* <p>
* h[ae]llo subscribes to hello and hallo, but not hillo
*
* @param keyPattern - key pattern
* @return value collection
*/
Collection<V> values(String keyPattern);
/**
* Returns map entries collection.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllEntrySet()} does.
*
* @return map entries collection
*/
@Override
Set<java.util.Map.Entry<K, V>> entrySet();
/**
* Returns map entries collection matches key pattern.
* This method <b>DOESN'T</b> fetch all of them as {@link #readAllEntrySet()} does.
*
* Supported glob-style patterns:
* <p>
* h?llo subscribes to hello, hallo and hxllo
* <p>
* h*llo subscribes to hllo and heeeello
* <p>
* h[ae]llo subscribes to hello and hallo, but not hillo
*
* @param keyPattern - key pattern
* @return map entries collection
*/
Set<java.util.Map.Entry<K, V>> entrySet(String keyPattern);
}

@ -168,7 +168,7 @@ public class RedissonMapCacheReactive<K, V> extends RedissonExpirableReactive im
return reactive(new Supplier<RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>>>() {
@Override
public RFuture<MapScanResult<ScanObjectEntry, ScanObjectEntry>> get() {
return ((RedissonMapCache<K, V>)mapCache).scanIteratorAsync(getName(), client, startPos);
return ((RedissonMapCache<K, V>)mapCache).scanIteratorAsync(getName(), client, startPos, null);
}
});
}

@ -3,6 +3,7 @@ package org.redisson;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.Serializable;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@ -30,6 +31,7 @@ import org.redisson.api.map.event.EntryRemovedListener;
import org.redisson.api.map.event.EntryUpdatedListener;
import org.redisson.client.codec.DoubleCodec;
import org.redisson.client.codec.LongCodec;
import org.redisson.client.codec.StringCodec;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.codec.MsgPackJacksonCodec;
@ -495,6 +497,43 @@ public class RedissonMapCacheTest extends BaseMapTest {
Assert.assertTrue(map.values().contains(new SimpleValue("2")));
}
@Test
public void testKeySetByPattern() {
RMapCache<String, String> map = redisson.getMapCache("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200", 1, TimeUnit.MINUTES);
map.put("30", "300");
assertThat(map.keySet("?0")).containsExactly("10", "20", "30");
assertThat(map.keySet("1")).isEmpty();
assertThat(map.keySet("10")).containsExactly("10");
}
@Test
public void testValuesByPattern() {
RMapCache<String, String> map = redisson.getMapCache("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200", 1, TimeUnit.MINUTES);
map.put("30", "300");
assertThat(map.values("?0")).containsExactly("100", "200", "300");
assertThat(map.values("1")).isEmpty();
assertThat(map.values("10")).containsExactly("100");
}
@Test
public void testEntrySetByPattern() {
RMapCache<String, String> map = redisson.getMapCache("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200", 1, TimeUnit.MINUTES);
map.put("30", "300");
assertThat(map.entrySet("?0")).containsExactly(new AbstractMap.SimpleEntry("10", "100"), new AbstractMap.SimpleEntry("20", "200"), new AbstractMap.SimpleEntry("30", "300"));
assertThat(map.entrySet("1")).isEmpty();
assertThat(map.entrySet("10")).containsExactly(new AbstractMap.SimpleEntry("10", "100"));
}
@Test
public void testContainsValue() throws InterruptedException {
RMapCache<SimpleKey, SimpleValue> map = redisson.getMapCache("simple01", new MsgPackJacksonCodec());

@ -446,7 +446,43 @@ public class RedissonMapTest extends BaseMapTest {
Assert.assertTrue(map.keySet().contains(new SimpleKey("33")));
Assert.assertFalse(map.keySet().contains(new SimpleKey("44")));
}
@Test
public void testKeySetByPattern() {
RMap<String, String> map = redisson.getMap("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200");
map.put("30", "300");
assertThat(map.keySet("?0")).containsExactly("10", "20", "30");
assertThat(map.keySet("1")).isEmpty();
assertThat(map.keySet("10")).containsExactly("10");
}
@Test
public void testValuesByPattern() {
RMap<String, String> map = redisson.getMap("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200");
map.put("30", "300");
assertThat(map.values("?0")).containsExactly("100", "200", "300");
assertThat(map.values("1")).isEmpty();
assertThat(map.values("10")).containsExactly("100");
}
@Test
public void testEntrySetByPattern() {
RMap<String, String> map = redisson.getMap("simple", StringCodec.INSTANCE);
map.put("10", "100");
map.put("20", "200");
map.put("30", "300");
assertThat(map.entrySet("?0")).containsExactly(new AbstractMap.SimpleEntry("10", "100"), new AbstractMap.SimpleEntry("20", "200"), new AbstractMap.SimpleEntry("30", "300"));
assertThat(map.entrySet("1")).isEmpty();
assertThat(map.entrySet("10")).containsExactly(new AbstractMap.SimpleEntry("10", "100"));
}
@Test
public void testReadAllKeySet() {
RMap<SimpleKey, SimpleValue> map = redisson.getMap("simple");

Loading…
Cancel
Save