diff --git a/examples/README.md b/examples/README.md index 19e9df31f90..4e7655a3519 100644 --- a/examples/README.md +++ b/examples/README.md @@ -3,7 +3,7 @@ This folder contains example scripts showing how to use Node Redis in different scenarios. | File Name | Description | -| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +|------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| | `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). | | `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. | | `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). | +| `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. | | `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. | diff --git a/examples/dump-and-restore.js b/examples/dump-and-restore.js new file mode 100644 index 00000000000..081e44f9f9a --- /dev/null +++ b/examples/dump-and-restore.js @@ -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(); diff --git a/packages/client/lib/cluster/commands.ts b/packages/client/lib/cluster/commands.ts index 58fa651be1b..84a37862772 100644 --- a/packages/client/lib/cluster/commands.ts +++ b/packages/client/lib/cluster/commands.ts @@ -110,6 +110,7 @@ import * as PTTL from '../commands/PTTL'; import * as PUBLISH from '../commands/PUBLISH'; import * as RENAME from '../commands/RENAME'; import * as RENAMENX from '../commands/RENAMENX'; +import * as RESTORE from '../commands/RESTORE'; import * as RPOP_COUNT from '../commands/RPOP_COUNT'; import * as RPOP from '../commands/RPOP'; import * as RPOPLPUSH from '../commands/RPOPLPUSH'; @@ -434,6 +435,8 @@ export default { rename: RENAME, RENAMENX, renameNX: RENAMENX, + RESTORE, + restore: RESTORE, RPOP_COUNT, rPopCount: RPOP_COUNT, RPOP, diff --git a/packages/client/lib/commands/RESTORE.spec.ts b/packages/client/lib/commands/RESTORE.spec.ts new file mode 100644 index 00000000000..89d42f3d4de --- /dev/null +++ b/packages/client/lib/commands/RESTORE.spec.ts @@ -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); +}); diff --git a/packages/client/lib/commands/RESTORE.ts b/packages/client/lib/commands/RESTORE.ts new file mode 100644 index 00000000000..d9ac11c424b --- /dev/null +++ b/packages/client/lib/commands/RESTORE.ts @@ -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';