1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-06 02:15:48 +03:00

Merge branch 'master' of github.com:redis/node-redis into v5

This commit is contained in:
Leibale
2023-09-19 18:57:28 -04:00
20 changed files with 445 additions and 24 deletions

View File

@@ -3,7 +3,7 @@
This folder contains example scripts showing how to use Node Redis in different scenarios. This folder contains example scripts showing how to use Node Redis in different scenarios.
| File Name | Description | | File Name | Description |
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | |------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------|
| `blocking-list-pop.js` | Block until an element is pushed to a list. | | `blocking-list-pop.js` | Block until an element is pushed to a list. |
| `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io). | | `bloom-filter.js` | Space efficient set membership checks with a [Bloom Filter](https://en.wikipedia.org/wiki/Bloom_filter) using [RedisBloom](https://redisbloom.io). |
| `check-connection-status.js` | Check the client's connection status. | | `check-connection-status.js` | Check the client's connection status. |
@@ -12,6 +12,7 @@ This folder contains example scripts showing how to use Node Redis in different
| `connect-to-cluster.js` | Connect to a Redis cluster. | | `connect-to-cluster.js` | Connect to a Redis cluster. |
| `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch. | | `count-min-sketch.js` | Estimate the frequency of a given event using the [RedisBloom](https://redisbloom.io) Count-Min Sketch. |
| `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). | | `cuckoo-filter.js` | Space efficient set membership checks with a [Cuckoo Filter](https://en.wikipedia.org/wiki/Cuckoo_filter) using [RedisBloom](https://redisbloom.io). |
| `dump-and-restore.js` | Demonstrates the use of the [`DUMP`](https://redis.io/commands/dump/) and [`RESTORE`](https://redis.io/commands/restore/) commands |
| `get-server-time.js` | Get the time from the Redis server. | | `get-server-time.js` | Get the time from the Redis server. |
| `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). | | `hyperloglog.js` | Showing use of Hyperloglog commands [PFADD, PFCOUNT and PFMERGE](https://redis.io/commands/?group=hyperloglog). |
| `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. | | `lua-multi-incr.js` | Define a custom lua script that allows you to perform INCRBY on multiple keys. |

View File

@@ -0,0 +1,22 @@
// This example demonstrates the use of the DUMP and RESTORE commands
import { commandOptions, createClient } from 'redis';
const client = createClient();
await client.connect();
// DUMP a specific key into a local variable
const dump = await client.dump(
commandOptions({ returnBuffers: true }),
'source'
);
// RESTORE into a new key
await client.restore('destination', 0, dump);
// RESTORE and REPLACE an existing key
await client.restore('destination', 0, dump, {
REPLACE: true
});
await client.quit();

50
package-lock.json generated
View File

@@ -131,9 +131,9 @@
} }
}, },
"node_modules/@babel/compat-data": { "node_modules/@babel/compat-data": {
"version": "7.22.9", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.20.tgz",
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", "integrity": "sha512-BQYjKbpXjoXwFW5jGqiizJQQT/aC7pFm9Ok1OWssonuguICi264lbgMzRp2ZMmRSlfkX6DsWDDcsrctK8Rwfiw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -219,9 +219,9 @@
} }
}, },
"node_modules/@babel/helper-environment-visitor": { "node_modules/@babel/helper-environment-visitor": {
"version": "7.22.5", "version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=6.9.0" "node": ">=6.9.0"
@@ -354,7 +354,7 @@
"integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==", "integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/helper-validator-identifier": "^7.22.5", "@babel/helper-validator-identifier": "^7.22.20",
"chalk": "^2.4.2", "chalk": "^2.4.2",
"js-tokens": "^4.0.0" "js-tokens": "^4.0.0"
}, },
@@ -922,9 +922,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.12", "version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", "integrity": "sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==",
"dev": true "dev": true
}, },
"node_modules/@types/mocha": { "node_modules/@types/mocha": {
@@ -3734,6 +3734,20 @@
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
"dev": true "dev": true
}, },
"node_modules/set-function-name": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz",
"integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==",
"dev": true,
"dependencies": {
"define-data-property": "^1.0.1",
"functions-have-names": "^1.2.3",
"has-property-descriptors": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/shebang-command": { "node_modules/shebang-command": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -3774,9 +3788,9 @@
"dev": true "dev": true
}, },
"node_modules/sinon": { "node_modules/sinon": {
"version": "15.2.0", "version": "16.0.0",
"resolved": "https://registry.npmjs.org/sinon/-/sinon-15.2.0.tgz", "resolved": "https://registry.npmjs.org/sinon/-/sinon-16.0.0.tgz",
"integrity": "sha512-nPS85arNqwBXaIsFCkolHjGIkFo+Oxu9vbgmBJizLAhqe6P2o3Qmj3KCUoRkfhHtvgDhZdWD3risLHAUJ8npjw==", "integrity": "sha512-B8AaZZm9CT5pqe4l4uWJztfD/mOTa7dL8Qo0W4+s+t74xECOgSZDDQCBjNgIK3+n4kyxQrSTv2V5ul8K25qkiQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@sinonjs/commons": "^3.0.0", "@sinonjs/commons": "^3.0.0",
@@ -3995,9 +4009,9 @@
} }
}, },
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "1.0.2", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz",
"integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": ">=16.13.0" "node": ">=16.13.0"
@@ -4447,7 +4461,7 @@
}, },
"packages/json": { "packages/json": {
"name": "@redis/json", "name": "@redis/json",
"version": "1.0.4", "version": "1.0.5",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@redis/test-utils": "*" "@redis/test-utils": "*"
@@ -4488,7 +4502,7 @@
}, },
"packages/search": { "packages/search": {
"name": "@redis/search", "name": "@redis/search",
"version": "1.1.3", "version": "1.1.4",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@redis/test-utils": "*" "@redis/test-utils": "*"

View File

@@ -0,0 +1,30 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLIENT_NO-TOUCH';
describe('CLIENT NO-TOUCH', () => {
testUtils.isVersionGreaterThanHook([7, 2]);
describe('transformArguments', () => {
it('true', () => {
assert.deepEqual(
transformArguments(true),
['CLIENT', 'NO-TOUCH', 'ON']
);
});
it('false', () => {
assert.deepEqual(
transformArguments(false),
['CLIENT', 'NO-TOUCH', 'OFF']
);
});
});
testUtils.testWithClient('client.clientNoTouch', async client => {
assert.equal(
await client.clientNoTouch(true),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,11 @@
import { RedisCommandArguments } from '.';
export function transformArguments(value: boolean): RedisCommandArguments {
return [
'CLIENT',
'NO-TOUCH',
value ? 'ON' : 'OFF'
];
}
export declare function transformReply(): 'OK' | Buffer;

View File

@@ -0,0 +1,22 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CLUSTER_MYSHARDID';
describe('CLUSTER MYSHARDID', () => {
testUtils.isVersionGreaterThanHook([7, 2]);
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLUSTER', 'MYSHARDID']
);
});
testUtils.testWithCluster('clusterNode.clusterMyShardId', async cluster => {
const client = await cluster.nodeClient(cluster.masters[0]);
assert.equal(
typeof await client.clusterMyShardId(),
'string'
);
}, GLOBAL.CLUSTERS.OPEN);
});

View File

@@ -0,0 +1,7 @@
export const IS_READ_ONLY = true;
export function transformArguments() {
return ['CLUSTER', 'MYSHARDID'];
}
export declare function transformReply(): string | Buffer;

View File

@@ -0,0 +1,26 @@
import {strict as assert} from 'assert';
import testUtils, {GLOBAL} from '../test-utils';
import { transformArguments } from './LATENCY_HISTORY';
describe('LATENCY HISTORY', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('command'),
['LATENCY', 'HISTORY', 'command']
);
});
testUtils.testWithClient('client.latencyHistory', async client => {
await Promise.all([
client.configSet('latency-monitor-threshold', '100'),
client.sendCommand(['DEBUG', 'SLEEP', '1'])
]);
const latencyHisRes = await client.latencyHistory('command');
assert.ok(Array.isArray(latencyHisRes));
for (const [timestamp, latency] of latencyHisRes) {
assert.equal(typeof timestamp, 'number');
assert.equal(typeof latency, 'number');
}
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,27 @@
export type EventType = (
'active-defrag-cycle' |
'aof-fsync-always' |
'aof-stat' |
'aof-rewrite-diff-write' |
'aof-rename' |
'aof-write' |
'aof-write-active-child' |
'aof-write-alone' |
'aof-write-pending-fsync' |
'command' |
'expire-cycle' |
'eviction-cycle' |
'eviction-del' |
'fast-command' |
'fork' |
'rdb-unlink-temp-file'
);
export function transformArguments(event: EventType) {
return ['LATENCY', 'HISTORY', event];
}
export declare function transformReply(): Array<[
timestamp: number,
latency: number,
]>;

View File

@@ -0,0 +1,48 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './PUBSUB_SHARDNUMSUB';
describe('PUBSUB SHARDNUMSUB', () => {
testUtils.isVersionGreaterThanHook([7]);
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['PUBSUB', 'SHARDNUMSUB']
);
});
it('string', () => {
assert.deepEqual(
transformArguments('channel'),
['PUBSUB', 'SHARDNUMSUB', 'channel']
);
});
it('array', () => {
assert.deepEqual(
transformArguments(['1', '2']),
['PUBSUB', 'SHARDNUMSUB', '1', '2']
);
});
});
testUtils.testWithClient('client.pubSubShardNumSub', async client => {
assert.deepEqual(
await client.pubSubShardNumSub(['foo', 'bar']),
Object.create(null, {
foo: {
value: 0,
configurable: true,
enumerable: true
},
bar: {
value: 0,
configurable: true,
enumerable: true
}
})
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,24 @@
import { pushVerdictArguments } from './generic-transformers';
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const IS_READ_ONLY = true;
export function transformArguments(
channels?: Array<RedisCommandArgument> | RedisCommandArgument
): RedisCommandArguments {
const args = ['PUBSUB', 'SHARDNUMSUB'];
if (channels) return pushVerdictArguments(args, channels);
return args;
}
export function transformReply(rawReply: Array<string | number>): Record<string, number> {
const transformedReply = Object.create(null);
for (let i = 0; i < rawReply.length; i += 2) {
transformedReply[rawReply[i]] = rawReply[i + 1];
}
return transformedReply;
}

View File

@@ -0,0 +1,74 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './RESTORE';
describe('RESTORE', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments('key', 0, 'value'),
['RESTORE', 'key', '0', 'value']
);
});
it('with REPLACE', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
REPLACE: true
}),
['RESTORE', 'key', '0', 'value', 'REPLACE']
);
});
it('with ABSTTL', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
ABSTTL: true
}),
['RESTORE', 'key', '0', 'value', 'ABSTTL']
);
});
it('with IDLETIME', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
IDLETIME: 1
}),
['RESTORE', 'key', '0', 'value', 'IDLETIME', '1']
);
});
it('with FREQ', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
FREQ: 1
}),
['RESTORE', 'key', '0', 'value', 'FREQ', '1']
);
});
it('with REPLACE, ABSTTL, IDLETIME and FREQ', () => {
assert.deepEqual(
transformArguments('key', 0, 'value', {
REPLACE: true,
ABSTTL: true,
IDLETIME: 1,
FREQ: 2
}),
['RESTORE', 'key', '0', 'value', 'REPLACE', 'ABSTTL', 'IDLETIME', '1', 'FREQ', '2']
);
});
});
testUtils.testWithClient('client.restore', async client => {
const [, dump] = await Promise.all([
client.set('source', 'value'),
client.dump(client.commandOptions({ returnBuffers: true }), 'source')
]);
assert.equal(
await client.restore('destination', 0, dump),
'OK'
);
}, GLOBAL.SERVERS.OPEN);
});

View File

@@ -0,0 +1,39 @@
import { RedisCommandArgument, RedisCommandArguments } from '.';
export const FIRST_KEY_INDEX = 1;
interface RestoreOptions {
REPLACE?: true;
ABSTTL?: true;
IDLETIME?: number;
FREQ?: number;
}
export function transformArguments(
key: RedisCommandArgument,
ttl: number,
serializedValue: RedisCommandArgument,
options?: RestoreOptions
): RedisCommandArguments {
const args = ['RESTORE', key, ttl.toString(), serializedValue];
if (options?.REPLACE) {
args.push('REPLACE');
}
if (options?.ABSTTL) {
args.push('ABSTTL');
}
if (options?.IDLETIME) {
args.push('IDLETIME', options.IDLETIME.toString());
}
if (options?.FREQ) {
args.push('FREQ', options.FREQ.toString());
}
return args;
}
export declare function transformReply(): 'OK';

View File

@@ -28,4 +28,5 @@
"keywords": [ "keywords": [
"redis", "redis",
"RedisGraph" "RedisGraph"
]} ]
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@redis/json", "name": "@redis/json",
"version": "1.0.4", "version": "1.0.5",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",

View File

@@ -6,7 +6,6 @@ export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true; export const IS_READ_ONLY = true;
export interface SearchOptions { export interface SearchOptions {
// NOCONTENT?: true; TODO
VERBATIM?: true; VERBATIM?: true;
NOSTOPWORDS?: true; NOSTOPWORDS?: true;
// WITHSCORES?: true; // WITHSCORES?: true;

View File

@@ -0,0 +1,45 @@
import { strict as assert } from 'assert';
import { SchemaFieldTypes } from '.';
import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments, transformReply } from './SEARCH_NOCONTENT';
describe('SEARCH_NOCONTENT', () => {
describe('transformArguments', () => {
it('without options', () => {
assert.deepEqual(
transformArguments('index', 'query'),
['FT.SEARCH', 'index', 'query', 'NOCONTENT']
);
});
});
describe('transformReply', () => {
it('returns total and keys', () => {
assert.deepEqual(transformReply([3, '1', '2', '3']), {
total: 3,
documents: ['1', '2', '3']
})
});
});
describe('client.ft.searchNoContent', () => {
testUtils.testWithClient('returns total and keys', async client => {
await Promise.all([
client.ft.create('index', {
field: SchemaFieldTypes.TEXT
}),
client.hSet('1', 'field', 'field1'),
client.hSet('2', 'field', 'field2'),
client.hSet('3', 'field', 'field3')
]);
assert.deepEqual(
await client.ft.searchNoContent('index', '*'),
{
total: 3,
documents: ['1','2','3']
}
);
}, GLOBAL.SERVERS.OPEN);
});
});

View File

@@ -0,0 +1,30 @@
import { RedisCommandArguments } from "@redis/client/dist/lib/commands";
import { pushSearchOptions } from ".";
import { SearchOptions, SearchRawReply } from "./SEARCH";
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(
index: string,
query: string,
options?: SearchOptions
): RedisCommandArguments {
return pushSearchOptions(
['FT.SEARCH', index, query, 'NOCONTENT'],
options
);
}
export interface SearchNoContentReply {
total: number;
documents: Array<string>;
};
export function transformReply(reply: SearchRawReply): SearchNoContentReply {
return {
total: reply[0],
documents: reply.slice(1)
};
}

View File

@@ -1,6 +1,6 @@
{ {
"name": "@redis/search", "name": "@redis/search",
"version": "1.1.3", "version": "1.1.4",
"license": "MIT", "license": "MIT",
"main": "./dist/index.js", "main": "./dist/index.js",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",

View File

@@ -28,4 +28,5 @@
"keywords": [ "keywords": [
"redis", "redis",
"RedisTimeSeries" "RedisTimeSeries"
]} ]
}