Skip to content

Commit

Permalink
Node: add GETBIT command (valkey-io#1989)
Browse files Browse the repository at this point in the history
Signed-off-by: aaron-congo <[email protected]>
Signed-off-by: Chloe Yip <[email protected]>
  • Loading branch information
aaron-congo authored and cyip10 committed Jul 23, 2024
1 parent 142baf4 commit 8858209
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Node: Added BITCOUNT command ([#1982](https://github.com/valkey-io/valkey-glide/pull/1982))
* Node: Added FLUSHDB command ([#1986](https://github.com/valkey-io/valkey-glide/pull/1986))
* Node: Added GETDEL command ([#1968](https://github.com/valkey-io/valkey-glide/pull/1968))
* Node: Added GETBIT command ([#1989](https://github.com/valkey-io/valkey-glide/pull/1989))
* Node: Added SETBIT command ([#1978](https://github.com/valkey-io/valkey-glide/pull/1978))
* Node: Added LPUSHX and RPUSHX command([#1959](https://github.com/valkey-io/valkey-glide/pull/1959))
* Node: Added LSET command ([#1952](https://github.com/valkey-io/valkey-glide/pull/1952))
Expand Down
22 changes: 22 additions & 0 deletions node/src/BaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
createExpireAt,
createGeoAdd,
createGet,
createGetBit,
createGetDel,
createHDel,
createHExists,
Expand Down Expand Up @@ -975,6 +976,27 @@ export class BaseClient {
return this.createWritePromise(createDecrBy(key, amount));
}

/**
* Returns the bit value at `offset` in the string value stored at `key`. `offset` must be greater than or equal
* to zero.
*
* See https://valkey.io/commands/getbit/ for more details.
*
* @param key - The key of the string.
* @param offset - The index of the bit to return.
* @returns The bit at the given `offset` of the string. Returns `0` if the key is empty or if the `offset` exceeds
* the length of the string.
*
* @example
* ```typescript
* const result = await client.getbit("key", 1);
* console.log(result); // Output: 1 - The second bit of the string stored at "key" is set to 1.
* ```
*/
public getbit(key: string, offset: number): Promise<number> {
return this.createWritePromise(createGetBit(key, offset));
}

/**
* Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index, with
* `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less than
Expand Down
10 changes: 10 additions & 0 deletions node/src/Commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,16 @@ export function createDecrBy(
return createCommand(RequestType.DecrBy, [key, amount.toString()]);
}

/**
* @internal
*/
export function createGetBit(
key: string,
offset: number,
): command_request.Command {
return createCommand(RequestType.GetBit, [key, offset.toString()]);
}

/**
* @internal
*/
Expand Down
17 changes: 17 additions & 0 deletions node/src/Transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
createFunctionLoad,
createGeoAdd,
createGet,
createGetBit,
createGetDel,
createHDel,
createHExists,
Expand Down Expand Up @@ -391,6 +392,22 @@ export class BaseTransaction<T extends BaseTransaction<T>> {
return this.addAndReturn(createDecrBy(key, amount));
}

/**
* Returns the bit value at `offset` in the string value stored at `key`. `offset` must be greater than or equal
* to zero.
*
* See https://valkey.io/commands/getbit/ for more details.
*
* @param key - The key of the string.
* @param offset - The index of the bit to return.
*
* Command Response - The bit at the given `offset` of the string. Returns `0` if the key is empty or if the
* `offset` exceeds the length of the string.
*/
public getbit(key: string, offset: number): T {
return this.addAndReturn(createGetBit(key, offset));
}

/**
* Sets or clears the bit at `offset` in the string value stored at `key`. The `offset` is a zero-based index, with
* `0` being the first element of the list, `1` being the next element, and so on. The `offset` must be less than
Expand Down
37 changes: 34 additions & 3 deletions node/tests/SharedTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -496,12 +496,43 @@ export function runBaseTests<Context>(config: {
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`getbit test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key = `{key}-${uuidv4()}`;
const nonExistingKey = `{key}-${uuidv4()}`;
const setKey = `{key}-${uuidv4()}`;

expect(await client.set(key, "foo")).toEqual("OK");
expect(await client.getbit(key, 1)).toEqual(1);
// When offset is beyond the string length, the string is assumed to be a contiguous space with 0 bits.
expect(await client.getbit(key, 1000)).toEqual(0);
// When key does not exist it is assumed to be an empty string, so offset is always out of range and the
// value is also assumed to be a contiguous space with 0 bits.
expect(await client.getbit(nonExistingKey, 1)).toEqual(0);

// invalid argument - offset can't be negative
await expect(client.getbit(key, -1)).rejects.toThrow(
RequestError,
);

// key exists, but it is not a string
expect(await client.sadd(setKey, ["foo"])).toEqual(1);
await expect(client.getbit(setKey, 0)).rejects.toThrow(
RequestError,
);
}, protocol);
},
config.timeout,
);

it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
`setbit test_%p`,
async (protocol) => {
await runTest(async (client: BaseClient) => {
const key = `{key}-${uuidv4()}`;
const stringKey = `{key}-${uuidv4()}`;
const setKey = `{key}-${uuidv4()}`;

expect(await client.setbit(key, 1, 1)).toEqual(0);
expect(await client.setbit(key, 1, 0)).toEqual(1);
Expand All @@ -517,8 +548,8 @@ export function runBaseTests<Context>(config: {
);

// key exists, but it is not a string
expect(await client.sadd(stringKey, ["foo"])).toEqual(1);
await expect(client.setbit(stringKey, 0, 0)).rejects.toThrow(
expect(await client.sadd(setKey, ["foo"])).toEqual(1);
await expect(client.setbit(setKey, 0, 0)).rejects.toThrow(
RequestError,
);
}, protocol);
Expand Down
2 changes: 2 additions & 0 deletions node/tests/TestUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ export async function transactionTest(

baseTransaction.setbit(key17, 1, 1);
args.push(0);
baseTransaction.getbit(key17, 1);
args.push(1);
baseTransaction.set(key17, "foobar");
args.push("OK");
baseTransaction.bitcount(key17);
Expand Down

0 comments on commit 8858209

Please sign in to comment.