diff --git a/CHANGELOG.md b/CHANGELOG.md index b7639dba5b..78bb75ed77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -99,6 +99,7 @@ * Node: Added ZINTER and ZUNION commands ([#2146](https://github.com/aws/glide-for-redis/pull/2146)) * Node: Added XACK commands ([#2112](https://github.com/valkey-io/valkey-glide/pull/2112)) * Node: Added XGROUP SETID command ([#2135]((https://github.com/valkey-io/valkey-glide/pull/2135)) +* Node: Added binary variant to string commands ([#2183](https://github.com/valkey-io/valkey-glide/pull/2183)) #### Breaking Changes * Node: (Refactor) Convert classes to types ([#2005](https://github.com/valkey-io/valkey-glide/pull/2005)) diff --git a/node/src/BaseClient.ts b/node/src/BaseClient.ts index 772e1e904b..0e14bcfe12 100644 --- a/node/src/BaseClient.ts +++ b/node/src/BaseClient.ts @@ -987,9 +987,11 @@ export class BaseClient { * @remarks Since Valkey version 6.2.0. * * @param key - The key to retrieve from the database. - * @param options - (Optional) Set expiriation to the given key. - * "persist" will retain the time to live associated with the key. Equivalent to `PERSIST` in the VALKEY API. - * Otherwise, a {@link TimeUnit} and duration of the expire time should be specified. + * @param options - (Optional) Additional Parameters: + * - (Optional) `expiry`: expiriation to the given key: + * `"persist"` will retain the time to live associated with the key. Equivalent to `PERSIST` in the VALKEY API. + * Otherwise, a {@link TimeUnit} and duration of the expire time should be specified. + * - (Optional) `decoder`: see {@link DecoderOption}. * @returns If `key` exists, returns the value of `key` as a `string`. Otherwise, return `null`. * * @example @@ -999,10 +1001,14 @@ export class BaseClient { * ``` */ public async getex( - key: string, - options?: "persist" | { type: TimeUnit; duration: number }, - ): Promise { - return this.createWritePromise(createGetEx(key, options)); + key: GlideString, + options?: { + expiry: "persist" | { type: TimeUnit; duration: number }; + } & DecoderOption, + ): Promise { + return this.createWritePromise(createGetEx(key, options?.expiry), { + decoder: options?.decoder, + }); } /** @@ -1076,7 +1082,7 @@ export class BaseClient { * * @param key - The key to store. * @param value - The value to store with the given key. - * @param options - The set options. + * @param options - (Optional) See {@link SetOptions} and {@link DecoderOption}. * @returns - If the value is successfully set, return OK. * If value isn't set because of `onlyIfExists` or `onlyIfDoesNotExist` conditions, return null. * If `returnOldValue` is set, return the old value as a string. @@ -1103,9 +1109,11 @@ export class BaseClient { public async set( key: GlideString, value: GlideString, - options?: SetOptions, - ): Promise<"OK" | string | null> { - return this.createWritePromise(createSet(key, value, options)); + options?: SetOptions & DecoderOption, + ): Promise<"OK" | GlideString | null> { + return this.createWritePromise(createSet(key, value, options), { + decoder: options?.decoder, + }); } /** @@ -1294,7 +1302,7 @@ export class BaseClient { * console.log(result); // Output: 11 * ``` */ - public async incr(key: string): Promise { + public async incr(key: GlideString): Promise { return this.createWritePromise(createIncr(key)); } @@ -1314,7 +1322,7 @@ export class BaseClient { * console.log(result); // Output: 15 * ``` */ - public async incrBy(key: string, amount: number): Promise { + public async incrBy(key: GlideString, amount: number): Promise { return this.createWritePromise(createIncrBy(key, amount)); } @@ -1336,7 +1344,10 @@ export class BaseClient { * console.log(result); // Output: 13.0 * ``` */ - public async incrByFloat(key: string, amount: number): Promise { + public async incrByFloat( + key: GlideString, + amount: number, + ): Promise { return this.createWritePromise(createIncrByFloat(key, amount)); } @@ -1355,7 +1366,7 @@ export class BaseClient { * console.log(result); // Output: 9 * ``` */ - public async decr(key: string): Promise { + public async decr(key: GlideString): Promise { return this.createWritePromise(createDecr(key)); } @@ -1375,7 +1386,7 @@ export class BaseClient { * console.log(result); // Output: 5 * ``` */ - public async decrBy(key: string, amount: number): Promise { + public async decrBy(key: GlideString, amount: number): Promise { return this.createWritePromise(createDecrBy(key, amount)); } @@ -6319,6 +6330,7 @@ export class BaseClient { * * @param key1 - The key that stores the first string. * @param key2 - The key that stores the second string. + * @param options - (Optional) See {@link DecoderOption}. * @returns A `String` containing all the longest common subsequence combined between the 2 strings. * An empty `String` is returned if the keys do not exist or have no common subsequences. * @@ -6329,8 +6341,12 @@ export class BaseClient { * console.log(result); // Output: 'acd' * ``` */ - public async lcs(key1: string, key2: string): Promise { - return this.createWritePromise(createLCS(key1, key2)); + public async lcs( + key1: GlideString, + key2: GlideString, + options?: DecoderOption, + ): Promise { + return this.createWritePromise(createLCS(key1, key2), options); } /** @@ -6342,6 +6358,7 @@ export class BaseClient { * * @param key1 - The key that stores the first string. * @param key2 - The key that stores the second string. + * @param options - (Optional) See {@link DecoderOption}. * @returns The total length of all the longest common subsequences between the 2 strings. * * @example @@ -6351,8 +6368,15 @@ export class BaseClient { * console.log(result); // Output: 3 * ``` */ - public async lcsLen(key1: string, key2: string): Promise { - return this.createWritePromise(createLCS(key1, key2, { len: true })); + public async lcsLen( + key1: GlideString, + key2: GlideString, + options?: DecoderOption, + ): Promise { + return this.createWritePromise( + createLCS(key1, key2, { len: true }), + options, + ); } /** @@ -6365,8 +6389,9 @@ export class BaseClient { * * @param key1 - The key that stores the first string. * @param key2 - The key that stores the second string. - * @param withMatchLen - (Optional) If `true`, include the length of the substring matched for the each match. - * @param minMatchLen - (Optional) The minimum length of matches to include in the result. + * @param options - (Optional) Additional parameters: + * - (Optional) `withMatchLen`: if `true`, include the length of the substring matched for the each match. + * - (Optional) `minMatchLen`: the minimum length of matches to include in the result. * @returns A `Record` containing the indices of the longest common subsequences between the * 2 strings and the lengths of the longest common subsequences. The resulting map contains two * keys, "matches" and "len": @@ -6402,12 +6427,16 @@ export class BaseClient { * ``` */ public async lcsIdx( - key1: string, - key2: string, - options?: { withMatchLen?: boolean; minMatchLen?: number }, + key1: GlideString, + key2: GlideString, + options?: { + withMatchLen?: boolean; + minMatchLen?: number; + }, ): Promise> { return this.createWritePromise( createLCS(key1, key2, { idx: options ?? {} }), + { decoder: Decoder.String }, ); } @@ -6509,9 +6538,9 @@ export class BaseClient { * ``` */ public async setrange( - key: string, + key: GlideString, offset: number, - value: string, + value: GlideString, ): Promise { return this.createWritePromise(createSetRange(key, offset, value)); } diff --git a/node/src/Commands.ts b/node/src/Commands.ts index 78fe767852..41c8eea946 100644 --- a/node/src/Commands.ts +++ b/node/src/Commands.ts @@ -348,7 +348,7 @@ export function createMSetNX( /** * @internal */ -export function createIncr(key: string): command_request.Command { +export function createIncr(key: GlideString): command_request.Command { return createCommand(RequestType.Incr, [key]); } @@ -356,7 +356,7 @@ export function createIncr(key: string): command_request.Command { * @internal */ export function createIncrBy( - key: string, + key: GlideString, amount: number, ): command_request.Command { return createCommand(RequestType.IncrBy, [key, amount.toString()]); @@ -366,7 +366,7 @@ export function createIncrBy( * @internal */ export function createIncrByFloat( - key: string, + key: GlideString, amount: number, ): command_request.Command { return createCommand(RequestType.IncrByFloat, [key, amount.toString()]); @@ -442,7 +442,7 @@ export function createHSetNX( /** * @internal */ -export function createDecr(key: string): command_request.Command { +export function createDecr(key: GlideString): command_request.Command { return createCommand(RequestType.Decr, [key]); } @@ -450,7 +450,7 @@ export function createDecr(key: string): command_request.Command { * @internal */ export function createDecrBy( - key: string, + key: GlideString, amount: number, ): command_request.Command { return createCommand(RequestType.DecrBy, [key, amount.toString()]); @@ -3681,8 +3681,8 @@ export function createLastSave(): command_request.Command { /** @internal */ export function createLCS( - key1: string, - key2: string, + key1: GlideString, + key2: GlideString, options?: { len?: boolean; idx?: { withMatchLen?: boolean; minMatchLen?: number }; @@ -3794,9 +3794,9 @@ export function createZScan( /** @internal */ export function createSetRange( - key: string, + key: GlideString, offset: number, - value: string, + value: GlideString, ): command_request.Command { return createCommand(RequestType.SetRange, [key, offset.toString(), value]); } @@ -3944,7 +3944,7 @@ export enum TimeUnit { * @internal */ export function createGetEx( - key: string, + key: GlideString, options?: "persist" | { type: TimeUnit; duration: number }, ): command_request.Command { const args = [key]; diff --git a/node/src/Transaction.ts b/node/src/Transaction.ts index eabf799f1b..0422fdee09 100644 --- a/node/src/Transaction.ts +++ b/node/src/Transaction.ts @@ -337,7 +337,7 @@ export class BaseTransaction> { * Command Response - If `key` exists, returns the value of `key` as a `string`. Otherwise, return `null`. */ public getex( - key: string, + key: GlideString, options?: "persist" | { type: TimeUnit; duration: number }, ): T { return this.addAndReturn(createGetEx(key, options)); @@ -386,7 +386,7 @@ export class BaseTransaction> { * If `value` isn't set because of `onlyIfExists` or `onlyIfDoesNotExist` conditions, return null. * If `returnOldValue` is set, return the old value as a string. */ - public set(key: string, value: string, options?: SetOptions): T { + public set(key: GlideString, value: GlideString, options?: SetOptions): T { return this.addAndReturn(createSet(key, value, options)); } @@ -545,7 +545,7 @@ export class BaseTransaction> { * * Command Response - the value of `key` after the increment. */ - public incr(key: string): T { + public incr(key: GlideString): T { return this.addAndReturn(createIncr(key)); } @@ -557,7 +557,7 @@ export class BaseTransaction> { * * Command Response - the value of `key` after the increment. */ - public incrBy(key: string, amount: number): T { + public incrBy(key: GlideString, amount: number): T { return this.addAndReturn(createIncrBy(key, amount)); } @@ -572,7 +572,7 @@ export class BaseTransaction> { * Command Response - the value of `key` after the increment. * */ - public incrByFloat(key: string, amount: number): T { + public incrByFloat(key: GlideString, amount: number): T { return this.addAndReturn(createIncrByFloat(key, amount)); } @@ -594,7 +594,7 @@ export class BaseTransaction> { * * Command Response - the value of `key` after the decrement. */ - public decr(key: string): T { + public decr(key: GlideString): T { return this.addAndReturn(createDecr(key)); } @@ -606,7 +606,7 @@ export class BaseTransaction> { * * Command Response - the value of `key` after the decrement. */ - public decrBy(key: string, amount: number): T { + public decrBy(key: GlideString, amount: number): T { return this.addAndReturn(createDecrBy(key, amount)); } @@ -3679,7 +3679,7 @@ export class BaseTransaction> { * Command Response - A `String` containing all the longest common subsequence combined between the 2 strings. * An empty `String` is returned if the keys do not exist or have no common subsequences. */ - public lcs(key1: string, key2: string): T { + public lcs(key1: GlideString, key2: GlideString): T { return this.addAndReturn(createLCS(key1, key2)); } @@ -3694,7 +3694,7 @@ export class BaseTransaction> { * * Command Response - The total length of all the longest common subsequences between the 2 strings. */ - public lcsLen(key1: string, key2: string): T { + public lcsLen(key1: GlideString, key2: GlideString): T { return this.addAndReturn(createLCS(key1, key2, { len: true })); } @@ -3707,8 +3707,9 @@ export class BaseTransaction> { * * @param key1 - The key that stores the first string. * @param key2 - The key that stores the second string. - * @param withMatchLen - (Optional) If `true`, include the length of the substring matched for the each match. - * @param minMatchLen - (Optional) The minimum length of matches to include in the result. + * @param options - (Optional) Additional parameters: + * - (Optional) `withMatchLen`: if `true`, include the length of the substring matched for the each match. + * - (Optional) `minMatchLen`: the minimum length of matches to include in the result. * * Command Response - A `Record` containing the indices of the longest common subsequences between the * 2 strings and the lengths of the longest common subsequences. The resulting map contains two @@ -3722,8 +3723,8 @@ export class BaseTransaction> { * See example of {@link BaseClient.lcsIdx|lcsIdx} for more details. */ public lcsIdx( - key1: string, - key2: string, + key1: GlideString, + key2: GlideString, options?: { withMatchLen?: boolean; minMatchLen?: number }, ): T { return this.addAndReturn(createLCS(key1, key2, { idx: options ?? {} })); @@ -3766,7 +3767,7 @@ export class BaseTransaction> { * * Command Response - The length of the string stored at `key` after it was modified. */ - public setrange(key: string, offset: number, value: string): T { + public setrange(key: GlideString, offset: number, value: GlideString): T { return this.addAndReturn(createSetRange(key, offset, value)); } diff --git a/node/tests/SharedTests.ts b/node/tests/SharedTests.ts index c3b82e046b..0fb3074630 100644 --- a/node/tests/SharedTests.ts +++ b/node/tests/SharedTests.ts @@ -438,13 +438,17 @@ export function runBaseTests(config: { async (protocol) => { await runTest(async (client: BaseClient) => { const key = uuidv4(); + const keyEncoded = Buffer.from(key); expect(await client.set(key, "10")).toEqual("OK"); expect(await client.incr(key)).toEqual(11); - expect(await client.get(key)).toEqual("11"); - expect(await client.incrBy(key, 4)).toEqual(15); - expect(await client.get(key)).toEqual("15"); - expect(await client.incrByFloat(key, 1.5)).toEqual(16.5); - expect(await client.get(key)).toEqual("16.5"); + expect(await client.incr(keyEncoded)).toEqual(12); + expect(await client.get(key)).toEqual("12"); + expect(await client.incrBy(key, 4)).toEqual(16); + expect(await client.incrBy(keyEncoded, 1)).toEqual(17); + expect(await client.get(key)).toEqual("17"); + expect(await client.incrByFloat(key, 1.5)).toEqual(18.5); + expect(await client.incrByFloat(key, 1.5)).toEqual(20); + expect(await client.get(key)).toEqual("20"); }, protocol); }, config.timeout, @@ -550,11 +554,14 @@ export function runBaseTests(config: { async (protocol) => { await runTest(async (client: BaseClient) => { const key = uuidv4(); + const keyEncoded = Buffer.from(key); expect(await client.set(key, "10")).toEqual("OK"); expect(await client.decr(key)).toEqual(9); - expect(await client.get(key)).toEqual("9"); - expect(await client.decrBy(key, 4)).toEqual(5); - expect(await client.get(key)).toEqual("5"); + expect(await client.decr(keyEncoded)).toEqual(8); + expect(await client.get(key)).toEqual("8"); + expect(await client.decrBy(key, 4)).toEqual(4); + expect(await client.decrBy(keyEncoded, 1)).toEqual(3); + expect(await client.get(key)).toEqual("3"); }, protocol); }, config.timeout, @@ -9679,11 +9686,23 @@ export function runBaseTests(config: { // keys does not exist or is empty expect(await client.lcs(key1, key2)).toEqual(""); + expect( + await client.lcs(Buffer.from(key1), Buffer.from(key2)), + ).toEqual(""); expect(await client.lcsLen(key1, key2)).toEqual(0); + expect( + await client.lcsLen(Buffer.from(key1), Buffer.from(key2)), + ).toEqual(0); expect(await client.lcsIdx(key1, key2)).toEqual({ matches: [], len: 0, }); + expect( + await client.lcsIdx(Buffer.from(key1), Buffer.from(key2)), + ).toEqual({ + matches: [], + len: 0, + }); // LCS with some strings expect( @@ -11203,12 +11222,25 @@ export function runBaseTests(config: { expect( await client.getex(key1, { - type: TimeUnit.Seconds, - duration: 15, + expiry: { + type: TimeUnit.Seconds, + duration: 15, + }, + }), + ).toEqual(value); + // test the binary option + expect( + await client.getex(Buffer.from(key1), { + expiry: { + type: TimeUnit.Seconds, + duration: 1, + }, }), ).toEqual(value); expect(await client.ttl(key1)).toBeGreaterThan(0); - expect(await client.getex(key1, "persist")).toEqual(value); + expect(await client.getex(key1, { expiry: "persist" })).toEqual( + value, + ); expect(await client.ttl(key1)).toBe(-1); // non existent key @@ -11217,8 +11249,10 @@ export function runBaseTests(config: { // invalid time measurement await expect( client.getex(key1, { - type: TimeUnit.Seconds, - duration: -10, + expiry: { + type: TimeUnit.Seconds, + duration: -10, + }, }), ).rejects.toThrow(RequestError);