DATAREDIS-746 - Add Zset intersect operation aggregation support.

We now support aggregation and weight options for ZINTERSTORE. We also encapsulate weights used for ZUNIONSTORE and ZINTERSTORE within a Weights value object.

zSetOps.intersectAndStore(set1, Arrays.asList(set2, set3), out, Aggregate.MAX,
				Weights.of(1, 2, 1));

Related ticket: DATAREDIS-515.

Original Pull Request: #314
This commit is contained in:
Mark Paluch
2018-02-16 09:45:07 +01:00
committed by Christoph Strobl
parent 7d6524bace
commit 56a01625d5
18 changed files with 802 additions and 54 deletions

View File

@@ -844,6 +844,10 @@ public class RedisConnectionUnitTests {
return delegate.zUnionStore(destKey, aggregate, weights, sets);
}
public Long zUnionStore(byte[] destKey, Aggregate aggregate, Weights weights, byte[]... sets) {
return delegate.zUnionStore(destKey, aggregate, weights, sets);
}
public Long zInterStore(byte[] destKey, byte[]... sets) {
return delegate.zInterStore(destKey, sets);
}
@@ -852,6 +856,10 @@ public class RedisConnectionUnitTests {
return delegate.zInterStore(destKey, aggregate, weights, sets);
}
public Long zInterStore(byte[] destKey, Aggregate aggregate, Weights weights, byte[]... sets) {
return delegate.zInterStore(destKey, aggregate, weights, sets);
}
public Cursor<Tuple> zScan(byte[] key, ScanOptions options) {
return delegate.zScan(key, options);
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2018 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.data.redis.connection;
import static org.assertj.core.api.Assertions.*;
import org.junit.Test;
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
/**
* Unit tests for {@link org.springframework.data.redis.connection.RedisZSetCommands.Weights}.
*
* @author Mark Paluch
*/
public class WeightsUnitTests {
@Test // DATAREDIS-746
public void shouldCreateWeights() {
assertThat(Weights.of(1, 2, 3).toArray()).contains(1, 2, 3);
assertThat(Weights.of(1, 2d, 3).toArray()).contains(1d, 2d, 3d);
}
@Test // DATAREDIS-746
public void shouldRejectCreationWithNull() {
assertThatThrownBy(() -> Weights.of((int[]) null)).isInstanceOf(IllegalArgumentException.class);
assertThatThrownBy(() -> Weights.of((double[]) null)).isInstanceOf(IllegalArgumentException.class);
}
@Test // DATAREDIS-746
public void shouldCreateEqualWeights() {
Weights weights = Weights.fromSetCount(3);
assertThat(weights.getWeight(0)).isOne();
assertThat(weights.getWeight(1)).isOne();
assertThat(weights.getWeight(2)).isOne();
}
@Test // DATAREDIS-746
public void getShouldThrowIndexOutOfBoundsException() {
assertThatThrownBy(() -> Weights.fromSetCount(1).getWeight(1)).isInstanceOf(IndexOutOfBoundsException.class);
assertThatThrownBy(() -> Weights.fromSetCount(1).getWeight(-1)).isInstanceOf(IndexOutOfBoundsException.class);
}
@Test // DATAREDIS-746
public void shouldMultiplyDouble() {
Weights weights = Weights.of(1, 2, 3).multiply(2.5);
assertThat(weights.getWeight(0)).isEqualTo(2.5);
assertThat(weights.getWeight(2)).isEqualTo(7.5);
}
@Test // DATAREDIS-746
public void shouldMultiplyInt() {
Weights weights = Weights.of(1, 2, 3).multiply(2);
assertThat(weights.getWeight(0)).isEqualTo(2);
assertThat(weights.getWeight(2)).isEqualTo(6);
}
}

View File

@@ -21,6 +21,7 @@ import reactor.test.StepVerifier;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.junit.AfterClass;
@@ -35,7 +36,9 @@ import org.springframework.data.redis.ConnectionFactoryTracker;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisZSetCommands.Aggregate;
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@@ -484,6 +487,33 @@ public class DefaultReactiveZSetOperationsIntegrationTests<K, V> {
StepVerifier.create(zSetOperations.range(destKey, new Range<>(0L, 100L))).expectNextCount(3).verifyComplete();
}
@Test // DATAREDIS-746
public void unionAndStoreWithAggregation() {
K key = keyFactory.instance();
K otherKey = keyFactory.instance();
K destKey = keyFactory.instance();
V onlyInKey = valueFactory.instance();
V shared = valueFactory.instance();
V onlyInOtherKey = valueFactory.instance();
StepVerifier.create(zSetOperations.add(key, onlyInKey, 10)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(key, shared, 11)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(otherKey, onlyInOtherKey, 10)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(otherKey, shared, 11)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM))
.expectNext(3L).verifyComplete();
StepVerifier.create(zSetOperations.score(destKey, shared)).expectNext(22d).verifyComplete();
StepVerifier.create(
zSetOperations.unionAndStore(key, Collections.singleton(otherKey), destKey, Aggregate.SUM, Weights.of(2, 1)))
.expectNext(3L).verifyComplete();
StepVerifier.create(zSetOperations.score(destKey, shared)).expectNext(33d).verifyComplete();
}
@Test // DATAREDIS-602
public void intersectAndStore() {
@@ -507,7 +537,39 @@ public class DefaultReactiveZSetOperationsIntegrationTests<K, V> {
StepVerifier.create(zSetOperations.range(destKey, new Range<>(0L, 5L))) //
.expectNextCount(1) //
.verifyComplete();
}
@Test // DATAREDIS-746
public void intersectAndStoreWithAggregation() {
K key = keyFactory.instance();
K otherKey = keyFactory.instance();
K destKey = keyFactory.instance();
V onlyInKey = valueFactory.instance();
V shared = valueFactory.instance();
V onlyInOtherKey = valueFactory.instance();
StepVerifier.create(zSetOperations.add(key, onlyInKey, 10)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(key, shared, 11)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(otherKey, onlyInOtherKey, 10)).expectNext(true).verifyComplete();
StepVerifier.create(zSetOperations.add(otherKey, shared, 11)).expectNext(true).verifyComplete();
StepVerifier
.create(zSetOperations.intersectAndStore(key, Collections.singletonList(otherKey), destKey, Aggregate.SUM))
.expectNext(1L).expectComplete().verify();
StepVerifier.create(zSetOperations.score(destKey, shared)) //
.expectNext(22d) //
.verifyComplete();
StepVerifier.create(zSetOperations.intersectAndStore(key, Collections.singletonList(otherKey), destKey,
Aggregate.SUM, Weights.of(1, 2))).expectNext(1L).expectComplete().verify();
StepVerifier.create(zSetOperations.score(destKey, shared)) //
.expectNext(33d) //
.verifyComplete();
}
@Test // DATAREDIS-602

View File

@@ -42,6 +42,7 @@ import org.springframework.data.redis.LongAsStringObjectFactory;
import org.springframework.data.redis.LongObjectFactory;
import org.springframework.data.redis.ObjectFactory;
import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.Weights;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.data.redis.test.util.MinimumRedisVersionRule;
import org.springframework.test.annotation.IfProfileValue;
@@ -394,9 +395,42 @@ public class DefaultZSetOperationsTests<K, V> {
zSetOps.add(key1, value1, 4.0);
zSetOps.add(key2, value1, 3.0);
int weight[] = { 1, 2 };
zSetOps.unionAndStore(key1, Collections.singletonList(key2), key1, RedisZSetCommands.Aggregate.MAX,
Weights.of(1, 2));
zSetOps.unionAndStore(key1, Collections.singletonList(key2), weight, key1, RedisZSetCommands.Aggregate.MAX);
assertThat(zSetOps.score(key1, value1), closeTo(6.0, 0.1));
}
@Test // DATAREDIS-746
public void testZsetIntersectWithAggregate() {
K key1 = keyFactory.instance();
K key2 = keyFactory.instance();
V value1 = valueFactory.instance();
V value2 = valueFactory.instance();
zSetOps.add(key1, value1, 1.0);
zSetOps.add(key1, value2, 2.0);
zSetOps.add(key2, value2, 3.0);
zSetOps.intersectAndStore(key1, Collections.singletonList(key2), key1, RedisZSetCommands.Aggregate.MIN);
assertThat(zSetOps.score(key1, value2), closeTo(2.0, 0.1));
}
@Test // DATAREDIS-746
public void testZsetIntersectWithAggregateWeights() {
K key1 = keyFactory.instance();
K key2 = keyFactory.instance();
V value1 = valueFactory.instance();
zSetOps.add(key1, value1, 4.0);
zSetOps.add(key2, value1, 3.0);
zSetOps.intersectAndStore(key1, Collections.singletonList(key2), key1, RedisZSetCommands.Aggregate.MAX,
Weights.of(1, 2));
assertThat(zSetOps.score(key1, value1), closeTo(6.0, 0.1));
}