diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 3efa793eeb9..1c67e321a32 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -901,6 +901,19 @@ export default class RedisClient< } while (cursor !== '0'); } + async* hScanValuesIterator( + this: RedisClientType, + key: RedisArgument, + options?: ScanCommonOptions & ScanIteratorOptions + ) { + let cursor = options?.cursor ?? '0'; + do { + const reply = await this.hScanNoValues(key, cursor, options); + cursor = reply.cursor; + yield reply.fields; + } while (cursor !== '0'); + } + async* sScanIterator( this: RedisClientType, key: RedisArgument, diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts new file mode 100644 index 00000000000..3564a958cc1 --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts @@ -0,0 +1,56 @@ +import { strict as assert } from 'node:assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import HSCAN_NOVALUES from './HSCAN_NOVALUES'; + +describe('HSCAN_NOVALUES', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0'), + ['HSCAN', 'key', '0', 'NOVALUES'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] + ); + }); + + it('with MATCH & COUNT', () => { + assert.deepEqual( + HSCAN_NOVALUES.transformArguments('key', '0', { + MATCH: 'pattern', + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'COUNT', '1', 'NOVALUES'] + ); + }); + }); + + testUtils.testWithClient('client.hScanNoValues', async client => { + const [, reply] = await Promise.all([ + client.hSet('key', 'field', 'value'), + client.hScanNoValues('key', '0') + ]); + + assert.deepEqual(reply, { + cursor: '0', + fields: [ + 'field', + ] + }); + }, GLOBAL.SERVERS.OPEN); +}); diff --git a/packages/client/lib/commands/HSCAN_NOVALUES.ts b/packages/client/lib/commands/HSCAN_NOVALUES.ts new file mode 100644 index 00000000000..35ff861338c --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -0,0 +1,22 @@ +import { RedisArgument, BlobStringReply, Command } from '../RESP/types'; +import { ScanCommonOptions, pushScanArguments } from './SCAN'; + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments( + key: RedisArgument, + cursor: RedisArgument, + options?: ScanCommonOptions + ) { + const args = pushScanArguments(['HSCAN', key], cursor, options); + args.push('NOVALUES'); + return args; + }, + transformReply([cursor, fields]: [BlobStringReply, Array]) { + return { + cursor, + fields + }; + } +} as const satisfies Command; diff --git a/packages/client/lib/commands/index.ts b/packages/client/lib/commands/index.ts index b2898988386..55a61f577ed 100644 --- a/packages/client/lib/commands/index.ts +++ b/packages/client/lib/commands/index.ts @@ -144,6 +144,7 @@ import HRANDFIELD_COUNT_WITHVALUES from './HRANDFIELD_COUNT_WITHVALUES'; import HRANDFIELD_COUNT from './HRANDFIELD_COUNT'; import HRANDFIELD from './HRANDFIELD'; import HSCAN from './HSCAN'; +import HSCAN_NOVALUES from './HSCAN_NOVALUES'; import HSET from './HSET'; import HSETNX from './HSETNX'; import HSTRLEN from './HSTRLEN'; @@ -623,6 +624,8 @@ export default { hRandField: HRANDFIELD, HSCAN, hScan: HSCAN, + HSCAN_NOVALUES, + hScanNoValues: HSCAN_NOVALUES, HSET, hSet: HSET, HSETNX,