diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts index 4442d3adb83..b65da2344d4 100644 --- a/packages/client/lib/client/index.spec.ts +++ b/packages/client/lib/client/index.spec.ts @@ -788,6 +788,28 @@ describe('Client', () => { assert.deepEqual(hash, results); }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('hScanNoValuesIterator', async client => { + const hash: Record = {}; + const expectedKeys: Array = []; + for (let i = 0; i < 100; i++) { + hash[i.toString()] = i.toString(); + expectedKeys.push(i.toString()); + } + + await client.hSet('key', hash); + + const keys: Array = []; + for await (const key of client.hScanNoValuesIterator('key')) { + keys.push(key); + } + + function sort(a: string, b: string) { + return Number(a) - Number(b); + } + + assert.deepEqual(keys.sort(sort), expectedKeys); + }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('sScanIterator', async client => { const members = new Set(); for (let i = 0; i < 100; i++) { diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts index 4c3964c7aa0..d7f33e97b16 100644 --- a/packages/client/lib/client/index.ts +++ b/packages/client/lib/client/index.ts @@ -1,5 +1,5 @@ import COMMANDS from './commands'; -import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; +import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands'; import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket'; import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue'; import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command'; @@ -820,6 +820,17 @@ export default class RedisClient< } while (cursor !== 0); } + async* hScanNoValuesIterator(key: string, options?: ScanOptions): AsyncIterable> { + let cursor = 0; + do { + const reply = await (this as any).hScanNoValues(key, cursor, options); + cursor = reply.cursor; + for (const k of reply.keys) { + yield k; + } + } while (cursor !== 0); + } + async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable { let cursor = 0; do { diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 84a37862772..f9a45f4b905 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -64,6 +64,7 @@ import * as HRANDFIELD_COUNT_WITHVALUES from '../commands/HRANDFIELD_COUNT_WITHV import * as HRANDFIELD_COUNT from '../commands/HRANDFIELD_COUNT'; import * as HRANDFIELD from '../commands/HRANDFIELD'; import * as HSCAN from '../commands/HSCAN'; +import * as HSCAN_NOVALUES from '../commands/HSCAN_NOVALUES'; import * as HSET from '../commands/HSET'; import * as HSETNX from '../commands/HSETNX'; import * as HSTRLEN from '../commands/HSTRLEN'; @@ -343,6 +344,8 @@ export default { hRandField: HRANDFIELD, HSCAN, hScan: HSCAN, + HSCAN_NOVALUES, + hScanNoValues: HSCAN_NOVALUES, HSET, hSet: HSET, HSETNX, diff --git a/packages/client/lib/commands/HSCAN.spec.ts b/packages/client/lib/commands/HSCAN.spec.ts index b426763b99b..6757888a875 100644 --- a/packages/client/lib/commands/HSCAN.spec.ts +++ b/packages/client/lib/commands/HSCAN.spec.ts @@ -73,5 +73,18 @@ describe('HSCAN', () => { tuples: [] } ); + + await Promise.all([ + client.hSet('key', 'a', '1'), + client.hSet('key', 'b', '2') + ]); + + assert.deepEqual( + await client.hScan('key', 0), + { + cursor: 0, + tuples: [{field: 'a', value: '1'}, {field: 'b', value: '2'}] + } + ); }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/client/lib/commands/HSCAN.ts b/packages/client/lib/commands/HSCAN.ts index ba18fb986bc..5167693b604 100644 --- a/packages/client/lib/commands/HSCAN.ts +++ b/packages/client/lib/commands/HSCAN.ts @@ -16,7 +16,7 @@ export function transformArguments( ], cursor, options); } -type HScanRawReply = [RedisCommandArgument, Array]; +export type HScanRawReply = [RedisCommandArgument, Array]; export interface HScanTuple { field: RedisCommandArgument; 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..0e51792b5ed --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.spec.ts @@ -0,0 +1,77 @@ +import { strict as assert } from 'assert'; +import testUtils, { GLOBAL } from '../test-utils'; +import { transformArguments, transformReply } from './HSCAN_NOVALUES'; + +describe('HSCAN_NOVALUES', () => { + describe('transformArguments', () => { + it('cusror only', () => { + assert.deepEqual( + transformArguments('key', 0), + ['HSCAN', 'key', '0', 'NOVALUES'] + ); + }); + + it('with MATCH', () => { + assert.deepEqual( + transformArguments('key', 0, { + MATCH: 'pattern' + }), + ['HSCAN', 'key', '0', 'MATCH', 'pattern', 'NOVALUES'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', 0, { + COUNT: 1 + }), + ['HSCAN', 'key', '0', 'COUNT', '1', 'NOVALUES'] + ); + }); + }); + + describe('transformReply', () => { + it('without keys', () => { + assert.deepEqual( + transformReply(['0', []]), + { + cursor: 0, + keys: [] + } + ); + }); + + it('with keys', () => { + assert.deepEqual( + transformReply(['0', ['key1', 'key2']]), + { + cursor: 0, + keys: ['key1', 'key2'] + } + ); + }); + }); + + testUtils.testWithClient('client.hScanNoValues', async client => { + assert.deepEqual( + await client.hScanNoValues('key', 0), + { + cursor: 0, + keys: [] + } + ); + + await Promise.all([ + client.hSet('key', 'a', '1'), + client.hSet('key', 'b', '2') + ]); + + assert.deepEqual( + await client.hScanNoValues('key', 0), + { + cursor: 0, + keys: ['a', 'b'] + } + ); + }, 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..5b0062481bd --- /dev/null +++ b/packages/client/lib/commands/HSCAN_NOVALUES.ts @@ -0,0 +1,27 @@ +import { RedisCommandArgument, RedisCommandArguments } from '.'; +import { ScanOptions } from './generic-transformers'; +import { HScanRawReply, transformArguments as transformHScanArguments } from './HSCAN'; + +export {FIRST_KEY_INDEX, IS_READ_ONLY} from './HSCAN'; + +export function transformArguments( + key: RedisCommandArgument, + cursor: number, + options?: ScanOptions +): RedisCommandArguments { + const args = transformHScanArguments(key, cursor, options); + args.push('NOVALUES'); + return args; +} + +interface HScanNoValuesReply { + cursor: number; + keys: Array; +} + +export function transformReply([cursor, rawData]: HScanRawReply): HScanNoValuesReply { + return { + cursor: Number(cursor), + keys: [...rawData] + }; +}