Skip to content

Commit

Permalink
Merge branch 'main' into java/integ_yuryf_SETRANGE
Browse files Browse the repository at this point in the history
  • Loading branch information
Yury-Fridlyand authored Apr 9, 2024
2 parents a7e9c2c + 78cfa33 commit 7906e36
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 8 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Python: Added JSON.DEL JSON.FORGET commands ([#1146](https://github.com/aws/glide-for-redis/pull/1146))
* Python: Added STRLEN command ([#1230](https://github.com/aws/glide-for-redis/pull/1230))
* Python: Added HKEYS command ([#1228](https://github.com/aws/glide-for-redis/pull/1228))
* Python: Added ZREMRANGEBYSCORE command ([#1151](https://github.com/aws/glide-for-redis/pull/1151))
* Node: Added SPOP, SPOPCOUNT commands. ([#1117](https://github.com/aws/glide-for-redis/pull/1117))

#### Fixes
* Python: Fix typing error "‘type’ object is not subscriptable" ([#1203](https://github.com/aws/glide-for-redis/pull/1203))
Expand Down Expand Up @@ -84,4 +86,4 @@

Preview release of **GLIDE for Redis** a Polyglot Redis client.

See the [README](README.md) for additional information.
See the [README](README.md) for additional information.
1 change: 1 addition & 0 deletions glide-core/src/protobuf/redis_request.proto
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ enum RequestType {
DBSize = 92;
Brpop = 93;
Hkeys = 94;
Spop = 95;
PfAdd = 96;
PfCount = 97;
PfMerge = 98;
Expand Down
3 changes: 3 additions & 0 deletions glide-core/src/request_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ pub enum RequestType {
DBSize = 92,
Brpop = 93,
Hkeys = 94,
Spop = 95,
PfAdd = 96,
PfCount = 97,
PfMerge = 98,
Expand Down Expand Up @@ -224,6 +225,7 @@ impl From<::protobuf::EnumOrUnknown<ProtobufRequestType>> for RequestType {
ProtobufRequestType::RPushX => RequestType::RPushX,
ProtobufRequestType::LPushX => RequestType::LPushX,
ProtobufRequestType::Blpop => RequestType::Blpop,
ProtobufRequestType::Spop => RequestType::Spop,
ProtobufRequestType::SetRange => RequestType::SetRange,
}
}
Expand Down Expand Up @@ -334,6 +336,7 @@ impl RequestType {
RequestType::RPushX => Some(cmd("RPUSHX")),
RequestType::LPushX => Some(cmd("LPUSHX")),
RequestType::Blpop => Some(cmd("BLPOP")),
RequestType::Spop => Some(cmd("SPOP")),
RequestType::SetRange => Some(cmd("SETRANGE")),
}
}
Expand Down
25 changes: 25 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import {
createSAdd,
createSCard,
createSMembers,
createSPop,
createSRem,
createSet,
createSismember,
Expand Down Expand Up @@ -1234,6 +1235,30 @@ export class BaseClient {
return this.createWritePromise(createSismember(key, member));
}

/** Removes and returns one random member from the set value store at `key`.
* See https://redis.io/commands/spop/ for details.
* To pop multiple members, see `spopCount`.
*
* @param key - The key of the set.
* @returns the value of the popped member.
* If `key` does not exist, null will be returned.
*/
public spop(key: string): Promise<string | null> {
return this.createWritePromise(createSPop(key));
}

/** Removes and returns up to `count` random members from the set value store at `key`, depending on the set's length.
* See https://redis.io/commands/spop/ for details.
*
* @param key - The key of the set.
* @param count - The count of the elements to pop from the set.
* @returns A list of popped elements will be returned depending on the set's length.
* If `key` does not exist, empty list will be returned.
*/
public spopCount(key: string, count: number): Promise<string[]> {
return this.createWritePromise(createSPop(key, count));
}

/** Returns the number of keys in `keys` that exist in the database.
* See https://redis.io/commands/exists/ for details.
*
Expand Down
8 changes: 8 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,14 @@ export function createSismember(
return createCommand(RequestType.SIsMember, [key, member]);
}

/**
* @internal
*/
export function createSPop(key: string, count?: number): redis_request.Command {
const args: string[] = count == undefined ? [key] : [key, count.toString()];
return createCommand(RequestType.Spop, args);
}

/**
* @internal
*/
Expand Down
27 changes: 27 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
createSAdd,
createSCard,
createSMembers,
createSPop,
createSRem,
createSelect,
createSet,
Expand Down Expand Up @@ -677,6 +678,32 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createSismember(key, member));
}

/** Removes and returns one random member from the set value store at `key`.
* See https://redis.io/commands/spop/ for details.
* To pop multiple members, see `spopCount`.
*
* @param key - The key of the set.
*
* Command Response - the value of the popped member.
* If `key` does not exist, null will be returned.
*/
public spop(key: string): T {
return this.addAndReturn(createSPop(key));
}

/** Removes and returns up to `count` random members from the set value store at `key`, depending on the set's length.
* See https://redis.io/commands/spop/ for details.
*
* @param key - The key of the set.
* @param count - The count of the elements to pop from the set.
*
* Command Response - A list of popped elements will be returned depending on the set's length.
* If `key` does not exist, empty list will be returned.
*/
public spopCount(key: string, count: number): T {
return this.addAndReturn(createSPop(key, count));
}

/** Returns the number of keys in `keys` that exist in the database.
* See https://redis.io/commands/exists/ for details.
*
Expand Down
23 changes: 23 additions & 0 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1121,6 +1121,29 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`spop and spopCount test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key = uuidv4();
const members = ["member1", "member2", "member3"];
expect(await client.sadd(key, members)).toEqual(3);

const result1 = await client.spop(key);
expect(members).toContain(result1);

const result2 = await client.spopCount(key, 2);
expect(members).toContain(result2?.[0]);
expect(members).toContain(result2?.[1]);
expect(result2).not.toContain(result1);

expect(await client.spop("nonExistingKey")).toEqual(null);
expect(await client.spopCount("nonExistingKey", 1)).toEqual([]);
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`exists with existing keys, an non existing key_%p`,
async (protocol) => {
Expand Down
4 changes: 4 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ export async function transactionTest(
args.push(true);
baseTransaction.smembers(key7);
args.push(["bar"]);
baseTransaction.spop(key7);
args.push("bar");
baseTransaction.scard(key7);
args.push(0);
baseTransaction.zadd(key8, {
member1: 1,
member2: 2,
Expand Down
48 changes: 48 additions & 0 deletions python/python/glide/async_commands/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1802,6 +1802,54 @@ async def zrem(
await self._execute_command(RequestType.Zrem, [key] + members),
)

async def zremrangebyscore(
self,
key: str,
min_score: Union[InfBound, ScoreBoundary],
max_score: Union[InfBound, ScoreBoundary],
) -> int:
"""
Removes all elements in the sorted set stored at `key` with a score between `min_score` and `max_score`.
See https://redis.io/commands/zremrangebyscore/ for more details.
Args:
key (str): The key of the sorted set.
min_score (Union[InfBound, ScoreBoundary]): The minimum score to remove from.
Can be an instance of InfBound representing positive/negative infinity,
or ScoreBoundary representing a specific score and inclusivity.
max_score (Union[InfBound, ScoreBoundary]): The maximum score to remove up to.
Can be an instance of InfBound representing positive/negative infinity,
or ScoreBoundary representing a specific score and inclusivity.
Returns:
int: The number of members that were removed from the sorted set.
If `key` does not exist, it is treated as an empty sorted set, and the command returns 0.
If `min_score` is greater than `max_score`, 0 is returned.
Examples:
>>> await client.zremrangebyscore("my_sorted_set", ScoreBoundary(5.0 , is_inclusive=true) , InfBound.POS_INF)
2 # Indicates that 2 members with scores between 5.0 (not exclusive) and +inf have been removed from the sorted set "my_sorted_set".
>>> await client.zremrangebyscore("non_existing_sorted_set", ScoreBoundary(5.0 , is_inclusive=true) , ScoreBoundary(10.0 , is_inclusive=false))
0 # Indicates that no members were removed as the sorted set "non_existing_sorted_set" does not exist.
"""
score_min = (
min_score.value["score_arg"]
if type(min_score) == InfBound
else min_score.value
)
score_max = (
max_score.value["score_arg"]
if type(max_score) == InfBound
else max_score.value
)

return cast(
int,
await self._execute_command(
RequestType.ZRemRangeByScore, [key, score_min, score_max]
),
)

async def zscore(self, key: str, member: str) -> Optional[float]:
"""
Returns the score of `member` in the sorted set stored at `key`.
Expand Down
39 changes: 39 additions & 0 deletions python/python/glide/async_commands/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -1389,6 +1389,45 @@ def zrem(
"""
return self.append_command(RequestType.Zrem, [key] + members)

def zremrangebyscore(
self: TTransaction,
key: str,
min_score: Union[InfBound, ScoreBoundary],
max_score: Union[InfBound, ScoreBoundary],
) -> TTransaction:
"""
Removes all elements in the sorted set stored at `key` with a score between `min_score` and `max_score`.
See https://redis.io/commands/zremrangebyscore/ for more details.
Args:
key (str): The key of the sorted set.
min_score (Union[InfBound, ScoreBoundary]): The minimum score to remove from.
Can be an instance of InfBound representing positive/negative infinity,
or ScoreBoundary representing a specific score and inclusivity.
max_score (Union[InfBound, ScoreBoundary]): The maximum score to remove up to.
Can be an instance of InfBound representing positive/negative infinity,
or ScoreBoundary representing a specific score and inclusivity.
Commands response:
int: The number of members that were removed from the sorted set.
If `key` does not exist, it is treated as an empty sorted set, and the command returns 0.
If `min_score` is greater than `max_score`, 0 is returned.
"""
score_min = (
min_score.value["score_arg"]
if type(min_score) == InfBound
else min_score.value
)
score_max = (
max_score.value["score_arg"]
if type(max_score) == InfBound
else max_score.value
)
return self.append_command(
RequestType.ZRemRangeByScore, [key, score_min, score_max]
)

def zscore(self: TTransaction, key: str, member: str) -> TTransaction:
"""
Returns the score of `member` in the sorted set stored at `key`.
Expand Down
28 changes: 28 additions & 0 deletions python/python/tests/test_async_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,34 @@ async def test_zrem(self, redis_client: TRedisClient):

assert await redis_client.zrem("non_existing_set", ["member"]) == 0

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_zremrangebyscore(self, redis_client: TRedisClient):
key = get_random_string(10)
members_scores = {"one": 1, "two": 2, "three": 3}
assert await redis_client.zadd(key, members_scores) == 3

assert (
await redis_client.zremrangebyscore(
key, ScoreBoundary(1, False), ScoreBoundary(2)
)
== 1
)
assert (
await redis_client.zremrangebyscore(key, ScoreBoundary(1), InfBound.NEG_INF)
== 0
)
assert (
await redis_client.zremrangebyscore(
"non_existing_set", InfBound.NEG_INF, InfBound.POS_INF
)
== 0
)

assert await redis_client.set(key, "value") == OK
with pytest.raises(RequestError):
await redis_client.zremrangebyscore(key, InfBound.NEG_INF, InfBound.POS_INF)

@pytest.mark.parametrize("cluster_mode", [True, False])
@pytest.mark.parametrize("protocol", [ProtocolVersion.RESP2, ProtocolVersion.RESP3])
async def test_zcard(self, redis_client: TRedisClient):
Expand Down
16 changes: 9 additions & 7 deletions python/python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ async def transaction_test(
transaction.sismember(key7, "bar")
args.append(True)

transaction.zadd(key8, {"one": 1, "two": 2, "three": 3})
args.append(3)
transaction.zadd(key8, {"one": 1, "two": 2, "three": 3, "four": 4})
args.append(4)
transaction.zrank(key8, "one")
args.append(0)
if not await check_if_server_version_lt(redis_client, "7.2.0"):
Expand All @@ -166,19 +166,21 @@ async def transaction_test(
transaction.zrem(key8, ["one"])
args.append(1)
transaction.zcard(key8)
args.append(2)
args.append(3)
transaction.zcount(key8, ScoreBoundary(2, is_inclusive=True), InfBound.POS_INF)
args.append(2)
args.append(3)
transaction.zscore(key8, "two")
args.append(2.0)
transaction.zrange(key8, RangeByIndex(start=0, stop=-1))
args.append(["two", "three"])
args.append(["two", "three", "four"])
transaction.zrange_withscores(key8, RangeByIndex(start=0, stop=-1))
args.append({"two": 2, "three": 3})
args.append({"two": 2, "three": 3, "four": 4})
transaction.zpopmin(key8)
args.append({"two": 2.0})
transaction.zpopmax(key8)
args.append({"three": 3})
args.append({"four": 4})
transaction.zremrangebyscore(key8, InfBound.NEG_INF, InfBound.POS_INF)
args.append(1)
return args


Expand Down

0 comments on commit 7906e36

Please sign in to comment.