From 250e20322a8d238da9a91f7e6b7e51675c89f56d Mon Sep 17 00:00:00 2001 From: Adan Date: Thu, 7 Mar 2024 11:41:10 +0000 Subject: [PATCH] Added zremRangeByScore command in Node. --- glide-core/src/protobuf/redis_request.proto | 1 + glide-core/src/socket_listener.rs | 1 + node/src/BaseClient.ts | 29 ++++++++++- node/src/Commands.ts | 57 +++++++++++---------- node/src/Transaction.ts | 26 +++++++++- node/tests/SharedTests.ts | 40 ++++++++++++++- node/tests/TestUtilities.ts | 24 ++++++--- 7 files changed, 140 insertions(+), 38 deletions(-) diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 877c773b8d..98dab34055 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -128,6 +128,7 @@ enum RequestType { Hvals = 84; PTTL = 85; ZRemRangeByRank = 86; + ZRemRangeByScore = 87; } message Command { diff --git a/glide-core/src/socket_listener.rs b/glide-core/src/socket_listener.rs index 89a289acc2..95c71916b7 100644 --- a/glide-core/src/socket_listener.rs +++ b/glide-core/src/socket_listener.rs @@ -358,6 +358,7 @@ fn get_command(request: &Command) -> Option { RequestType::Hvals => Some(cmd("HVALS")), RequestType::PTTL => Some(cmd("PTTL")), RequestType::ZRemRangeByRank => Some(cmd("ZREMRANGEBYRANK")), + RequestType::ZRemRangeByScore => Some(cmd("ZREMRANGEBYSCORE")), } } diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 198fc408e0..e0bbb49547 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -66,6 +66,7 @@ import { createZpopmin, createZrem, createZremRangeByRank, + createZremRangeByScore, createZscore, } from "./Commands"; import { @@ -1176,7 +1177,7 @@ export class BaseClient { * Both `start` and `end` are zero-based indexes with 0 being the element with the lowest score. * These indexes can be negative numbers, where they indicate offsets starting at the element with the highest score. * See https://redis.io/commands/zremrangebyrank/ for more details. - * + * * @param key - The key of the sorted set. * @param start - The starting point of the range. * @param end - The end of the range. @@ -1185,10 +1186,34 @@ export class BaseClient { * If `end` exceeds the actual end of the sorted set, the range will stop at the actual end of the sorted set. * If `key` does not exist 0 will be returned. */ - public zremRangeByRank(key: string, start: number, end: number): Promise { + public zremRangeByRank( + key: string, + start: number, + end: number, + ): Promise { return this.createWritePromise(createZremRangeByRank(key, start, end)); } + /** Removes all elements in the sorted set stored at `key` with a score between `minScore` and `maxScore`. + * See https://redis.io/commands/zremrangebyscore/ for more details. + * + * @param key - The key of the sorted set. + * @param minScore - The minimum score to remove from. Can be positive/negative infinity, or specific score and inclusivity. + * @param maxScore - The maximum score to remove to. Can be positive/negative infinity, or specific score and inclusivity. + * @returns the number of members removed. + * If `key` does not exist, it is treated as an empty sorted set, and the command returns 0. + * If `minScore` is greater than `maxScore`, 0 is returned. + */ + public zremRangeByScore( + key: string, + minScore: ScoreLimit, + maxScore: ScoreLimit, + ): Promise { + return this.createWritePromise( + createZremRangeByScore(key, minScore, maxScore), + ); + } + private readonly MAP_READ_FROM_STRATEGY: Record< ReadFrom, connection_request.ReadFrom diff --git a/node/src/Commands.ts b/node/src/Commands.ts index c7edf68aec..2a81c10651 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -794,6 +794,20 @@ const positiveInfinityArg = "+inf"; const negativeInfinityArg = "-inf"; const isInclusiveArg = "("; +function getScoreLimitArg(score: ScoreLimit): string { + if (score == "positiveInfinity") { + return positiveInfinityArg; + } else if (score == "negativeInfinity") { + return negativeInfinityArg; + } + + const value = + score.isInclusive == false + ? isInclusiveArg + score.bound.toString() + : score.bound.toString(); + return value; +} + /** * @internal */ @@ -803,31 +817,8 @@ export function createZcount( maxScore: ScoreLimit, ): redis_request.Command { const args = [key]; - - if (minScore == "positiveInfinity") { - args.push(positiveInfinityArg); - } else if (minScore == "negativeInfinity") { - args.push(negativeInfinityArg); - } else { - const value = - minScore.isInclusive == false - ? isInclusiveArg + minScore.bound.toString() - : minScore.bound.toString(); - args.push(value); - } - - if (maxScore == "positiveInfinity") { - args.push(positiveInfinityArg); - } else if (maxScore == "negativeInfinity") { - args.push(negativeInfinityArg); - } else { - const value = - maxScore.isInclusive == false - ? isInclusiveArg + maxScore.bound.toString() - : maxScore.bound.toString(); - args.push(value); - } - + args.push(getScoreLimitArg(minScore)); + args.push(getScoreLimitArg(maxScore)); return createCommand(RequestType.Zcount, args); } @@ -897,7 +888,7 @@ export function createPttl(key: string): redis_request.Command { export function createZremRangeByRank( key: string, start: number, - stop: number + stop: number, ): redis_request.Command { return createCommand(RequestType.ZRemRangeByRank, [ key, @@ -905,3 +896,17 @@ export function createZremRangeByRank( stop.toString(), ]); } + +/** + * @internal + */ +export function createZremRangeByScore( + key: string, + minScore: ScoreLimit, + maxScore: ScoreLimit, +): redis_request.Command { + const args = [key]; + args.push(getScoreLimitArg(minScore)); + args.push(getScoreLimitArg(maxScore)); + return createCommand(RequestType.ZRemRangeByScore, args); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index b6bc409e89..12cb191045 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -69,6 +69,7 @@ import { createZpopmin, createZrem, createZremRangeByRank, + createZremRangeByScore, createZscore, } from "./Commands"; import { redis_request } from "./ProtobufMessage"; @@ -941,11 +942,11 @@ export class BaseTransaction> { * Both `start` and `end` are zero-based indexes with 0 being the element with the lowest score. * These indexes can be negative numbers, where they indicate offsets starting at the element with the highest score. * See https://redis.io/commands/zremrangebyrank/ for more details. - * + * * @param key - The key of the sorted set. * @param start - The starting point of the range. * @param end - The end of the range. - * + * * Command Response - The number of members removed. * If `start` exceeds the end of the sorted set, or if `start` is greater than `end`, 0 returned. * If `end` exceeds the actual end of the sorted set, the range will stop at the actual end of the sorted set. @@ -955,6 +956,27 @@ export class BaseTransaction> { return this.addAndReturn(createZremRangeByRank(key, start, end)); } + /** Removes all elements in the sorted set stored at `key` with a score between `minScore` and `maxScore`. + * See https://redis.io/commands/zremrangebyscore/ for more details. + * + * @param key - The key of the sorted set. + * @param minScore - The minimum score to remove from. Can be positive/negative infinity, or specific score and inclusivity. + * @param maxScore - The maximum score to remove to. Can be positive/negative infinity, or specific score and inclusivity. + * + * Command Response - the number of members removed. + * If `key` does not exist, it is treated as an empty sorted set, and the command returns 0. + * If `minScore` is greater than `maxScore`, 0 is returned. + */ + public zremRangeByScore( + key: string, + minScore: ScoreLimit, + maxScore: ScoreLimit, + ): T { + return this.addAndReturn( + createZremRangeByScore(key, minScore, maxScore), + ); + } + /** Executes a single command, without checking inputs. Every part of the command, including subcommands, * should be added as a separate value in args. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index d1241ec438..67cf76a9a3 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1705,11 +1705,47 @@ export function runBaseTests(config: { expect(await client.zremRangeByRank(key, 0, 1)).toEqual(2); expect(await client.zremRangeByRank(key, 0, 10)).toEqual(1); expect( - await client.zremRangeByRank("nonExistingKey", 0, -1) + await client.zremRangeByRank("nonExistingKey", 0, -1), ).toEqual(0); }, protocol); }, - config.timeout + config.timeout, + ); + + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `zremRangeByScore test_%p`, + async (protocol) => { + await runTest(async (client: BaseClient) => { + const key = uuidv4(); + const membersScores = { one: 1, two: 2, three: 3 }; + expect(await client.zadd(key, membersScores)).toEqual(3); + + expect( + await client.zremRangeByScore( + key, + { bound: 1, isInclusive: false }, + { bound: 2 }, + ), + ).toEqual(1); + + expect( + await client.zremRangeByScore( + key, + { bound: 1 }, + "negativeInfinity", + ), + ).toEqual(0); + + expect( + await client.zremRangeByScore( + "nonExistingKey", + "negativeInfinity", + "positiveInfinity", + ), + ).toEqual(0); + }, protocol); + }, + config.timeout, ); } diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index 7614cb26ca..64f1769db8 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -135,23 +135,35 @@ export function transactionTest( args.push(1); baseTransaction.smembers(key7); args.push(["bar"]); - baseTransaction.zadd(key8, { member1: 1, member2: 2, member3: 3.5, member4: 4 }); - args.push(4); + baseTransaction.zadd(key8, { + member1: 1, + member2: 2, + member3: 3.5, + member4: 4, + member5: 5, + }); + args.push(5); baseTransaction.zaddIncr(key8, "member2", 1); args.push(3); baseTransaction.zrem(key8, ["member1"]); args.push(1); baseTransaction.zcard(key8); - args.push(3); + args.push(4); baseTransaction.zscore(key8, "member2"); args.push(3.0); baseTransaction.zcount(key8, { bound: 2 }, "positiveInfinity"); - args.push(3); + args.push(4); baseTransaction.zpopmin(key8); args.push({ member2: 3.0 }); baseTransaction.zpopmax(key8); - args.push({ member4: 4 }); - baseTransaction.zremRangeByRank(key8, 0, 1); + args.push({ member5: 5 }); + baseTransaction.zremRangeByRank(key8, 1, 1); + args.push(1); + baseTransaction.zremRangeByScore( + key8, + "negativeInfinity", + "positiveInfinity", + ); args.push(1); return args; }