From c909de1fb86c1fb3aa3c63836910a3a5c0188301 Mon Sep 17 00:00:00 2001 From: Costin Leau Date: Mon, 15 Nov 2010 13:35:14 +0100 Subject: [PATCH] + generified implementation for RedisSortedSet + added BoundedZSet/ZSetOperations --- .../redis/core/BoundZSetOperations.java | 52 +++++ .../core/DefaultBoundZSetOperations.java | 94 +++++++++ .../datastore/redis/core/RedisOperations.java | 5 + .../datastore/redis/core/RedisTemplate.java | 189 ++++++++++++++++-- .../datastore/redis/core/ZSetOperations.java | 51 +++++ .../redis/util/DefaultRedisSortedSet.java | 92 +++++---- .../datastore/redis/util/RedisSortedSet.java | 16 +- 7 files changed, 441 insertions(+), 58 deletions(-) create mode 100644 spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/BoundZSetOperations.java create mode 100644 spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultBoundZSetOperations.java create mode 100644 spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ZSetOperations.java diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/BoundZSetOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/BoundZSetOperations.java new file mode 100644 index 000000000..72d1e2dcd --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/BoundZSetOperations.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.datastore.redis.core; + +import java.util.Set; + + +/** + * ZSet (or SortedSet) operations bound to a certain key. + * + * @author Costin Leau + */ +public interface BoundZSetOperations extends KeyBound { + + RedisOperations getOperations(); + + void intersectAndStore(K destKey, K... keys); + + Set range(int start, int end); + + Set rangeByScore(double min, double max); + + void removeRange(int start, int end); + + void removeRangeByScore(double min, double max); + + void unionAndStore(K destKey, K... keys); + + boolean add(V value, double score); + + Integer rank(Object o); + + boolean remove(Object o); + + int size(); + + Set reverseRange(int start, int end); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultBoundZSetOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultBoundZSetOperations.java new file mode 100644 index 000000000..8b3fe5c6f --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/DefaultBoundZSetOperations.java @@ -0,0 +1,94 @@ +/* + * Copyright 2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.datastore.redis.core; + +import java.util.Set; + +/** + * Default implementation for {@link BoundZSetOperations}. + * + * @author Costin Leau + */ +class DefaultBoundZSetOperations extends DefaultKeyBound implements BoundZSetOperations { + + private final ZSetOperations ops; + + public DefaultBoundZSetOperations(K key, RedisTemplate template) { + super(key); + this.ops = template.zSetOps(); + } + + @Override + public boolean add(V value, double score) { + return ops.add(getKey(), value, score); + } + + @Override + public RedisOperations getOperations() { + return ops.getOperations(); + } + + @Override + public void intersectAndStore(K destKey, K... keys) { + ops.intersectAndStore(getKey(), destKey, keys); + } + + @Override + public Set range(int start, int end) { + return ops.range(getKey(), start, end); + } + + @Override + public Set rangeByScore(double min, double max) { + return ops.rangeByScore(getKey(), min, max); + } + + @Override + public Integer rank(Object o) { + return ops.rank(getKey(), o); + } + + @Override + public boolean remove(Object o) { + return ops.remove(getKey(), o); + } + + @Override + public void removeRange(int start, int end) { + ops.removeRange(getKey(), start, end); + } + + @Override + public void removeRangeByScore(double min, double max) { + ops.removeRangeByScore(getKey(), min, max); + } + + @Override + public Set reverseRange(int start, int end) { + return ops.reverseRange(getKey(), start, end); + } + + @Override + public int size() { + return ops.size(getKey()); + } + + @Override + public void unionAndStore(K destKey, K... keys) { + ops.unionAndStore(getKey(), destKey, keys); + } +} \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java index ac2e6eac5..52a4d5a89 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisOperations.java @@ -15,6 +15,7 @@ */ package org.springframework.datastore.redis.core; + /** * Basic set of Redis operations, implemented by {@link RedisTemplate}. * @@ -45,4 +46,8 @@ public interface RedisOperations { SetOperations setOps(); BoundSetOperations forSet(K key); + + ZSetOperations zSetOps(); + + BoundZSetOperations forZSet(K key); } diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java index 4486557a4..97db2af7e 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/RedisTemplate.java @@ -492,6 +492,16 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation // Set operations // + private K[] aggregateKeys(K key, K... keys) { + Object[] aggregate = new Object[keys.length + 1]; + aggregate[0] = key; + for (int i = 0; i < keys.length; i++) { + aggregate[i + 1] = keys[i]; + } + + return (K[]) aggregate; + } + @Override public BoundSetOperations forSet(K key) { return new DefaultBoundSetOperations(key, this); @@ -516,16 +526,6 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation }, false); } - private K[] aggregateKeys(K key, K... keys) { - Object[] aggregate = new Object[keys.length + 1]; - aggregate[0] = key; - for (int i = 0; i < keys.length; i++) { - aggregate[i + 1] = keys[i]; - } - - return (K[]) aggregate; - } - @Override public Set diff(final K key, final K... keys) { final byte[][] rawKeys = rawKeys(aggregateKeys(key, keys)); @@ -554,7 +554,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation @Override public RedisOperations getOperations() { - throw new UnsupportedOperationException(); + return RedisTemplate.this; } @Override @@ -648,7 +648,7 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation public void unionAndStore(K key, K destKey, K... keys) { final byte[][] rawKeys = rawKeys(aggregateKeys(key, keys)); final byte[] rawDestKey = rawKey(destKey); - Object rawValues = execute(new RedisCallback() { + execute(new RedisCallback() { @Override public Object doInRedis(RedisConnection connection) throws Exception { connection.sUnionStore(rawDestKey, rawKeys); @@ -657,4 +657,169 @@ public class RedisTemplate extends RedisAccessor implements RedisOperation }, false); } } + + // + // ZSet operations + // + + @Override + public BoundZSetOperations forZSet(K key) { + return new DefaultBoundZSetOperations(key, this); + } + + @Override + public ZSetOperations zSetOps() { + return new DefaultZSetOperations(); + } + + private class DefaultZSetOperations implements ZSetOperations { + + @Override + public boolean add(final K key, final V value, final double score) { + final byte[] rawKey = rawKey(key); + final byte[] rawValue = rawValue(value); + + return execute(new RedisCallback() { + @Override + public Boolean doInRedis(RedisConnection connection) throws Exception { + return connection.zAdd(rawKey, score, rawValue); + } + }, false); + } + + @Override + public RedisOperations getOperations() { + return RedisTemplate.this; + } + + @Override + public void intersectAndStore(K key, K destKey, K... keys) { + final byte[][] rawKeys = rawKeys(aggregateKeys(key, keys)); + final byte[] rawDestKey = rawKey(destKey); + execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws Exception { + connection.zInterStore(rawDestKey, rawKeys); + return null; + } + }, false); + } + + @Override + public Set range(K key, final int start, final int end) { + final byte[] rawKey = rawKey(key); + + Set rawValues = execute(new RedisCallback>() { + @Override + public Set doInRedis(RedisConnection connection) throws Exception { + return connection.zRange(rawKey, start, end); + } + }, false); + + return values(rawValues, Set.class); + } + + @Override + public Set rangeByScore(K key, final double min, final double max) { + final byte[] rawKey = rawKey(key); + + Set rawValues = execute(new RedisCallback>() { + @Override + public Set doInRedis(RedisConnection connection) throws Exception { + return connection.zRangeByScore(rawKey, min, max); + } + }, false); + + return values(rawValues, Set.class); + } + + @Override + public Integer rank(K key, Object o) { + final byte[] rawKey = rawKey(key); + final byte[] rawValue = rawValue(o); + + return execute(new RedisCallback() { + @Override + public Integer doInRedis(RedisConnection connection) throws Exception { + return connection.zRank(rawKey, rawValue); + } + }, false); + } + + @Override + public boolean remove(K key, Object o) { + final byte[] rawKey = rawKey(key); + final byte[] rawValue = rawValue(o); + + return execute(new RedisCallback() { + @Override + public Boolean doInRedis(RedisConnection connection) throws Exception { + return connection.zRem(rawKey, rawValue); + } + }, false); + } + + @Override + public void removeRange(K key, final int start, final int end) { + final byte[] rawKey = rawKey(key); + execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws Exception { + connection.zRemRange(rawKey, start, end); + return null; + } + }, false); + } + + @Override + public void removeRangeByScore(K key, final double min, final double max) { + final byte[] rawKey = rawKey(key); + execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws Exception { + connection.zRemRangeByScore(rawKey, min, max); + return null; + } + }, false); + } + + @Override + public Set reverseRange(K key, final int start, final int end) { + final byte[] rawKey = rawKey(key); + + Set rawValues = execute(new RedisCallback>() { + @Override + public Set doInRedis(RedisConnection connection) throws Exception { + return connection.zRevRange(rawKey, start, end); + } + }, false); + + return values(rawValues, Set.class); + } + + @Override + public int size(K key) { + final byte[] rawKey = rawKey(key); + + return execute(new RedisCallback() { + @Override + public Integer doInRedis(RedisConnection connection) throws Exception { + return connection.zCard(rawKey); + } + }, false); + } + + @Override + public void unionAndStore(K key, K destKey, K... keys) { + final byte[][] rawKeys = rawKeys(aggregateKeys(key, keys)); + final byte[] rawDestKey = rawKey(destKey); + execute(new RedisCallback() { + @Override + public Object doInRedis(RedisConnection connection) throws Exception { + connection.zUnionStore(rawDestKey, rawKeys); + return null; + } + }, false); + } + } } \ No newline at end of file diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ZSetOperations.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ZSetOperations.java new file mode 100644 index 000000000..0dc9d01c4 --- /dev/null +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/core/ZSetOperations.java @@ -0,0 +1,51 @@ +/* + * Copyright 2010 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.datastore.redis.core; + +import java.util.Set; + +/** + * Redis ZSet/sorted set specific operations. + * + * @author Costin Leau + */ +public interface ZSetOperations { + + void intersectAndStore(K key, K destKey, K... keys); + + Set range(K key, int start, int end); + + Set rangeByScore(K key, double min, double max); + + void removeRange(K key, int start, int end); + + void removeRangeByScore(K key, double min, double max); + + void unionAndStore(K key, K destKey, K... keys); + + boolean add(K key, V value, double score); + + Integer rank(K key, Object o); + + boolean remove(K key, Object o); + + int size(K key); + + Set reverseRange(K key, int start, int end); + + RedisOperations getOperations(); +} diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/DefaultRedisSortedSet.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/DefaultRedisSortedSet.java index 8228de6ff..2f937f942 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/DefaultRedisSortedSet.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/DefaultRedisSortedSet.java @@ -20,126 +20,142 @@ import java.util.Iterator; import java.util.Set; import java.util.SortedSet; -import org.springframework.datastore.redis.connection.RedisCommands; +import org.springframework.datastore.redis.core.BoundZSetOperations; +import org.springframework.datastore.redis.core.RedisOperations; /** * Default implementation for {@link RedisSortedSet}. * * @author Costin Leau */ -class DefaultRedisSortedSet extends AbstractRedisCollection implements RedisSortedSet { +class DefaultRedisSortedSet extends AbstractRedisCollection implements RedisSortedSet { - private class DefaultRedisSortedSetIterator extends RedisIterator { + private final BoundZSetOperations boundZSetOps; + + private class DefaultRedisSortedSetIterator extends RedisIterator { - public DefaultRedisSortedSetIterator(Iterator delegate) { + public DefaultRedisSortedSetIterator(Iterator delegate) { super(delegate); } @Override - protected void removeFromRedisStorage(String item) { + protected void removeFromRedisStorage(E item) { DefaultRedisSortedSet.this.remove(item); } } - public DefaultRedisSortedSet(String key, RedisCommands commands) { - super(key, commands); + /** + * Constructs a new DefaultRedisSortedSet instance. + * + * @param key + * @param operations + */ + public DefaultRedisSortedSet(String key, RedisOperations operations) { + super(key, operations); + boundZSetOps = operations.forZSet(key); + } + + + public DefaultRedisSortedSet(BoundZSetOperations boundOps) { + super(boundOps.getKey(), boundOps.getOperations()); + this.boundZSetOps = boundOps; } @Override - public RedisSortedSet intersectAndStore(String destKey, RedisSortedSet... sets) { - commands.zInterStore(destKey, extractKeys(sets)); - return new DefaultRedisSortedSet(destKey, commands); + public RedisSortedSet intersectAndStore(String destKey, RedisSortedSet... sets) { + boundZSetOps.intersectAndStore(destKey, extractKeys(sets)); + return new DefaultRedisSortedSet(boundZSetOps.getOperations().forZSet(destKey)); } @Override - public Set range(int start, int end) { - return commands.zRange(key, start, end); + public Set range(int start, int end) { + return boundZSetOps.range(start, end); } @Override - public Set rangeByScore(double min, double max) { - return commands.zRangeByScore(key, min, max); + public Set rangeByScore(double min, double max) { + return boundZSetOps.rangeByScore(min, max); } @Override - public RedisSortedSet remove(int start, int end) { - commands.zRemRange(key, start, end); + public RedisSortedSet remove(int start, int end) { + boundZSetOps.removeRange(start, end); return this; } @Override - public RedisSortedSet removeByScore(double min, double max) { - commands.zRemRangeByScore(key, min, max); + public RedisSortedSet removeByScore(double min, double max) { + boundZSetOps.removeRangeByScore(min, max); return this; } @Override - public RedisSortedSet unionAndStore(String destKey, RedisSortedSet... sets) { - commands.zUnionStore(destKey, extractKeys(sets)); - return new DefaultRedisSortedSet(destKey, commands); + public RedisSortedSet unionAndStore(String destKey, RedisSortedSet... sets) { + boundZSetOps.unionAndStore(destKey, extractKeys(sets)); + return new DefaultRedisSortedSet(boundZSetOps.getOperations().forZSet(destKey)); } @Override - public boolean add(String e) { - return commands.zAdd(key, 0, e); + public boolean add(E e) { + return boundZSetOps.add(e, 0); } @Override public void clear() { - commands.zRemRange(key, 0, -1); + boundZSetOps.removeRange(0, -1); } @Override public boolean contains(Object o) { - return (commands.zRank(key, o.toString()) != null); + return (boundZSetOps.rank(o) != null); } @Override - public Iterator iterator() { - return new DefaultRedisSortedSetIterator(commands.zRange(key, 0, -1).iterator()); + public Iterator iterator() { + return new DefaultRedisSortedSetIterator(boundZSetOps.range(0, -1).iterator()); } @Override public boolean remove(Object o) { - return commands.zRem(key, o.toString()); + return boundZSetOps.remove(o); } @Override public int size() { - return commands.zCard(key); + return boundZSetOps.size(); } @Override - public Comparator comparator() { + public Comparator comparator() { return null; } @Override - public String first() { - return commands.zRange(key, 0, 0).iterator().next(); + public E first() { + return boundZSetOps.range(0, 0).iterator().next(); } @Override - public SortedSet headSet(String toElement) { + public SortedSet headSet(E toElement) { throw new UnsupportedOperationException(); } @Override - public String last() { - return commands.zRevRange(key, 0, 0).iterator().next(); + public E last() { + return boundZSetOps.reverseRange(0, 0).iterator().next(); } @Override - public SortedSet subSet(String fromElement, String toElement) { + public SortedSet subSet(E fromElement, E toElement) { throw new UnsupportedOperationException(); } @Override - public SortedSet tailSet(String fromElement) { + public SortedSet tailSet(E fromElement) { throw new UnsupportedOperationException(); } - private String[] extractKeys(RedisSortedSet... sets) { + private String[] extractKeys(RedisSortedSet... sets) { String[] keys = new String[sets.length + 1]; keys[0] = key; for (int i = 0; i < keys.length; i++) { diff --git a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSortedSet.java b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSortedSet.java index b38baebd2..480f3f4a5 100644 --- a/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSortedSet.java +++ b/spring-datastore-redis/src/main/java/org/springframework/datastore/redis/util/RedisSortedSet.java @@ -20,21 +20,21 @@ import java.util.SortedSet; /** * Redis extension for the {@link SortedSet} contract. Supports {@link SortedSet} specific - * operations backed by Redis commands. + * operations backed by Redis operations. * * @author Costin Leau */ -public interface RedisSortedSet extends RedisStore, SortedSet { +public interface RedisSortedSet extends RedisStore, SortedSet { - RedisSortedSet intersectAndStore(String destKey, RedisSortedSet... sets); + RedisSortedSet intersectAndStore(String destKey, RedisSortedSet... sets); - RedisSortedSet unionAndStore(String destKey, RedisSortedSet... sets); + RedisSortedSet unionAndStore(String destKey, RedisSortedSet... sets); - Set range(int start, int end); + Set range(int start, int end); - Set rangeByScore(double min, double max); + Set rangeByScore(double min, double max); - RedisSortedSet remove(int start, int end); + RedisSortedSet remove(int start, int end); - RedisSortedSet removeByScore(double min, double max); + RedisSortedSet removeByScore(double min, double max); }