From 53c1be5d8633e14d22b363229691d91b986cf12e Mon Sep 17 00:00:00 2001 From: Shoham Elias Date: Mon, 15 Jul 2024 11:05:45 +0000 Subject: [PATCH] Node: add SRANDMEMBER command Signed-off-by: Shoham Elias --- CHANGELOG.md | 1 + node/src/BaseClient.ts | 58 ++++++++++++++++++++++++++++++++++++- node/src/Commands.ts | 14 ++++++++- node/src/Transaction.ts | 27 ++++++++++++++++- node/tests/SharedTests.ts | 42 +++++++++++++++++++++++++++ node/tests/TestUtilities.ts | 6 ++++ 6 files changed, 145 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fe0fa89e8..364b71c1ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ * Node: Added SINTERSTORE command ([#1929](https://github.com/valkey-io/valkey-glide/pull/1929)) * Node: Added SUNION command ([#1919](https://github.com/valkey-io/valkey-glide/pull/1919)) * Node: Added SDIFF command ([#1924](https://github.com/valkey-io/valkey-glide/pull/1924)) +* Node: Added SRANDMEMBER command ([#1938](https://github.com/valkey-io/valkey-glide/pull/1938)) ## 1.0.0 (2024-07-09) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 07ce579286..2466b42ee9 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -12,6 +12,7 @@ import * as net from "net"; import { Buffer, BufferWriter, Reader, Writer } from "protobufjs"; import { AggregationType, + BulkString, ExpireOptions, InsertPosition, KeyWeight, @@ -80,7 +81,9 @@ import { createSMembers, createSMove, createSPop, + createSRandMember, createSRem, + createSUnion, createSUnionStore, createSet, createStrlen, @@ -105,7 +108,6 @@ import { createZRemRangeByRank, createZRemRangeByScore, createZScore, - createSUnion, } from "./Commands"; import { ClosingError, @@ -1491,6 +1493,60 @@ export class BaseClient { ); } + /** Returns a random element from the set value stored at `key`. + * See https://valkey.io/commands/srandmember for more details. + * + * @param key - The key from which to retrieve the set member. + * @returns a random element from the set, or null if `key` does not exist. + * + * @example + * ```typescript + * // Example usage of srandmember method to return a random member from a set + * const result = await client.srandmember("my_set"); + * console.log(result); // Output: 'member1' - A random member of "my_set". + * ``` + * + * @example + * ```typescript + * // Example usage of srandmember method with non-existing key + * const result = await client.srandmember("non_existing_set"); + * console.log(result); // Output: null + * ``` + */ + public srandmember(key: BulkString): Promise { + return this.createWritePromise(createSRandMember(key)); + } + + /** Returns one or more random elements from the set value stored at `key`. + * See https://valkey.io/commands/srandmember for more details. + * + * @param key - The key of the sorted set. + * @param count - The number of members to return. + * If `count` is positive, returns unique members. + * If `count` is negative, allows for duplicates members. + * @returns a list of members from the set. If the set does not exist or is empty, an empty list will be returned. + * + * @example + * ```typescript + * // Example usage of srandmemberCount method to return multiple random members from a set + * const result = await client.srandmemberCount("my_set", -3); + * console.log(result); // Output: ['member1', 'member1', 'member2'] - Random members of "my_set". + * ``` + * + * @example + * ```typescript + * // Example usage of srandmemberCount method with non-existing key + * const result = await client.srandmemberCount("non_existing_set", 3); + * console.log(result); // Output: [] - An empty list since the key does not exist. + * ``` + */ + public async srandmemberCount( + key: BulkString, + count: number, + ): Promise { + return this.createWritePromise(createSRandMember(key, count)); + } + /** Returns the number of keys in `keys` that exist in the database. * See https://valkey.io/commands/exists/ for details. * diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 97b324e2c4..bf3fdc3ab2 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -23,7 +23,7 @@ function isLargeCommand(args: BulkString[]) { return false; } -type BulkString = string | Uint8Array; +export type BulkString = string | Uint8Array; /** * Convert a string array into Uint8Array[] @@ -669,6 +669,18 @@ export function createSPop( return createCommand(RequestType.SPop, args); } +/** + * @internal + */ +export function createSRandMember( + key: BulkString, + count?: number, +): command_request.Command { + const args: BulkString[] = + count == undefined ? [key] : [key, count.toString()]; + return createCommand(RequestType.SRandMember, args); +} + /** * @internal */ diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index d67f6a0195..b2c26f7e01 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -4,6 +4,7 @@ import { AggregationType, + BulkString, ExpireOptions, InfoOptions, InsertPosition, @@ -83,7 +84,9 @@ import { createSMembers, createSMove, createSPop, + createSRandMember, createSRem, + createSUnion, createSUnionStore, createSelect, createSet, @@ -110,7 +113,6 @@ import { createZRemRangeByRank, createZRemRangeByScore, createZScore, - createSUnion, } from "./Commands"; import { command_request } from "./ProtobufMessage"; @@ -831,6 +833,29 @@ export class BaseTransaction> { return this.addAndReturn(createSPop(key, count), true); } + /** Returns a random element from the set value stored at `key`. + * See https://valkey.io/commands/srandmember for more details. + * + * @param key - The key from which to retrieve the set member. + * Command Response - a random element from the set, or null if `key` does not exist. + */ + public srandmember(key: BulkString): T { + return this.addAndReturn(createSRandMember(key)); + } + + /** Returns one or more random elements from the set value stored at `key`. + * See https://valkey.io/commands/srandmember for more details. + * + * @param key - The key of the sorted set. + * @param count - The number of members to return. + * If `count` is positive, returns unique members. + * If `count` is negative, allows for duplicates members. + * Command Response - a list of members from the set. If the set does not exist or is empty, an empty list will be returned. + */ + public srandmemberCount(key: BulkString, count: number): T { + return this.addAndReturn(createSRandMember(key, count)); + } + /** Returns the number of keys in `keys` that exist in the database. * See https://valkey.io/commands/exists/ for details. * diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index 8af80ab999..735043a347 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -1428,6 +1428,48 @@ export function runBaseTests(config: { config.timeout, ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( + `srandmember and srandmemberCount 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 result2 = await client.srandmember(key); + expect(members).toContain(intoString(result2)); + expect(await client.srandmember("nonExistingKey")).toEqual( + null, + ); + + // unique values are expected as count is positive + let result = await client.srandmemberCount(key, 4); + checkSimple(result.length).toEqual(3); + checkSimple(new Set(result)).toEqual(new Set(members)); + + // duplicate values are expected as count is negative + result = await client.srandmemberCount(key, -4); + checkSimple(result.length).toEqual(4); + result.forEach((member) => { + expect(members).toContain(intoString(member)); + }); + + // empty return values for non-existing or empty keys + result = await client.srandmemberCount(key, 0); + checkSimple(result.length).toEqual(0); + checkSimple(result).toEqual([]); + expect( + await client.srandmemberCount("nonExistingKey", 0), + ).toEqual([]); + + expect(await client.set(key, "value")).toBe("OK"); + await expect(client.srandmember(key)).rejects.toThrow(); + await expect(client.srandmemberCount(key, 2)).rejects.toThrow(); + }, protocol); + }, + config.timeout, + ); + it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])( `exists with existing keys, an non existing key_%p`, async (protocol) => { diff --git a/node/tests/TestUtilities.ts b/node/tests/TestUtilities.ts index b6d106a791..24416b13b6 100644 --- a/node/tests/TestUtilities.ts +++ b/node/tests/TestUtilities.ts @@ -404,6 +404,12 @@ export async function transactionTest( args.push(true); baseTransaction.smembers(key7); args.push(new Set(["bar"])); + baseTransaction.srandmember(key7); + args.push("bar"); + baseTransaction.srandmemberCount(key7, 2); + args.push(["bar"]); + baseTransaction.srandmemberCount(key7, -2); + args.push(["bar", "bar"]); baseTransaction.spop(key7); args.push("bar"); baseTransaction.spopCount(key7, 2);