1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-07 13:22:56 +03:00

some commands

This commit is contained in:
Leibale
2023-05-09 14:04:26 +03:00
parent a6c0d1dbb3
commit d59254e497
21 changed files with 295 additions and 235 deletions

View File

@@ -85,7 +85,10 @@ Some command arguments/replies have changed to align more closely to data types
- `HRANDFIELD_COUNT_WITHVALUES`: `Record<BlobString, BlobString>` -> `Array<{ field: BlobString; value: BlobString; }>` (it can return duplicates).
- `SCAN`, `HSCAN`, `SSCAN`, and `ZSCAN`: cursor type is `string` instead of `number`?
- `HSETNX`: `boolean` -> `number` [^boolean-to-number]
- `ZINTER`: instead of `client.ZINTER('11, { WEIGHTS: [1] })` use `client.ZINTER({ key: '1', weight: 1 }])`
- `ZINTER`: instead of `client.ZINTER('key', { WEIGHTS: [1] })` use `client.ZINTER({ key: 'key', weight: 1 }])`
- `ZINTER_WITHSCORES`: instead of `client.ZINTER_WITHSCORES('key', { WEIGHTS: [1] })` use `client.ZINTER_WITHSCORES({ key: 'key', weight: 1 }])`
- `ZUNION`: instead of `client.ZUNION('key', { WEIGHTS: [1] })` use `client.ZUNION({ key: 'key', weight: 1 }])`
- `ZUNION_WITHSCORES`: instead of `client.ZUNION_WITHSCORES('key', { WEIGHTS: [1] })` use `client.ZUNION_WITHSCORES({ key: 'key', weight: 1 }])`
- `SETNX`: `boolean` -> `number` [^boolean-to-number]
- `COPY`: `destinationDb` -> `DB`, `replace` -> `REPLACE`, `boolean` -> `number` [^boolean-to-number]
- `EXPIRE`: `boolean` -> `number` [^boolean-to-number]
@@ -104,6 +107,7 @@ Some command arguments/replies have changed to align more closely to data types
- `GEOSEARCH_WITH`/`GEORADIUS_WITH`: `GeoReplyWith` -> `GEO_REPLY_WITH` [^enum-to-constants]
- `GEORADIUSSTORE` -> `GEORADIUS_STORE`
- `GEORADIUSBYMEMBERSTORE` -> `GEORADIUSBYMEMBER_STORE`
- `XACK`: `boolean` -> `number` [^boolean-to-number]
[^enum-to-constants]: TODO

View File

@@ -2,7 +2,7 @@ import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import BITPOS from './BITPOS';
describe.only('BITPOS', () => {
describe('BITPOS', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(

View File

@@ -1,26 +1,26 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { RedisFlushModes, transformArguments } from './FLUSHALL';
import FLUSHALL, { REDIS_FLUSH_MODES } from './FLUSHALL';
describe('FLUSHALL', () => {
describe('transformArguments', () => {
it('default', () => {
assert.deepEqual(
transformArguments(),
FLUSHALL.transformArguments(),
['FLUSHALL']
);
});
it('ASYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.ASYNC),
FLUSHALL.transformArguments(REDIS_FLUSH_MODES.ASYNC),
['FLUSHALL', 'ASYNC']
);
});
it('SYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.SYNC),
FLUSHALL.transformArguments(REDIS_FLUSH_MODES.SYNC),
['FLUSHALL', 'SYNC']
);
});

View File

@@ -8,6 +8,8 @@ export const REDIS_FLUSH_MODES = {
export type RedisFlushModes = typeof REDIS_FLUSH_MODES[keyof typeof REDIS_FLUSH_MODES];
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: false,
transformArguments(mode?: RedisFlushModes) {
const args = ['FLUSHALL'];

View File

@@ -1,27 +1,27 @@
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import { RedisFlushModes } from './FLUSHALL';
import { transformArguments } from './FLUSHDB';
import FLUSHDB from './FLUSHDB';
import { REDIS_FLUSH_MODES } from './FLUSHALL';
describe('FLUSHDB', () => {
describe('transformArguments', () => {
it('default', () => {
assert.deepEqual(
transformArguments(),
FLUSHDB.transformArguments(),
['FLUSHDB']
);
});
it('ASYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.ASYNC),
FLUSHDB.transformArguments(REDIS_FLUSH_MODES.ASYNC),
['FLUSHDB', 'ASYNC']
);
});
it('SYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.SYNC),
FLUSHDB.transformArguments(REDIS_FLUSH_MODES.SYNC),
['FLUSHDB', 'SYNC']
);
});

View File

@@ -2,6 +2,8 @@ import { SimpleStringReply, Command } from '../RESP/types';
import { RedisFlushModes } from './FLUSHALL';
export default {
FIRST_KEY_INDEX: undefined,
IS_READ_ONLY: false,
transformArguments(mode?: RedisFlushModes) {
const args = ['FLUSHDB'];

View File

@@ -43,10 +43,6 @@ export function pushGeoSearchArguments(
args.push('BYBOX', by.width.toString(), by.height.toString(), by.unit);
}
if (options?.SORT) {
args.push(options.SORT);
}
pushGeoSearchOptions(args, options);
return args;

View File

@@ -13,7 +13,7 @@ describe('HEXISTS', () => {
testUtils.testAll('hExists', async client => {
assert.equal(
await client.hExists('key', 'field'),
false
0
);
}, {
client: GLOBAL.SERVERS.OPEN,

View File

@@ -13,7 +13,7 @@ describe('MOVE', () => {
testUtils.testAll('move', async client => {
assert.equal(
await client.move('key', 1),
1
0
);
}, {
client: GLOBAL.SERVERS.OPEN,

View File

@@ -1,4 +1,4 @@
import { RedisArgument, BlobStringReply, Command } from '../RESP/types';
import { RedisArgument, BlobStringReply, Command, CommandArguments } from '../RESP/types';
export interface XAddOptions {
TRIM?: {
@@ -9,17 +9,12 @@ export interface XAddOptions {
};
}
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(
key: RedisArgument,
export function pushXAddArguments(
args: CommandArguments,
id: RedisArgument,
message: Record<string, RedisArgument>,
options?: XAddOptions
) {
const args = ['XADD', key];
if (options?.TRIM) {
if (options.TRIM.strategy) {
args.push(options.TRIM.strategy);
@@ -43,6 +38,18 @@ export default {
}
return args;
}
export default {
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(
key: RedisArgument,
id: RedisArgument,
message: Record<string, RedisArgument>,
options?: XAddOptions
) {
return pushXAddArguments(['XADD', key], id, message, options);
},
transformReply: undefined as unknown as () => BlobStringReply
} as const satisfies Command;

View File

@@ -9,7 +9,7 @@ describe('XADD NOMKSTREAM', () => {
XADD_NOMKSTREAM.transformArguments('key', '*', {
field: 'value'
}),
['XADD', 'key', '*', 'field', 'value', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', '*', 'field', 'value']
);
});
@@ -19,7 +19,7 @@ describe('XADD NOMKSTREAM', () => {
'1': 'I',
'2': 'II'
}),
['XADD', 'key', '*', '1', 'I', '2', 'II', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', '*', '1', 'I', '2', 'II']
);
});
@@ -32,7 +32,7 @@ describe('XADD NOMKSTREAM', () => {
threshold: 1000
}
}),
['XADD', 'key', '1000', '*', 'field', 'value', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', '1000', '*', 'field', 'value']
);
});
@@ -46,7 +46,7 @@ describe('XADD NOMKSTREAM', () => {
threshold: 1000
}
}),
['XADD', 'key', 'MAXLEN', '1000', '*', 'field', 'value', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', 'MAXLEN', '1000', '*', 'field', 'value']
);
});
@@ -60,7 +60,7 @@ describe('XADD NOMKSTREAM', () => {
threshold: 1000
}
}),
['XADD', 'key', '=', '1000', '*', 'field', 'value', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', '=', '1000', '*', 'field', 'value']
);
});
@@ -74,17 +74,17 @@ describe('XADD NOMKSTREAM', () => {
limit: 1
}
}),
['XADD', 'key', '1000', 'LIMIT', '1', '*', 'field', 'value', 'NOMKSTREAM']
['XADD', 'key', 'NOMKSTREAM', '1000', 'LIMIT', '1', '*', 'field', 'value']
);
});
});
testUtils.testAll('xAddNoMkStream', async client => {
assert.equal(
typeof await client.xAddNoMkStream('key', '*', {
await client.xAddNoMkStream('key', '*', {
field: 'value'
}),
'string'
null
);
}, {
client: GLOBAL.SERVERS.OPEN,

View File

@@ -1,13 +1,16 @@
import { BlobStringReply, NullReply, Command } from '../RESP/types';
import XADD from './XADD';
import { RedisArgument, BlobStringReply, NullReply, Command } from '../RESP/types';
import { XAddOptions, pushXAddArguments } from './XADD';
export default {
FIRST_KEY_INDEX: XADD.FIRST_KEY_INDEX,
IS_READ_ONLY: XADD.IS_READ_ONLY,
transformArguments(...args: Parameters<typeof XADD.transformArguments>) {
const redisArgs = XADD.transformArguments(...args);
redisArgs.push('NOMKSTREAM');
return redisArgs;
FIRST_KEY_INDEX: 1,
IS_READ_ONLY: false,
transformArguments(
key: RedisArgument,
id: RedisArgument,
message: Record<string, RedisArgument>,
options?: XAddOptions
) {
return pushXAddArguments(['XADD', key, 'NOMKSTREAM'], id, message, options);
},
transformReply: undefined as unknown as () => BlobStringReply | NullReply
} as const satisfies Command;

View File

@@ -1,5 +1,5 @@
import { RedisArgument, ArrayReply, BlobStringReply, Command } from '../RESP/types';
import { transformDoubleArgument } from './generic-transformers';
import { ZKeys, pushZKeysArguments } from './generic-transformers';
export type ZInterKeyAndWeight = {
key: RedisArgument;
@@ -14,38 +14,10 @@ export interface ZInterOptions {
export function pushZInterArguments(
args: Array<RedisArgument>,
keys: ZInterKeys<RedisArgument> | ZInterKeys<ZInterKeyAndWeight>,
keys: ZKeys,
options?: ZInterOptions
) {
if (Array.isArray(keys)) {
args.push(keys.length.toString());
if (keys.length) {
if (isPlainKeys(keys)) {
args = args.concat(keys);
} else {
const start = args.length;
args[start + keys.length] = 'WEIGHTS';
for (let i = 0; i < keys.length; i++) {
const index = start + i;
args[index] = keys[i].key;
args[index + 1 + keys.length] = transformDoubleArgument(keys[i].weight);
}
}
}
} else {
args.push('1');
if (isPlainKey(keys)) {
args.push(keys);
} else {
args.push(
keys.key,
'WEIGHTS',
transformDoubleArgument(keys.weight)
);
}
}
pushZKeysArguments(args, keys);
if (options?.AGGREGATE) {
args.push('AGGREGATE', options.AGGREGATE);
@@ -54,14 +26,6 @@ export function pushZInterArguments(
return args;
}
function isPlainKey(key: RedisArgument | ZInterKeyAndWeight): key is RedisArgument {
return typeof key === 'string' || Buffer.isBuffer(key);
}
function isPlainKeys(keys: Array<RedisArgument> | Array<ZInterKeyAndWeight>): keys is Array<RedisArgument> {
return isPlainKey(keys[0]);
}
export default {
FIRST_KEY_INDEX: 2,
IS_READ_ONLY: true,

View File

@@ -13,22 +13,36 @@ describe('ZUNION', () => {
);
});
it('keys (array)', () => {
it('keys (Array<string>)', () => {
assert.deepEqual(
ZUNION.transformArguments(['1', '2']),
['ZUNION', '2', '1', '2']
);
});
it('with WEIGHTS', () => {
it('key & weight', () => {
assert.deepEqual(
ZUNION.transformArguments('key', {
WEIGHTS: [1]
ZUNION.transformArguments({
key: 'key',
weight: 1
}),
['ZUNION', '1', 'key', 'WEIGHTS', '1']
);
});
it('keys & weights', () => {
assert.deepEqual(
ZUNION.transformArguments([{
key: 'a',
weight: 1
}, {
key: 'b',
weight: 2
}]),
['ZUNION', '2', 'a', 'b', 'WEIGHTS', '1', '2']
);
});
it('with AGGREGATE', () => {
assert.deepEqual(
ZUNION.transformArguments('key', {

View File

@@ -1,8 +1,7 @@
import { ArrayReply, BlobStringReply, Command } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers';
import { ZKeys, pushZKeysArguments } from './generic-transformers';
export interface ZUnionOptions {
WEIGHTS?: Array<number>;
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
}
@@ -10,14 +9,10 @@ export default {
FIRST_KEY_INDEX: 2,
IS_READ_ONLY: true,
transformArguments(
keys: RedisVariadicArgument,
keys: ZKeys,
options?: ZUnionOptions
) {
const args = pushVariadicArgument(['ZUNION'], keys);
if (options?.WEIGHTS) {
args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString()));
}
const args = pushZKeysArguments(['ZUNION'], keys);
if (options?.AGGREGATE) {
args.push('AGGREGATE', options.AGGREGATE);

View File

@@ -49,7 +49,7 @@ describe('ZUNIONSTORE', () => {
testUtils.testAll('zUnionStore', async client => {
assert.equal(
await client.zUnionStore('destination', 'key'),
await client.zUnionStore('{tag}destination', '{tag}key'),
0
);
}, {

View File

@@ -1,8 +1,7 @@
import { RedisArgument, NumberReply, Command, } from '../RESP/types';
import { RedisVariadicArgument, pushVariadicArgument } from './generic-transformers';
import { ZKeys, pushZKeysArguments } from './generic-transformers';
export interface ZUnionOptions {
WEIGHTS?: Array<number>;
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
}
@@ -11,14 +10,10 @@ export default {
IS_READ_ONLY: false,
transformArguments(
destination: RedisArgument,
keys: RedisVariadicArgument,
keys: ZKeys,
options?: ZUnionOptions
) {
const args = pushVariadicArgument(['ZUNIONSTORE', destination], keys);
if (options?.WEIGHTS) {
args.push('WEIGHTS', ...options.WEIGHTS.map(weight => weight.toString()));
}
const args = pushZKeysArguments(['ZUNIONSTORE', destination], keys);
if (options?.AGGREGATE) {
args.push('AGGREGATE', options.AGGREGATE);

View File

@@ -1,48 +1,65 @@
// import { strict as assert } from 'assert';
// import testUtils, { GLOBAL } from '../test-utils';
// import { transformArguments } from './ZUNION_WITHSCORES';
import { strict as assert } from 'assert';
import testUtils, { GLOBAL } from '../test-utils';
import ZUNION_WITHSCORES from './ZUNION_WITHSCORES';
// describe('ZUNION WITHSCORES', () => {
// testUtils.isVersionGreaterThanHook([6, 2]);
describe('ZUNION WITHSCORES', () => {
testUtils.isVersionGreaterThanHook([6, 2]);
// describe('transformArguments', () => {
// it('key (string)', () => {
// assert.deepEqual(
// transformArguments('key'),
// ['ZUNION', '1', 'key', 'WITHSCORES']
// );
// });
describe('transformArguments', () => {
it('key (string)', () => {
assert.deepEqual(
ZUNION_WITHSCORES.transformArguments('key'),
['ZUNION', '1', 'key']
);
});
// it('keys (array)', () => {
// assert.deepEqual(
// transformArguments(['1', '2']),
// ['ZUNION', '2', '1', '2', 'WITHSCORES']
// );
// });
it('keys (Array<string>)', () => {
assert.deepEqual(
ZUNION_WITHSCORES.transformArguments(['1', '2']),
['ZUNION', '2', '1', '2']
);
});
// it('with WEIGHTS', () => {
// assert.deepEqual(
// transformArguments('key', {
// WEIGHTS: [1]
// }),
// ['ZUNION', '1', 'key', 'WEIGHTS', '1', 'WITHSCORES']
// );
// });
it('key & weight', () => {
assert.deepEqual(
ZUNION_WITHSCORES.transformArguments({
key: 'key',
weight: 1
}),
['ZUNION', '1', 'key', 'WEIGHTS', '1']
);
});
// it('with AGGREGATE', () => {
// assert.deepEqual(
// transformArguments('key', {
// AGGREGATE: 'SUM'
// }),
// ['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES']
// );
// });
// });
it('keys & weights', () => {
assert.deepEqual(
ZUNION_WITHSCORES.transformArguments([{
key: 'a',
weight: 1
}, {
key: 'b',
weight: 2
}]),
['ZUNION', '2', 'a', 'b', 'WEIGHTS', '1', '2']
);
});
// testUtils.testWithClient('client.zUnionWithScores', async client => {
// assert.deepEqual(
// await client.zUnionWithScores('key'),
// []
// );
// }, GLOBAL.SERVERS.OPEN);
// });
it('with AGGREGATE', () => {
assert.deepEqual(
ZUNION_WITHSCORES.transformArguments('key', {
AGGREGATE: 'SUM'
}),
['ZUNION', '1', 'key', 'AGGREGATE', 'SUM', 'WITHSCORES']
);
});
});
testUtils.testAll('zUnionWithScores', async client => {
assert.deepEqual(
await client.zUnionWithScores('key'),
[]
);
}, {
client: GLOBAL.SERVERS.OPEN,
cluster: GLOBAL.CLUSTERS.OPEN
});
});

View File

@@ -1,13 +1,14 @@
// import { RedisCommandArguments } from '.';
// import { transformArguments as transformZUnionArguments } from './ZUNION';
import { Command } from '../RESP/types';
import ZUNION from './ZUNION';
import { transformSortedSetReply } from './generic-transformers';
// export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION';
// export function transformArguments(...args: Parameters<typeof transformZUnionArguments>): RedisCommandArguments {
// return [
// ...transformZUnionArguments(...args),
// 'WITHSCORES'
// ];
// }
// export { transformSortedSetWithScoresReply as transformReply } from './generic-transformers';
export default {
FIRST_KEY_INDEX: ZUNION.FIRST_KEY_INDEX,
IS_READ_ONLY: ZUNION.IS_READ_ONLY,
transformArguments(...args: Parameters<typeof ZUNION['transformArguments']>) {
const redisArgs = ZUNION.transformArguments(...args);
redisArgs.push('WITHSCORES');
return redisArgs;
},
transformReply: transformSortedSetReply
} as const satisfies Command;

View File

@@ -432,3 +432,57 @@ export function transformRangeReply([start, end]: RawRangeReply): RangeReply {
end
};
}
export type ZKeyAndWeight = {
key: RedisArgument;
weight: number;
};
export type ZVariadicKeys<T> = T | [T, ...Array<T>];
export type ZKeys = ZVariadicKeys<RedisArgument> | ZVariadicKeys<ZKeyAndWeight>;
export function pushZKeysArguments(
args: CommandArguments,
keys: ZKeys
) {
if (Array.isArray(keys)) {
args.push(keys.length.toString());
if (keys.length) {
if (isPlainKeys(keys)) {
args = args.concat(keys);
} else {
const start = args.length;
args[start + keys.length] = 'WEIGHTS';
for (let i = 0; i < keys.length; i++) {
const index = start + i;
args[index] = keys[i].key;
args[index + 1 + keys.length] = transformDoubleArgument(keys[i].weight);
}
}
}
} else {
args.push('1');
if (isPlainKey(keys)) {
args.push(keys);
} else {
args.push(
keys.key,
'WEIGHTS',
transformDoubleArgument(keys.weight)
);
}
}
return args;
}
function isPlainKey(key: RedisArgument | ZKeyAndWeight): key is RedisArgument {
return typeof key === 'string' || Buffer.isBuffer(key);
}
function isPlainKeys(keys: Array<RedisArgument> | Array<ZKeyAndWeight>): keys is Array<RedisArgument> {
return isPlainKey(keys[0]);
}

View File

@@ -68,7 +68,12 @@ import GETDEL from './GETDEL';
import GETEX from './GETEX';
import GETRANGE from './GETRANGE';
import GETSET from './GETSET';
import EXISTS from './EXISTS';
import EXPIRE from './EXPIRE';
import EXPIREAT from './EXPIREAT';
import EXPIRETIME from './EXPIRETIME';
import FLUSHALL from './FLUSHALL';
import FLUSHDB from './FLUSHDB';
import HDEL from './HDEL';
import HEXISTS from './HEXISTS';
import HGET from './HGET';
@@ -119,10 +124,6 @@ import OBJECT_FREQ from './OBJECT_FREQ';
import OBJECT_IDLETIME from './OBJECT_IDLETIME';
import OBJECT_REFCOUNT from './OBJECT_REFCOUNT';
import PERSIST from './PERSIST';
import EXISTS from './EXISTS';
import EXPIRE from './EXPIRE';
import EXPIREAT from './EXPIREAT';
import EXPIRETIME from './EXPIRETIME';
import PEXPIRE from './PEXPIRE';
import PEXPIREAT from './PEXPIREAT';
import PEXPIRETIME from './PEXPIRETIME';
@@ -212,6 +213,7 @@ import ZREMRANGEBYRANK from './ZREMRANGEBYRANK';
import ZREVRANK from './ZREVRANK';
import ZSCAN from './ZSCAN';
import ZSCORE from './ZSCORE';
import ZUNION_WITHSCORES from './ZUNION_WITHSCORES';
import ZUNION from './ZUNION';
import ZUNIONSTORE from './ZUNIONSTORE';
import { Command } from '../RESP/types';
@@ -311,6 +313,18 @@ export default {
del: DEL,
DUMP,
dump: DUMP,
EXISTS,
exists: EXISTS,
EXPIRE,
expire: EXPIRE,
EXPIREAT,
expireAt: EXPIREAT,
EXPIRETIME,
expireTime: EXPIRETIME,
FLUSHALL,
flushAll: FLUSHALL,
FLUSHDB,
flushDb: FLUSHDB,
GEOADD,
geoAdd: GEOADD,
GEODIST,
@@ -357,8 +371,6 @@ export default {
getRange: GETRANGE,
GETSET,
getSet: GETSET,
FLUSHALL,
flushAll: FLUSHALL,
HDEL,
hDel: HDEL,
HEXISTS,
@@ -457,14 +469,6 @@ export default {
objectRefCount: OBJECT_REFCOUNT,
PERSIST,
persist: PERSIST,
EXISTS,
exists: EXISTS,
EXPIRE,
expire: EXPIRE,
EXPIREAT,
expireAt: EXPIREAT,
EXPIRETIME,
expireTime: EXPIRETIME,
PEXPIRE,
pExpire: PEXPIRE,
PEXPIREAT,
@@ -646,6 +650,8 @@ export default {
zScan: ZSCAN,
ZSCORE,
zScore: ZSCORE,
ZUNION_WITHSCORES,
zUnionWithScores: ZUNION_WITHSCORES,
ZUNION,
zUnion: ZUNION,
ZUNIONSTORE,