From c547a4254714b2bc2711c9ff5794ceee1423169b Mon Sep 17 00:00:00 2001 From: Adan Date: Fri, 16 Feb 2024 11:19:13 +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 | 19 +++++++++++ node/src/Commands.ts | 37 +++++++++++++++++++++ node/src/Transaction.ts | 22 +++++++++++- node/tests/SharedTests.ts | 36 ++++++++++++++++++++ node/tests/TestUtilities.ts | 2 ++ 7 files changed, 117 insertions(+), 1 deletion(-) diff --git a/glide-core/src/protobuf/redis_request.proto b/glide-core/src/protobuf/redis_request.proto index 244aefbb86..9bd594f40e 100644 --- a/glide-core/src/protobuf/redis_request.proto +++ b/glide-core/src/protobuf/redis_request.proto @@ -105,6 +105,7 @@ enum RequestType { ZScore = 67; Type = 68; HLen = 69; + ZRemRangeByScore = 70; } message Command { diff --git a/glide-core/src/socket_listener.rs b/glide-core/src/socket_listener.rs index 481a07ec80..3e7dfaa3cb 100644 --- a/glide-core/src/socket_listener.rs +++ b/glide-core/src/socket_listener.rs @@ -348,6 +348,7 @@ fn get_command(request: &Command) -> Option { RequestType::ZScore => Some(cmd("ZSCORE")), RequestType::Type => Some(cmd("TYPE")), RequestType::HLen => Some(cmd("HLEN")), + RequestType::ZRemRangeByScore => Some(cmd("ZREMRANGEBYSCORE")), } } diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 230643ebdb..ad7e7d2d62 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -56,6 +56,7 @@ import { createZcard, createZcount, createZrem, + createZremRangeByScore, createZscore, } from "./Commands"; import { @@ -1065,6 +1066,24 @@ export class BaseClient { return this.createWritePromise(createZcount(key, minScore, maxScore)); } + /** Removes all elements in the sorted set stored at `key` with a score between `minScore` and `maxScore` (inclusive). + * 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 d8ab96f9c4..4fe434ecab 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -816,3 +816,40 @@ export function createZcount( return createCommand(RequestType.Zcount, args); } + +/** + * @internal + */ +export function createZremRangeByScore( + key: string, + minScore: ScoreLimit, + 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); + } + + return createCommand(RequestType.ZRemRangeByScore, args); +} diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index c2c9104cbb..57e62d0689 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -59,7 +59,8 @@ import { createZcard, createZcount, createZrem, - createZscore, + createZremRangeByScore, + createZscore } from "./Commands"; import { redis_request } from "./ProtobufMessage"; @@ -828,6 +829,25 @@ export class BaseTransaction> { return this.addAndReturn(createZcount(key, minScore, maxScore)); } + /** Removes all elements in the sorted set stored at `key` with a score between `minScore` and `maxScore` (inclusive). + * 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 3aa60e9101..da4bd7d4c5 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1469,6 +1469,42 @@ export function runBaseTests(config: { }, 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 + ); } export function runCommonTests(config: { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index 82fa33976f..089922e1af 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -135,6 +135,8 @@ export function transactionTest( args.push(3.0); baseTransaction.zcount(key8, { bound: 2 }, "positiveInfinity"); args.push(1); + baseTransaction.zremRangeByScore(key8, "negativeInfinity", "positiveInfinity"); + args.push(1); return args; }