You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
implement h/s/zScanIterator
Co-authored-by: Leibale Eidelman <me@leibale.com>
This commit is contained in:
@@ -2,13 +2,16 @@ import { strict as assert } from 'assert';
|
|||||||
import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils';
|
import testUtils, { GLOBAL, waitTillBeenCalled } from '../test-utils';
|
||||||
import RedisClient, { RedisClientType } from '.';
|
import RedisClient, { RedisClientType } from '.';
|
||||||
import { RedisClientMultiCommandType } from './multi-command';
|
import { RedisClientMultiCommandType } from './multi-command';
|
||||||
import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts } from '../commands';
|
import { RedisCommandArguments, RedisCommandRawReply, RedisModules, RedisFunctions, RedisScripts, ConvertArgumentType } from '../commands';
|
||||||
import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors';
|
import { AbortError, ClientClosedError, ClientOfflineError, ConnectionTimeoutError, DisconnectsClientError, SocketClosedUnexpectedlyError, WatchError } from '../errors';
|
||||||
import { defineScript } from '../lua-script';
|
import { defineScript } from '../lua-script';
|
||||||
import { spy } from 'sinon';
|
import { spy } from 'sinon';
|
||||||
import { once } from 'events';
|
import { once } from 'events';
|
||||||
import { ClientKillFilters } from '../commands/CLIENT_KILL';
|
import { ClientKillFilters } from '../commands/CLIENT_KILL';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
|
import { commandOptions } from '../../dist/lib/command-options';
|
||||||
|
import { ZMember } from '../commands/generic-transformers';
|
||||||
|
|
||||||
|
|
||||||
export const SQUARE_SCRIPT = defineScript({
|
export const SQUARE_SCRIPT = defineScript({
|
||||||
SCRIPT: 'return ARGV[1] * ARGV[1];',
|
SCRIPT: 'return ARGV[1] * ARGV[1];',
|
||||||
@@ -639,10 +642,10 @@ describe('Client', () => {
|
|||||||
await client.mSet(args);
|
await client.mSet(args);
|
||||||
|
|
||||||
const results = new Set<Buffer>(),
|
const results = new Set<Buffer>(),
|
||||||
iteartor = client.scanIterator(
|
iterator = client.scanIterator(
|
||||||
client.commandOptions({ returnBuffers: true })
|
client.commandOptions({ returnBuffers: true })
|
||||||
);
|
);
|
||||||
for await (const key of iteartor) {
|
for await (const key of iterator) {
|
||||||
results.add(key);
|
results.add(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -650,7 +653,8 @@ describe('Client', () => {
|
|||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
});
|
});
|
||||||
|
|
||||||
testUtils.testWithClient('hScanIterator', async client => {
|
describe('hScanIterator', () => {
|
||||||
|
testUtils.testWithClient('strings', async client => {
|
||||||
const hash: Record<string, string> = {};
|
const hash: Record<string, string> = {};
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
hash[i.toString()] = i.toString();
|
hash[i.toString()] = i.toString();
|
||||||
@@ -666,7 +670,30 @@ describe('Client', () => {
|
|||||||
assert.deepEqual(hash, results);
|
assert.deepEqual(hash, results);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
|
||||||
testUtils.testWithClient('sScanIterator', async client => {
|
testUtils.testWithClient('buffers', async client => {
|
||||||
|
const hash = new Map<Buffer, Buffer>();
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
const buffer = Buffer.from([i]);
|
||||||
|
hash.set(buffer, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.hSet('key', hash);
|
||||||
|
|
||||||
|
const results = new Map<Buffer, Buffer>(),
|
||||||
|
iterator = client.hScanIterator(
|
||||||
|
client.commandOptions({ returnBuffers: true }),
|
||||||
|
'key'
|
||||||
|
);
|
||||||
|
for await (const { field, value } of iterator) {
|
||||||
|
results.set(field, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(hash, results);
|
||||||
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('sScanIterator', () => {
|
||||||
|
testUtils.testWithClient('strings', async client => {
|
||||||
const members = new Set<string>();
|
const members = new Set<string>();
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
members.add(i.toString());
|
members.add(i.toString());
|
||||||
@@ -682,11 +709,33 @@ describe('Client', () => {
|
|||||||
assert.deepEqual(members, results);
|
assert.deepEqual(members, results);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
|
||||||
testUtils.testWithClient('zScanIterator', async client => {
|
testUtils.testWithClient('buffers', async client => {
|
||||||
const members = [];
|
const members = new Set<Buffer>();
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
members.add(Buffer.from([i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.sAdd('key', Array.from(members));
|
||||||
|
|
||||||
|
const results = new Set<Buffer>(),
|
||||||
|
iterator = client.sScanIterator(
|
||||||
|
client.commandOptions({ returnBuffers: true }),
|
||||||
|
'key'
|
||||||
|
);
|
||||||
|
for await (const key of iterator) {
|
||||||
|
results.add(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(members, results);
|
||||||
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('zScanIterator', () => {
|
||||||
|
testUtils.testWithClient('strings', async client => {
|
||||||
|
const members: Array<ConvertArgumentType<ZMember, string>> = [];
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
members.push({
|
members.push({
|
||||||
score: 1,
|
score: i,
|
||||||
value: i.toString()
|
value: i.toString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -710,6 +759,40 @@ describe('Client', () => {
|
|||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
|
||||||
|
testUtils.testWithClient('buffers', async client => {
|
||||||
|
const members: Array<ConvertArgumentType<ZMember, Buffer>> = [];
|
||||||
|
for (let i = 0; i < 100; i++) {
|
||||||
|
members.push({
|
||||||
|
score: i,
|
||||||
|
value: Buffer.from([i])
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.zAdd('key', members);
|
||||||
|
|
||||||
|
const map = new Map<Buffer, number>(),
|
||||||
|
iterator = client.zScanIterator(
|
||||||
|
client.commandOptions({ returnBuffers: true }),
|
||||||
|
'key'
|
||||||
|
);
|
||||||
|
|
||||||
|
for await (const member of iterator) {
|
||||||
|
map.set(member.value, member.score);
|
||||||
|
}
|
||||||
|
|
||||||
|
type MemberTuple = [Buffer, number];
|
||||||
|
|
||||||
|
function sort(a: MemberTuple, b: MemberTuple) {
|
||||||
|
return b[0][0] - a[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.deepEqual(
|
||||||
|
[...map.entries()].sort(sort),
|
||||||
|
members.map<MemberTuple>(member => [member.value, member.score]).sort(sort)
|
||||||
|
);
|
||||||
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
});
|
||||||
|
|
||||||
describe('PubSub', () => {
|
describe('PubSub', () => {
|
||||||
testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => {
|
testUtils.testWithClient('should be able to publish and subscribe to messages', async publisher => {
|
||||||
function assertStringListener(message: string, channel: string) {
|
function assertStringListener(message: string, channel: string) {
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import COMMANDS from './commands';
|
import COMMANDS from './commands';
|
||||||
import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands } from '../commands';
|
import { RedisCommand, RedisCommandArguments, RedisCommandRawReply, RedisCommandReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, RedisCommandSignature, ConvertArgumentType, RedisFunction, ExcludeMappedString, RedisCommands, RedisCommandArgument } from '../commands';
|
||||||
import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket';
|
import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket';
|
||||||
import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue';
|
import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue';
|
||||||
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
|
import RedisClientMultiCommand, { RedisClientMultiCommandType } from './multi-command';
|
||||||
@@ -656,6 +656,20 @@ export default class RedisClient<
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #scanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
// commandOptions: T,
|
||||||
|
// options?: ScanCommandOptions
|
||||||
|
// ): AsyncIterable<T['returnBuffers'] extends true ? Buffer : string>;
|
||||||
|
// #scanIterator(
|
||||||
|
// options?: ScanCommandOptions
|
||||||
|
// ): AsyncIterable<string>;
|
||||||
|
// async* #scanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
// commandOptions?: T | ScanCommandOptions,
|
||||||
|
// options?: ScanCommandOptions
|
||||||
|
// ): AsyncIterable<T['returnBuffers'] extends true ? Buffer : string> {
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
scanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
scanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
commandOptions: T,
|
commandOptions: T,
|
||||||
options?: ScanCommandOptions
|
options?: ScanCommandOptions
|
||||||
@@ -674,7 +688,7 @@ export default class RedisClient<
|
|||||||
|
|
||||||
const scan = commandOptions ?
|
const scan = commandOptions ?
|
||||||
(...args: Array<unknown>) => (this as any).scan(commandOptions, ...args) :
|
(...args: Array<unknown>) => (this as any).scan(commandOptions, ...args) :
|
||||||
(...args: Array<unknown>) => (this as any).scan(...args);
|
(this as any).scan.bind(this);
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
do {
|
do {
|
||||||
@@ -686,10 +700,33 @@ export default class RedisClient<
|
|||||||
} while (cursor !== 0);
|
} while (cursor !== 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async* hScanIterator(key: string, options?: ScanOptions): AsyncIterable<ConvertArgumentType<HScanTuple, string>> {
|
hScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions: T,
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<HScanTuple, T['returnBuffers'] extends true ? Buffer : string>>;
|
||||||
|
hScanIterator(
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<HScanTuple, string>>;
|
||||||
|
async* hScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions?: T | RedisCommandArgument,
|
||||||
|
key?: RedisCommandArgument | ScanOptions,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<HScanTuple, T['returnBuffers'] extends true ? Buffer : string>> {
|
||||||
|
if (!isCommandOptions(commandOptions)) {
|
||||||
|
options = key as ScanOptions | undefined;
|
||||||
|
key = commandOptions;
|
||||||
|
commandOptions = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hScan = commandOptions ?
|
||||||
|
(...args: Array<unknown>) => (this as any).hScan(commandOptions, ...args) :
|
||||||
|
(this as any).hScan.bind(this);
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
do {
|
do {
|
||||||
const reply = await (this as any).hScan(key, cursor, options);
|
const reply = await hScan(key, cursor, options);
|
||||||
cursor = reply.cursor;
|
cursor = reply.cursor;
|
||||||
for (const tuple of reply.tuples) {
|
for (const tuple of reply.tuples) {
|
||||||
yield tuple;
|
yield tuple;
|
||||||
@@ -697,10 +734,34 @@ export default class RedisClient<
|
|||||||
} while (cursor !== 0);
|
} while (cursor !== 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async* sScanIterator(key: string, options?: ScanOptions): AsyncIterable<string> {
|
|
||||||
|
sScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions: T,
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<T['returnBuffers'] extends true ? Buffer : string>;
|
||||||
|
sScanIterator(
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<string>;
|
||||||
|
async* sScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions?: T | RedisCommandArgument,
|
||||||
|
key?: RedisCommandArgument | ScanOptions,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<T['returnBuffers'] extends true ? Buffer : string> {
|
||||||
|
if (!isCommandOptions(commandOptions)) {
|
||||||
|
options = key as ScanOptions | undefined;
|
||||||
|
key = commandOptions;
|
||||||
|
commandOptions = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sScan = commandOptions ?
|
||||||
|
(...args: Array<unknown>) => (this as any).sScan(commandOptions, ...args) :
|
||||||
|
(this as any).sScan.bind(this);
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
do {
|
do {
|
||||||
const reply = await (this as any).sScan(key, cursor, options);
|
const reply = await sScan(key, cursor, options);
|
||||||
cursor = reply.cursor;
|
cursor = reply.cursor;
|
||||||
for (const member of reply.members) {
|
for (const member of reply.members) {
|
||||||
yield member;
|
yield member;
|
||||||
@@ -708,10 +769,33 @@ export default class RedisClient<
|
|||||||
} while (cursor !== 0);
|
} while (cursor !== 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
async* zScanIterator(key: string, options?: ScanOptions): AsyncIterable<ConvertArgumentType<ZMember, string>> {
|
zScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions: T,
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<ZMember, T['returnBuffers'] extends true ? Buffer : string>>;
|
||||||
|
zScanIterator(
|
||||||
|
key: RedisCommandArgument,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<ZMember, string>>;
|
||||||
|
async* zScanIterator<T extends CommandOptions<ClientCommandOptions>>(
|
||||||
|
commandOptions?: T | RedisCommandArgument,
|
||||||
|
key?: RedisCommandArgument | ScanOptions,
|
||||||
|
options?: ScanOptions
|
||||||
|
): AsyncIterable<ConvertArgumentType<ZMember, T['returnBuffers'] extends true ? Buffer : string>> {
|
||||||
|
if (!isCommandOptions(commandOptions)) {
|
||||||
|
options = key as ScanOptions | undefined;
|
||||||
|
key = commandOptions;
|
||||||
|
commandOptions = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const zScan = commandOptions ?
|
||||||
|
(...args: Array<unknown>) => (this as any).zScan(commandOptions, ...args) :
|
||||||
|
(this as any).zScan.bind(this);
|
||||||
|
|
||||||
let cursor = 0;
|
let cursor = 0;
|
||||||
do {
|
do {
|
||||||
const reply = await (this as any).zScan(key, cursor, options);
|
const reply = await zScan(key, cursor, options);
|
||||||
cursor = reply.cursor;
|
cursor = reply.cursor;
|
||||||
for (const member of reply.members) {
|
for (const member of reply.members) {
|
||||||
yield member;
|
yield member;
|
||||||
|
@@ -52,7 +52,7 @@ const DOCKER_FODLER_PATH = path.join(__dirname, '../docker');
|
|||||||
async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array<string>): Promise<RedisServerDocker> {
|
async function spawnRedisServerDocker({ image, version }: RedisServerDockerConfig, serverArguments: Array<string>): Promise<RedisServerDocker> {
|
||||||
const port = (await portIterator.next()).value,
|
const port = (await portIterator.next()).value,
|
||||||
{ stdout, stderr } = await execAsync(
|
{ stdout, stderr } = await execAsync(
|
||||||
'docker run -d --network host $(' +
|
`docker run -d -p ${port}:${port} host $(` +
|
||||||
`docker build ${DOCKER_FODLER_PATH} -q ` +
|
`docker build ${DOCKER_FODLER_PATH} -q ` +
|
||||||
`--build-arg IMAGE=${image}:${version} ` +
|
`--build-arg IMAGE=${image}:${version} ` +
|
||||||
`--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` +
|
`--build-arg REDIS_ARGUMENTS="--save '' --port ${port.toString()} ${serverArguments.join(' ')}"` +
|
||||||
|
Reference in New Issue
Block a user