Skip to content

Commit

Permalink
implement zremrangebyscore (#102)
Browse files Browse the repository at this point in the history
* implement zremrangebyscore

* fix tests and implementation

Co-authored-by: Eugene Tolbakov <eugene.tolbakov@dataart.com>
Co-authored-by: Ivan Ponomarev <iponomarev@mail.ru>
  • Loading branch information
3 people authored May 3, 2021
1 parent dcf5cff commit 567b9c9
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class OperationFactory {
TRANSACTIONAL_OPERATIONS.put("zrange", RO_zrange::new);
TRANSACTIONAL_OPERATIONS.put("zrangebylex", RO_zrangebylex::new);
TRANSACTIONAL_OPERATIONS.put("zrem", RO_zrem::new);
TRANSACTIONAL_OPERATIONS.put("zremrangebyscore", RO_zremrangebyscore::new);
TRANSACTIONAL_OPERATIONS.put("rename", RO_rename::new);
TRANSACTIONAL_OPERATIONS.put("zscore", RO_zscore::new);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package com.github.fppt.jedismock.operations;

import com.github.fppt.jedismock.exception.WrongValueTypeException;
import com.github.fppt.jedismock.server.Response;
import com.github.fppt.jedismock.server.Slice;
import com.github.fppt.jedismock.storage.RedisBase;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.github.fppt.jedismock.Utils.convertToDouble;
import static com.github.fppt.jedismock.Utils.serializeObject;


public class RO_zremrangebyscore extends AbstractRedisOperation {

private static final String LOWEST_POSSIBLE_SCORE = "-inf";
private static final String HIGHEST_POSSIBLE_SCORE = "+inf";
private static final String EXCLUSIVE_PREFIX = "(";

RO_zremrangebyscore(RedisBase base, List<Slice> params) {
super(base, params);
}

@Override
Slice response() {
final Slice key = params().get(0);
final LinkedHashMap<Slice, Double> map = getDataFromBase(key, new LinkedHashMap<>());

if (map == null || map.isEmpty()) return Response.integer(0);

final String start = params().get(1).toString();
final double startScore = getStartScore(start);


final Predicate<Double> compareToStart = p -> start.startsWith(EXCLUSIVE_PREFIX)
? p.compareTo(startScore) > 0
: p.compareTo(startScore) >= 0;

final String end = params().get(2).toString();
final double endScore = getEndScore(end);

final Predicate<Double> compareToEnd = p -> (end.startsWith(EXCLUSIVE_PREFIX)
? p.compareTo(endScore) < 0
: p.compareTo(endScore) <= 0);

List<Double> values = map.values().stream()
.filter(compareToStart.and(compareToEnd))
.collect(Collectors.toList());

final Map<Slice, Double> result = map.entrySet().stream()
.filter(entry -> compareToStart.and(compareToEnd).negate().test(entry.getValue()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(u, v) -> {
//duplicate key
throw new IllegalStateException();
}, LinkedHashMap::new));

try {
base().putValue(key, serializeObject(result));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}

return Response.integer(values.size());
}

private double getStartScore(String start) {
if (LOWEST_POSSIBLE_SCORE.equalsIgnoreCase(start)) {
return Double.MIN_VALUE;
} else if (start.startsWith(EXCLUSIVE_PREFIX)) {
return convertToDouble(start.substring(1));
} else if (Character.isDigit(start.charAt(0))) {
return convertToDouble(start);
} else {
throw new WrongValueTypeException("Valid start must be a number or start with '" + EXCLUSIVE_PREFIX + "' or be equal to '"
+ LOWEST_POSSIBLE_SCORE + "'");
}
}

private double getEndScore(String end) {
if (HIGHEST_POSSIBLE_SCORE.equalsIgnoreCase(end)) {
return Double.MAX_VALUE;
} else if (end.startsWith(EXCLUSIVE_PREFIX)) {
return convertToDouble(end.substring(1));
} else if (Character.isDigit(end.charAt(0))) {
return convertToDouble(end);
} else {
throw new WrongValueTypeException("Valid end must be a number or start with '" + EXCLUSIVE_PREFIX + "' or be equal to '"
+ HIGHEST_POSSIBLE_SCORE + "'");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.github.fppt.jedismock.comparisontests;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisDataException;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import static org.junit.jupiter.api.Assertions.*;

@ExtendWith(ComparisonBase.class)
public class TestZRemRangeByScore {

private static final String ZSET_KEY = "myzset";

@BeforeEach
public void clearKey(Jedis jedis){
jedis.del(ZSET_KEY);
}

@TestTemplate
public void whenUsingZremrangeByScore_EnsureItReturnsZeroForNonDefinedKey(Jedis jedis) {
assertEquals(0, jedis.zremrangeByScore(ZSET_KEY, "-inf", "+inf"));
}

@TestTemplate
public void whenUsingZremrangeByScore_EnsureItClearsEverythingWithPlusMinusInfinity(Jedis jedis) {
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 1, "two");
jedis.zadd(ZSET_KEY, 1, "three");

assertEquals(3, jedis.zremrangeByScore(ZSET_KEY, "-inf", "+inf"));
assertEquals(0, jedis.zrange(ZSET_KEY, 0, -1).size());
}


@TestTemplate
public void whenUsingZremrangeByScore_EnsureItReturnsSetSizeWhenLowestAndHighestScoresSpecified(Jedis jedis) {
// given
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 2, "two");
jedis.zadd(ZSET_KEY, 3, "three");
assertEquals(3, jedis.zrange(ZSET_KEY, 0, -1).size());

// when
final Long zremrangeByScoreResult = jedis.zremrangeByScore(ZSET_KEY, "-inf", "+inf");

// then
assertEquals(3, zremrangeByScoreResult);
assertEquals(0, jedis.zrange(ZSET_KEY, 0, -1).size());
}

@TestTemplate
public void whenUsingZremrangeByScore_EnsureItRemovesValueWhenIntervalSpecified(Jedis jedis) {
// given
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 2, "two");
jedis.zadd(ZSET_KEY, 3, "three");
assertEquals(3, jedis.zrange(ZSET_KEY, 0, -1).size());

// when
final Long zremrangeByScoreResult = jedis.zremrangeByScore(ZSET_KEY, "-inf", "2");

// then
assertEquals(2, zremrangeByScoreResult);
final Set<String> zrangeResult = jedis.zrange(ZSET_KEY, 0, -1);
assertEquals(1, zrangeResult.size());
assertArrayEquals(zrangeResult.toArray(), new String[]{"three"});
}

@TestTemplate
public void whenUsingZremrangeByScore_EnsureItDoesNotRemoveValueWhenExclusiveIntervalSpecified(Jedis jedis) {
// given
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 2, "two");
jedis.zadd(ZSET_KEY, 3, "three");
assertEquals(3, jedis.zrange(ZSET_KEY, 0, -1).size());

// when
final Long zremrangeByScoreResult = jedis.zremrangeByScore(ZSET_KEY, "-inf", "(2");

// then
assertEquals(1, zremrangeByScoreResult);
final Set<String> zrangeResult = jedis.zrange(ZSET_KEY, 0, -1);
assertEquals(2, zrangeResult.size());
assertEquals(new HashSet(Arrays.asList("three", "two")), zrangeResult);
}

@TestTemplate
public void whenUsingZremrangeByScore_EnsureItRemovesValuesAccordingToSpecifiedInterval(Jedis jedis) {
// given
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 2, "two");
jedis.zadd(ZSET_KEY, 3, "three");
jedis.zadd(ZSET_KEY, 4, "four");
jedis.zadd(ZSET_KEY, 5, "five");
jedis.zadd(ZSET_KEY, 6, "six");
jedis.zadd(ZSET_KEY, 7, "seven");
jedis.zadd(ZSET_KEY, 8, "eight");
jedis.zadd(ZSET_KEY, 9, "nine");
jedis.zadd(ZSET_KEY, 10, "ten");
assertEquals(10, jedis.zrange(ZSET_KEY, 0, -1).size());

// when
final Long zremrangeByScoreResult = jedis.zremrangeByScore(ZSET_KEY, 5, 8);

// then
assertEquals(4, zremrangeByScoreResult);
final Set<String> zrangeResult = jedis.zrange(ZSET_KEY, 0, -1);
assertEquals(6, zrangeResult.size());
assertEquals(new HashSet(Arrays.asList("one", "two", "three", "four", "nine", "ten")), zrangeResult);
}


@TestTemplate
public void whenUsingZremrangeByScore_EnsureItThrowsExceptionsWhenStartAndEndHaveWrongFormat(Jedis jedis) {
// given
jedis.zadd(ZSET_KEY, 1, "one");
jedis.zadd(ZSET_KEY, 2, "two");
jedis.zadd(ZSET_KEY, 3, "three");

// then
assertThrows(JedisDataException.class,
() -> jedis.zremrangeByScore(ZSET_KEY, "(dd", "(sd"));
assertThrows(JedisDataException.class,
() -> jedis.zremrangeByScore(ZSET_KEY, "1.e", "2.d"));
assertThrows(RuntimeException.class,
() -> jedis.zremrangeByScore(ZSET_KEY, "FOO", "BAR"));
}
}

0 comments on commit 567b9c9

Please sign in to comment.