Add difference, intersect, and union support to RedisZSet.

Original Pull Request: #2097
This commit is contained in:
Mark Paluch
2021-06-24 10:42:24 +02:00
committed by Christoph Strobl
parent 1d8ff06eef
commit 8dc6f014ee
4 changed files with 425 additions and 49 deletions

View File

@@ -481,6 +481,20 @@ public interface BoundZSetOperations<K, V> extends BoundKeyOperations<K> {
@Nullable
Set<TypedTuple<V>> differenceWithScores(Collection<K> otherKeys);
/**
* Diff sorted {@code sets} and store result in destination {@code destKey}.
*
* @param otherKeys must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @return {@literal null} when used in pipeline / transaction.
* @since 2.6
* @see <a href="https://redis.io/commands/zdiffstore">Redis Documentation: ZDIFFSTORE</a>
*/
@Nullable
default Long differenceAndStore(K otherKey, K destKey) {
return differenceAndStore(Collections.singleton(otherKey), destKey);
}
/**
* Diff sorted {@code sets} and store result in destination {@code destKey}.
*

View File

@@ -105,6 +105,100 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
this.defaultScore = defaultScore;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#difference(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<E> difference(RedisZSet<?> set) {
return boundZSetOps.difference(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#difference(java.util.Collection)
*/
@Override
public Set<E> difference(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.difference(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#differenceWithScores(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<TypedTuple<E>> differenceWithScores(RedisZSet<?> set) {
return boundZSetOps.differenceWithScores(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#differenceWithScores(java.util.Collection)
*/
@Override
public Set<TypedTuple<E>> differenceWithScores(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.differenceWithScores(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#differenceAndStore(org.springframework.data.redis.support.collections.RedisZSet, java.lang.String)
*/
@Override
public RedisZSet<E> differenceAndStore(RedisZSet<?> set, String destKey) {
boundZSetOps.differenceAndStore(set.getKey(), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#differenceAndStore(java.util.Collection, java.lang.String)
*/
@Override
public RedisZSet<E> differenceAndStore(Collection<? extends RedisZSet<?>> sets, String destKey) {
boundZSetOps.differenceAndStore(CollectionUtils.extractKeys(sets), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#intersect(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<E> intersect(RedisZSet<?> set) {
return boundZSetOps.intersect(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#intersect(java.util.Collection)
*/
@Override
public Set<E> intersect(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.intersect(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#intersectWithScores(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<TypedTuple<E>> intersectWithScores(RedisZSet<?> set) {
return boundZSetOps.intersectWithScores(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#intersectWithScores(java.util.Collection)
*/
@Override
public Set<TypedTuple<E>> intersectWithScores(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.intersectWithScores(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#intersectAndStore(org.springframework.data.redis.support.collections.RedisZSet, java.lang.String)
@@ -127,6 +221,62 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#union(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<E> union(RedisZSet<?> set) {
return boundZSetOps.union(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#union(java.util.Collection)
*/
@Override
public Set<E> union(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.union(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionWithScores(org.springframework.data.redis.support.collections.RedisZSet)
*/
@Override
public Set<TypedTuple<E>> unionWithScores(RedisZSet<?> set) {
return boundZSetOps.unionWithScores(set.getKey());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionWithScores(java.util.Collection)
*/
@Override
public Set<TypedTuple<E>> unionWithScores(Collection<? extends RedisZSet<?>> sets) {
return boundZSetOps.unionWithScores(CollectionUtils.extractKeys(sets));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionAndStore(org.springframework.data.redis.support.collections.RedisZSet, java.lang.String)
*/
@Override
public RedisZSet<E> unionAndStore(RedisZSet<?> set, String destKey) {
boundZSetOps.unionAndStore(set.getKey(), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionAndStore(java.util.Collection, java.lang.String)
*/
@Override
public RedisZSet<E> unionAndStore(Collection<? extends RedisZSet<?>> sets, String destKey) {
boundZSetOps.unionAndStore(CollectionUtils.extractKeys(sets), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#range(long, long)
@@ -247,26 +397,6 @@ public class DefaultRedisZSet<E> extends AbstractRedisCollection<E> implements R
return this;
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionAndStore(org.springframework.data.redis.support.collections.RedisZSet, java.lang.String)
*/
@Override
public RedisZSet<E> unionAndStore(RedisZSet<?> set, String destKey) {
boundZSetOps.unionAndStore(set.getKey(), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.support.collections.RedisZSet#unionAndStore(java.util.Collection, java.lang.String)
*/
@Override
public RedisZSet<E> unionAndStore(Collection<? extends RedisZSet<?>> sets, String destKey) {
boundZSetOps.unionAndStore(CollectionUtils.extractKeys(sets), destKey);
return new DefaultRedisZSet<>(boundZSetOps.getOperations().boundZSetOps(destKey), getDefaultScore());
}
/*
* (non-Javadoc)
* @see java.util.AbstractCollection#add(java.lang.Object)

View File

@@ -66,6 +66,100 @@ public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
return new DefaultRedisZSet<>(key, operations, defaultScore);
}
/**
* Diff this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the values that differ.
* @since 2.6
*/
Set<E> difference(RedisZSet<?> set);
/**
* Diff this set and other {@link RedisZSet}s.
*
* @param sets must not be {@literal null}.
* @return a {@link Set} containing the values that differ.
* @since 2.6
*/
Set<E> difference(Collection<? extends RedisZSet<?>> sets);
/**
* Diff this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the values that differ with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> differenceWithScores(RedisZSet<?> set);
/**
* Diff this set and other {@link RedisZSet}s.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the values that differ with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> differenceWithScores(Collection<? extends RedisZSet<?>> sets);
/**
* Create a new {@link RedisZSet} by diffing this sorted set and {@link RedisZSet} and store result in destination
* {@code destKey}.
*
* @param set must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @return a new {@link RedisZSet} pointing at {@code destKey}.
* @since 2.6
*/
RedisZSet<E> differenceAndStore(RedisZSet<?> set, String destKey);
/**
* Create a new {@link RedisZSet} by diffing this sorted set and the collection {@link RedisZSet} and store result in
* destination {@code destKey}.
*
* @param sets must not be {@literal null}.
* @param destKey must not be {@literal null}.
* @return a new {@link RedisZSet} pointing at {@code destKey}.
* @since 2.6
*/
RedisZSet<E> differenceAndStore(Collection<? extends RedisZSet<?>> sets, String destKey);
/**
* Intersect this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the intersecting values.
* @since 2.6
*/
Set<E> intersect(RedisZSet<?> set);
/**
* Intersect this set and other {@link RedisZSet}s.
*
* @param sets must not be {@literal null}.
* @return a {@link Set} containing the intersecting values.
* @since 2.6
*/
Set<E> intersect(Collection<? extends RedisZSet<?>> sets);
/**
* Intersect this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the intersecting values with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> intersectWithScores(RedisZSet<?> set);
/**
* Intersect this set and other {@link RedisZSet}s.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the intersecting values with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> intersectWithScores(Collection<? extends RedisZSet<?>> sets);
/**
* Create a new {@link RedisZSet} by intersecting this sorted set and {@link RedisZSet} and store result in
* destination {@code destKey}.
@@ -86,6 +180,42 @@ public interface RedisZSet<E> extends RedisCollection<E>, Set<E> {
*/
RedisZSet<E> intersectAndStore(Collection<? extends RedisZSet<?>> sets, String destKey);
/**
* Union this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the combined values.
* @since 2.6
*/
Set<E> union(RedisZSet<?> set);
/**
* Union this set and other {@link RedisZSet}s.
*
* @param sets must not be {@literal null}.
* @return a {@link Set} containing the combined values.
* @since 2.6
*/
Set<E> union(Collection<? extends RedisZSet<?>> sets);
/**
* Union this set and another {@link RedisZSet}.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the combined values with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> unionWithScores(RedisZSet<?> set);
/**
* Union this set and other {@link RedisZSet}s.
*
* @param set must not be {@literal null}.
* @return a {@link Set} containing the combined values with their scores.
* @since 2.6
*/
Set<TypedTuple<E>> unionWithScores(Collection<? extends RedisZSet<?>> sets);
/**
* Create a new {@link RedisZSet} by union this sorted set and {@link RedisZSet} and store result in destination
* {@code destKey}.

View File

@@ -36,6 +36,7 @@ import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.test.condition.EnabledOnCommand;
@@ -303,35 +304,6 @@ public abstract class AbstractRedisZSetTestIntegration<T> extends AbstractRedisC
return new DefaultRedisZSet<>((BoundZSetOperations<String, T>) zSet.getOperations().boundZSetOps(key));
}
@ParameterizedRedisTest
void testIntersectAndStore() {
RedisZSet<T> interSet1 = createZSetFor("test:zset:inter1");
RedisZSet<T> interSet2 = createZSetFor("test:zset:inter");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
interSet1.add(t2, 2);
interSet1.add(t4, 3);
interSet2.add(t2, 2);
interSet2.add(t3, 3);
String resultName = "test:zset:inter:result:1";
RedisZSet<T> inter = zSet.intersectAndStore(Arrays.asList(interSet1, interSet2), resultName);
assertThat(inter).hasSize(1);
assertThat(inter).contains(t2);
assertThat(inter.score(t2)).isEqualTo(Double.valueOf(6));
assertThat(inter.getKey()).isEqualTo(resultName);
}
@ParameterizedRedisTest
void testRange() {
T t1 = getT();
@@ -640,6 +612,136 @@ public abstract class AbstractRedisZSetTestIntegration<T> extends AbstractRedisC
assertThat(iterator.next()).isEqualTo(t4);
}
@ParameterizedRedisTest // GH-2041
@EnabledOnCommand("ZDIFF")
void testDifference() {
RedisZSet<T> set1 = createZSetFor("test:zset:set1");
RedisZSet<T> set2 = createZSetFor("test:zset:set2");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
set1.add(t2, 2);
set1.add(t4, 3);
set2.add(t2, 2);
set2.add(t3, 3);
assertThat(zSet.difference(Arrays.asList(set1, set2))).containsOnly(t1);
assertThat(zSet.differenceWithScores(Arrays.asList(set1, set2))).containsOnly(new DefaultTypedTuple<>(t1, 1d));
}
@ParameterizedRedisTest // GH-2041
void testDifferenceAndStore() {
RedisZSet<T> set1 = createZSetFor("test:zset:set1");
RedisZSet<T> set2 = createZSetFor("test:zset:set2");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
set1.add(t2, 2);
set1.add(t4, 3);
set2.add(t2, 2);
set2.add(t3, 3);
String resultName = "test:zset:inter:result:1";
RedisZSet<T> diff = zSet.differenceAndStore(Arrays.asList(set1, set2), resultName);
assertThat(diff).containsOnly(t1);
}
@ParameterizedRedisTest // GH-2042
@EnabledOnCommand("ZINTER")
void testIntersect() {
RedisZSet<T> interSet1 = createZSetFor("test:zset:inter1");
RedisZSet<T> interSet2 = createZSetFor("test:zset:inter");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
interSet1.add(t2, 2);
interSet1.add(t4, 3);
interSet2.add(t2, 2);
interSet2.add(t3, 3);
assertThat(zSet.intersect(Arrays.asList(interSet1, interSet2))).containsOnly(t2);
assertThat(zSet.intersectWithScores(Arrays.asList(interSet1, interSet2)))
.containsOnly(new DefaultTypedTuple<>(t2, 6d));
}
@ParameterizedRedisTest
void testIntersectAndStore() {
RedisZSet<T> interSet1 = createZSetFor("test:zset:inter1");
RedisZSet<T> interSet2 = createZSetFor("test:zset:inter");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
interSet1.add(t2, 2);
interSet1.add(t4, 3);
interSet2.add(t2, 2);
interSet2.add(t3, 3);
String resultName = "test:zset:inter:result:1";
RedisZSet<T> inter = zSet.intersectAndStore(Arrays.asList(interSet1, interSet2), resultName);
assertThat(inter).hasSize(1);
assertThat(inter).contains(t2);
assertThat(inter.score(t2)).isEqualTo(Double.valueOf(6));
assertThat(inter.getKey()).isEqualTo(resultName);
}
@ParameterizedRedisTest // GH-2042
@EnabledOnCommand("ZUNION")
void testUnion() {
RedisZSet<T> set1 = createZSetFor("test:zset:union1");
RedisZSet<T> set2 = createZSetFor("test:zset:union2");
T t1 = getT();
T t2 = getT();
T t3 = getT();
T t4 = getT();
zSet.add(t1, 1);
zSet.add(t2, 2);
zSet.add(t3, 3);
set1.add(t2, 2);
set1.add(t4, 3);
set2.add(t2, 2);
set2.add(t3, 3);
assertThat(zSet.union(Arrays.asList(set1, set2))).contains(t1, t2, t3, t4);
}
@SuppressWarnings("unchecked")
@ParameterizedRedisTest
void testUnionAndStore() {