From ff07bbf3d37ff4d2a75a2c4dbcaf8c20ddb425f0 Mon Sep 17 00:00:00 2001 From: Leibale Date: Thu, 27 Jul 2023 11:23:34 -0400 Subject: [PATCH] WIP --- docs/v4-to-v5.md | 1 + packages/client/lib/commands/PING.ts | 4 +- .../time-series/lib/commands/ALTER.spec.ts | 2 +- .../time-series/lib/commands/MADD.spec.ts | 2 +- .../time-series/lib/commands/MGET.spec.ts | 64 +++--- packages/time-series/lib/commands/MGET.ts | 52 ++--- .../lib/commands/MGET_WITHLABELS.spec.ts | 64 +++--- .../lib/commands/MGET_WITHLABELS.ts | 49 ++--- .../time-series/lib/commands/MRANGE.spec.ts | 88 ++++---- packages/time-series/lib/commands/MRANGE.ts | 81 ++++++-- .../lib/commands/MRANGE_WITHLABELS.spec.ts | 91 ++++----- .../lib/commands/MRANGE_WITHLABELS.ts | 47 +++-- .../lib/commands/MREVRANGE.spec.ts | 84 ++++---- .../time-series/lib/commands/MREVRANGE.ts | 28 +-- .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 90 +++++---- .../lib/commands/MREVRANGE_WITHLABELS.ts | 30 +-- .../lib/commands/QUERYINDEX.spec.ts | 52 ++--- .../time-series/lib/commands/QUERYINDEX.ts | 21 +- .../time-series/lib/commands/RANGE.spec.ts | 64 +++--- packages/time-series/lib/commands/RANGE.ts | 127 ++++++++++-- .../time-series/lib/commands/REVRANGE.spec.ts | 190 +++++++++--------- packages/time-series/lib/commands/REVRANGE.ts | 31 +-- packages/time-series/lib/commands/index.ts | 106 +++++----- packages/time-series/lib/index.ts | 4 +- packages/time-series/package.json | 3 +- 25 files changed, 745 insertions(+), 630 deletions(-) diff --git a/docs/v4-to-v5.md b/docs/v4-to-v5.md index 66d94fb0a8..c500815cd6 100644 --- a/docs/v4-to-v5.md +++ b/docs/v4-to-v5.md @@ -181,6 +181,7 @@ Some command arguments/replies have changed to align more closely to data types - `JSON.ARRLEN`: `path` argument moved to `{ path: string; }` [^future-proofing] - `JSON.DEL`: `path` argument moved to `{ path: string; }` [^future-proofing] - `JSON.FORGET`: `path` argument moved to `{ path: string; }` [^future-proofing] +- `TS.[M][REV]RANGE`: `enum TimeSeriesBucketTimestamp` -> `const TIME_SERIES_BUCKET_TIMESTAMP` [^enum-to-constants], `enum TimeSeriesReducers` -> `const TIME_SERIES_REDUCERS` [^enum-to-constants], the `ALIGN` argument has been moved into `AGGREGRATION` [^enum-to-constants]: TODO diff --git a/packages/client/lib/commands/PING.ts b/packages/client/lib/commands/PING.ts index fabdfae650..7f6fd31047 100644 --- a/packages/client/lib/commands/PING.ts +++ b/packages/client/lib/commands/PING.ts @@ -1,4 +1,4 @@ -import { RedisArgument, SimpleStringReply, Command } from '../RESP/types'; +import { RedisArgument, SimpleStringReply, BlobStringReply, Command } from '../RESP/types'; export default { FIRST_KEY_INDEX: undefined, @@ -11,5 +11,5 @@ export default { return args; }, - transformReply: undefined as unknown as () => SimpleStringReply + transformReply: undefined as unknown as () => SimpleStringReply | BlobStringReply } as const satisfies Command; diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 6889cd2b2c..d09d40db66 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -64,7 +64,7 @@ describe('TS.ALTER', () => { testUtils.testWithClient('client.ts.alter', async client => { const [, reply] = await Promise.all([ client.ts.create('key'), - client.ts.alter('key', { RETENTION: 1 }) + client.ts.alter('key') ]); assert.equal(reply, 'OK'); diff --git a/packages/time-series/lib/commands/MADD.spec.ts b/packages/time-series/lib/commands/MADD.spec.ts index 119fcfce60..d9585ffbe7 100644 --- a/packages/time-series/lib/commands/MADD.spec.ts +++ b/packages/time-series/lib/commands/MADD.spec.ts @@ -3,7 +3,7 @@ import testUtils, { GLOBAL } from '../test-utils'; import MADD from './MADD'; import { SimpleError } from '@redis/client/lib/errors'; -describe.only('TS.MADD', () => { +describe('TS.MADD', () => { it('transformArguments', () => { assert.deepEqual( MADD.transformArguments([{ diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts index c932ff704a..810e54068b 100644 --- a/packages/time-series/lib/commands/MGET.spec.ts +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -1,40 +1,40 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET'; +import MGET from './MGET'; describe('TS.MGET', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'FILTER', 'label=value'] - ); - }); - - it('with LATEST', () => { - assert.deepEqual( - transformArguments('label=value', { - LATEST: true - }), - ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + MGET.transformArguments('label=value'), + ['TS.MGET', 'FILTER', 'label=value'] + ); }); - testUtils.testWithClient('client.ts.mGet', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value' } - }); + it('with LATEST', () => { + assert.deepEqual( + MGET.transformArguments('label=value', { + LATEST: true + }), + ['TS.MGET', 'LATEST', 'FILTER', 'label=value'] + ); + }); + }); - assert.deepEqual( - await client.ts.mGet('label=value'), - [{ - key: 'key', - sample: { - timestamp: 0, - value: 0 - } - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.mGet', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mGet('label=value') + ]); + + assert.deepEqual(reply, [{ + key: 'key', + sample: { + timestamp: 0, + value: 0 + } + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 67315722eb..861c23b1e4 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,31 +1,31 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Filter, pushFilterArgument, pushLatestArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { CommandArguments, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export interface MGetOptions { - LATEST?: boolean; +export interface TsMGetOptions { + LATEST?: boolean; } -export function transformArguments(filter: Filter, options?: MGetOptions): RedisCommandArguments { +export function pushLatestArgument(args: CommandArguments, latest?: boolean) { + if (latest) { + args.push('LATEST'); + } + + return args; +} + +export function pushFilterArgument(args: CommandArguments, filter: RedisVariadicArgument) { + args.push('FILTER'); + return pushVariadicArguments(args, filter); +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument, options?: TsMGetOptions) { const args = pushLatestArgument(['TS.MGET'], options?.LATEST); return pushFilterArgument(args, filter); -} - -export type MGetRawReply = Array<[ - key: string, - labels: RawLabels, - sample: SampleRawReply -]>; - -export interface MGetReply { - key: string, - sample: SampleReply -} - -export function transformReply(reply: MGetRawReply): Array { - return reply.map(([key, _, sample]) => ({ - key, - sample: transformSampleReply(sample) - })); -} + }, + // TODO + // transformSampleReply + transformReply: undefined as unknown as () => any +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts index 67c3a4c7b3..1a2d223cb7 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.spec.ts @@ -1,39 +1,41 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MGET_WITHLABELS'; +import MGET_WITHLABELS from './MGET_WITHLABELS'; describe('TS.MGET_WITHLABELS', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] - ); - }); - - it('with SELECTED_LABELS', () => { - assert.deepEqual( - transformArguments('label=value', { SELECTED_LABELS: 'label' }), - ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + MGET_WITHLABELS.transformArguments('label=value'), + ['TS.MGET', 'WITHLABELS', 'FILTER', 'label=value'] + ); }); - testUtils.testWithClient('client.ts.mGetWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value' } - }); + it('with SELECTED_LABELS', () => { + assert.deepEqual( + MGET_WITHLABELS.transformArguments('label=value', { + SELECTED_LABELS: 'label' + }), + ['TS.MGET', 'SELECTED_LABELS', 'label', 'FILTER', 'label=value'] + ); + }); + }); - assert.deepEqual( - await client.ts.mGetWithLabels('label=value'), - [{ - key: 'key', - labels: { label: 'value'}, - sample: { - timestamp: 0, - value: 0 - } - }] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.mGetWithLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mGetWithLabels('label=value') + ]); + + assert.deepEqual(reply, [{ + key: 'key', + labels: { label: 'value' }, + sample: { + timestamp: 0, + value: 0 + } + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index 232c17a0ad..273e71729b 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -1,37 +1,20 @@ -import { - SelectedLabels, - pushWithLabelsArgument, - Labels, - transformLablesReply, - transformSampleReply, - Filter, - pushFilterArgument -} from '.'; -import { MGetOptions, MGetRawReply, MGetReply } from './MGET'; -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { TsMGetOptions, pushLatestArgument, pushFilterArgument } from './MGET'; +import { pushWithLabelsArgument } from '.'; -export const IS_READ_ONLY = true; - -interface MGetWithLabelsOptions extends MGetOptions { - SELECTED_LABELS?: SelectedLabels; +export interface TsMGetWithLabelsOptions extends TsMGetOptions { + SELECTED_LABELS?: RedisVariadicArgument; } -export function transformArguments( - filter: Filter, - options?: MGetWithLabelsOptions -): RedisCommandArguments { - const args = pushWithLabelsArgument(['TS.MGET'], options?.SELECTED_LABELS); +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument, options?: TsMGetWithLabelsOptions) { + let args = pushLatestArgument(['TS.MGET'], options?.LATEST); + args = pushWithLabelsArgument(args, options?.SELECTED_LABELS); return pushFilterArgument(args, filter); -} - -export interface MGetWithLabelsReply extends MGetReply { - labels: Labels; -}; - -export function transformReply(reply: MGetRawReply): Array { - return reply.map(([key, labels, sample]) => ({ - key, - labels: transformLablesReply(labels), - sample: transformSampleReply(sample) - })); -} + }, + // TODO + transformReply: undefined as unknown as () => any +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index 5f5a7930e5..32cdd37c6f 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -1,50 +1,54 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MRANGE'; +import MRANGE from './MRANGE'; import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; describe('TS.MRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MRANGE.transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + [ + 'TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM' + ] + ); + }); - testUtils.testWithClient('client.ts.mRange', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRange', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { + label: 'value' + } + }), + client.ts.mRange('-', '+', 'label=value', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ts.mRange('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + key: 'key', + samples: [{ + timestamp: 0, + value: 0 + }] + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MRANGE.ts b/packages/time-series/lib/commands/MRANGE.ts index d589ac0332..8fbbe7cb81 100644 --- a/packages/time-series/lib/commands/MRANGE.ts +++ b/packages/time-series/lib/commands/MRANGE.ts @@ -1,21 +1,68 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; +import { RedisArgument, Command, CommandArguments } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; +import { Timestamp } from '.'; +import { TsRangeOptions, pushRangeArguments } from './RANGE'; +import { pushFilterArgument } from './MGET'; -export const IS_READ_ONLY = true; +export const TIME_SERIES_REDUCERS = { + AVG: 'AVG', + SUM: 'SUM', + MIN: 'MIN', + MAX: 'MAX', + RANGE: 'RANGE', + COUNT: 'COUNT', + STD_P: 'STD.P', + STD_S: 'STD.S', + VAR_P: 'VAR.P', + VAR_S: 'VAR.S' +}; -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: Filter, - options?: MRangeOptions -): RedisCommandArguments { - return pushMRangeArguments( - ['TS.MRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); +export type TimeSeriesReducers = typeof TIME_SERIES_REDUCERS[keyof typeof TIME_SERIES_REDUCERS]; + +export interface TsMRangeOptions extends TsRangeOptions { + GROUPBY?: { + label: RedisArgument; + reducer: TimeSeriesReducers; + }; } -export { transformMRangeReply as transformReply } from '.'; +export function pushGroupByArgument(args: CommandArguments, groupBy?: TsMRangeOptions['GROUPBY']) { + if (groupBy) { + args.push( + 'GROUPBY', + groupBy.label, + 'REDUCE', + groupBy.reducer + ); + } + + return args; +} + +export function transformMRangeArguments( + command: RedisArgument, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: RedisVariadicArgument, + options?: TsMRangeOptions +) { + let args = pushRangeArguments( + [command], + fromTimestamp, + toTimestamp, + options + ); + + args = pushFilterArgument(args, filter); + + return pushGroupByArgument(args, options?.GROUPBY); +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: transformMRangeArguments.bind(undefined, 'TS.MRANGE'), + // TODO + // export { transformMRangeReply as transformReply } from '.'; + transformReply: undefined as unknown as () => any +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index 729e32f19a..4997634958 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -1,52 +1,53 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MRANGE_WITHLABELS'; -import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; +import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; +import { TIME_SERIES_REDUCERS } from './MRANGE'; describe('TS.MRANGE_WITHLABELS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - SELECTED_LABELS: ['label'], - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); + it('transformArguments', () => { + assert.deepEqual( + MRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + SELECTED_LABELS: ['label'], + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TIME_SERIES_REDUCERS.SUM + }, + }), + ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] + ); + }); + + testUtils.testWithClient('client.ts.mRangeWithLabels', async client => { + await client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } }); - testUtils.testWithClient('client.ts.mRangeWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); - - assert.deepEqual( - await client.ts.mRangeWithLabels('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - labels: { label: 'value' }, - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual( + await client.ts.mRangeWithLabels('-', '+', 'label=value', { + COUNT: 1 + }), + [{ + key: 'key', + labels: { label: 'value' }, + samples: [{ + timestamp: 0, + value: 0 + }] + }] + ); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts index 16b7920e82..0688573878 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.ts @@ -1,21 +1,32 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments } from '.'; +import { RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers'; +import { TsMRangeOptions, pushGroupByArgument } from './MRANGE'; +import { Timestamp, pushWithLabelsArgument } from '.'; +import { pushFilterArgument } from './MGET'; +import { pushRangeArguments } from './RANGE'; -export const IS_READ_ONLY = true; - -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: string | Array, - options?: MRangeWithLabelsOptions -): RedisCommandArguments { - return pushMRangeWithLabelsArguments( - ['TS.MRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); +export interface TsMRangeWithLabelsOptions extends TsMRangeOptions { + SELECTED_LABELS?: RedisVariadicArgument; } -export { transformMRangeWithLabelsReply as transformReply } from '.'; +export function transformMRangeWithLabelsArguments( + command: RedisArgument, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + filter: RedisVariadicArgument, + options?: TsMRangeWithLabelsOptions +) { + let args = pushRangeArguments([command], fromTimestamp, toTimestamp, options); + args = pushWithLabelsArgument(args, options?.SELECTED_LABELS); + args = pushFilterArgument(args, filter); + return pushGroupByArgument(args, options?.GROUPBY); +} + +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: transformMRangeWithLabelsArguments.bind(undefined, 'TS.MRANGE'), + // TODO + // export { transformMRangeWithLabelsReply as transformReply } from '.'; + transformReply: undefined as unknown as () => any +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index e5dcf5f5f7..69937fe4d5 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -4,47 +4,49 @@ import { transformArguments } from './MREVRANGE'; import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; describe('TS.MREVRANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + [ + 'TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM' + ] + ); + }); - testUtils.testWithClient('client.ts.mRevRange', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRevRange', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRange('-', '+', 'label=value', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ts.mRevRange('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + key: 'key', + samples: [{ + timestamp: 0, + value: 0 + }] + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MREVRANGE.ts b/packages/time-series/lib/commands/MREVRANGE.ts index 127c052ffe..c64c37118e 100644 --- a/packages/time-series/lib/commands/MREVRANGE.ts +++ b/packages/time-series/lib/commands/MREVRANGE.ts @@ -1,21 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { MRangeOptions, Timestamp, pushMRangeArguments, Filter } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import MRANGE, { transformMRangeArguments } from './MRANGE'; -export const IS_READ_ONLY = true; - -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: Filter, - options?: MRangeOptions -): RedisCommandArguments { - return pushMRangeArguments( - ['TS.MREVRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); -} - -export { transformMRangeReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: MRANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: MRANGE.IS_READ_ONLY, + transformArguments: transformMRangeArguments.bind(undefined, 'TS.MREVRANGE'), + transformReply: MRANGE.transformReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index a3e50b7f26..5371a00983 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -1,52 +1,54 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './MREVRANGE_WITHLABELS'; +import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; import { TimeSeriesAggregationType, TimeSeriesReducers } from '.'; describe('TS.MREVRANGE_WITHLABELS', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('-', '+', 'label=value', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 0, - max: 1 - }, - SELECTED_LABELS: ['label'], - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - }, - GROUPBY: { - label: 'label', - reducer: TimeSeriesReducers.SUM - }, - }), - ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', - 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + MREVRANGE_WITHLABELS.transformArguments('-', '+', 'label=value', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 0, + max: 1 + }, + SELECTED_LABELS: ['label'], + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 1 + }, + GROUPBY: { + label: 'label', + reducer: TimeSeriesReducers.SUM + }, + }), + [ + 'TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM' + ] + ); + }); - testUtils.testWithClient('client.ts.mRevRangeWithLabels', async client => { - await client.ts.add('key', 0, 0, { - LABELS: { label: 'value'} - }); + testUtils.testWithClient('client.ts.mRevRangeWithLabels', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 0, 0, { + LABELS: { label: 'value' } + }), + client.ts.mRevRangeWithLabels('-', '+', 'label=value', { + COUNT: 1 + }) + ]); - assert.deepEqual( - await client.ts.mRevRangeWithLabels('-', '+', 'label=value', { - COUNT: 1 - }), - [{ - key: 'key', - labels: { label: 'value' }, - samples: [{ - timestamp: 0, - value: 0 - }] - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + key: 'key', + labels: { label: 'value' }, + samples: [{ + timestamp: 0, + value: 0 + }] + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts index 21a0ebc69c..281b3235d8 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.ts @@ -1,21 +1,11 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Timestamp, MRangeWithLabelsOptions, pushMRangeWithLabelsArguments, Filter } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import { transformMRangeWithLabelsArguments } from './MRANGE_WITHLABELS'; -export const IS_READ_ONLY = true; - -export function transformArguments( - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - filters: Filter, - options?: MRangeWithLabelsOptions -): RedisCommandArguments { - return pushMRangeWithLabelsArguments( - ['TS.MREVRANGE'], - fromTimestamp, - toTimestamp, - filters, - options - ); -} - -export { transformMRangeWithLabelsReply as transformReply } from '.'; +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments: transformMRangeWithLabelsArguments.bind(undefined, 'TS.MREVRANGE'), + // TODO + // export { transformMRangeWithLabelsReply as transformReply } from '.'; + transformReply: undefined as unknown as () => any +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/QUERYINDEX.spec.ts b/packages/time-series/lib/commands/QUERYINDEX.spec.ts index 68e473918a..86160ebd7d 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.spec.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.spec.ts @@ -1,34 +1,34 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './QUERYINDEX'; +import QUERYINDEX from './QUERYINDEX'; describe('TS.QUERYINDEX', () => { - describe('transformArguments', () => { - it('single filter', () => { - assert.deepEqual( - transformArguments('*'), - ['TS.QUERYINDEX', '*'] - ); - }); - - it('multiple filters', () => { - assert.deepEqual( - transformArguments(['a=1', 'b=2']), - ['TS.QUERYINDEX', 'a=1', 'b=2'] - ); - }); + describe('transformArguments', () => { + it('single filter', () => { + assert.deepEqual( + QUERYINDEX.transformArguments('*'), + ['TS.QUERYINDEX', '*'] + ); }); - testUtils.testWithClient('client.ts.queryIndex', async client => { - await client.ts.create('key', { - LABELS: { - label: 'value' - } - }); + it('multiple filters', () => { + assert.deepEqual( + QUERYINDEX.transformArguments(['a=1', 'b=2']), + ['TS.QUERYINDEX', 'a=1', 'b=2'] + ); + }); + }); - assert.deepEqual( - await client.ts.queryIndex('label=value'), - ['key'] - ); - }, GLOBAL.SERVERS.OPEN); + testUtils.testWithClient('client.ts.queryIndex', async client => { + const [, reply] = await Promise.all([ + client.ts.create('key', { + LABELS: { + label: 'value' + } + }), + client.ts.queryIndex('label=value') + ]); + + assert.deepEqual(reply, ['key']); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/QUERYINDEX.ts b/packages/time-series/lib/commands/QUERYINDEX.ts index b228578918..86c2a3c5a7 100644 --- a/packages/time-series/lib/commands/QUERYINDEX.ts +++ b/packages/time-series/lib/commands/QUERYINDEX.ts @@ -1,11 +1,14 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -import { Filter } from '.'; +import { ArrayReply, BlobStringReply, SetReply, Command } from '@redis/client/dist/lib/RESP/types'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; -export const IS_READ_ONLY = true; - -export function transformArguments(filter: Filter): RedisCommandArguments { +export default { + FIRST_KEY_INDEX: undefined, + IS_READ_ONLY: true, + transformArguments(filter: RedisVariadicArgument) { return pushVariadicArguments(['TS.QUERYINDEX'], filter); -} - -export declare function transformReply(): Array; + }, + transformReply: { + 2: undefined as unknown as () => ArrayReply, + 3: undefined as unknown as () => SetReply + } +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index bc3c549803..2c6c27f7a2 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -1,38 +1,40 @@ import { strict as assert } from 'assert'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './RANGE'; -import { TimeSeriesAggregationType } from '.'; +import RANGE from './RANGE'; +import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; describe('TS.RANGE', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 1, - max: 2 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - ['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1'] - ); - }); + it('transformArguments', () => { + assert.deepEqual( + RANGE.transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 1 + } + }), + [ + 'TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' + ] + ); + }); - testUtils.testWithClient('client.ts.range', async client => { - await client.ts.add('key', 1, 2); + testUtils.testWithClient('client.ts.range', async client => { + const [, reply] = await Promise.all([ + client.ts.add('key', 1, 2), + client.ts.range('key', '-', '+') + ]); - assert.deepEqual( - await client.ts.range('key', '-', '+'), - [{ - timestamp: 1, - value: 2 - }] - ); - }, GLOBAL.SERVERS.OPEN); + assert.deepEqual(reply, [{ + timestamp: 1, + value: 2 + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/RANGE.ts b/packages/time-series/lib/commands/RANGE.ts index e6ce256bbe..0bbe55aeba 100644 --- a/packages/time-series/lib/commands/RANGE.ts +++ b/packages/time-series/lib/commands/RANGE.ts @@ -1,24 +1,117 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; +import { CommandArguments, RedisArgument, Command } from '@redis/client/dist/lib/RESP/types'; +import { Timestamp, transformTimestampArgument } from '.'; +import { TimeSeriesAggregationType } from './CREATERULE'; -export const FIRST_KEY_INDEX = 1; +export const TIME_SERIES_BUCKET_TIMESTAMP = { + LOW: '-', + MIDDLE: '~', + END: '+' +}; -export const IS_READ_ONLY = true; +export type TimeSeriesBucketTimestamp = typeof TIME_SERIES_BUCKET_TIMESTAMP[keyof typeof TIME_SERIES_BUCKET_TIMESTAMP]; -export function transformArguments( - key: string, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - options?: RangeOptions -): RedisCommandArguments { - return pushRangeArguments( - ['TS.RANGE', key], - fromTimestamp, - toTimestamp, - options +export interface TsRangeOptions { + LATEST?: boolean; + FILTER_BY_TS?: Array; + FILTER_BY_VALUE?: { + min: number; + max: number; + }; + COUNT?: number; + ALIGN?: Timestamp; + AGGREGATION?: { + ALIGN?: Timestamp; + type: TimeSeriesAggregationType; + timeBucket: Timestamp; + BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp; + EMPTY?: boolean; + }; +} + +export function pushRangeArguments( + args: CommandArguments, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: TsRangeOptions +) { + args.push( + transformTimestampArgument(fromTimestamp), + transformTimestampArgument(toTimestamp) + ); + + if (options?.LATEST) { + args.push('LATEST'); + } + + if (options?.FILTER_BY_TS) { + args.push('FILTER_BY_TS'); + for (const timestamp of options.FILTER_BY_TS) { + args.push(transformTimestampArgument(timestamp)); + } + } + + if (options?.FILTER_BY_VALUE) { + args.push( + 'FILTER_BY_VALUE', + options.FILTER_BY_VALUE.min.toString(), + options.FILTER_BY_VALUE.max.toString() ); + } + + if (options?.COUNT !== undefined) { + args.push('COUNT', options.COUNT.toString()); + } + + if (options?.AGGREGATION) { + if (options?.ALIGN !== undefined) { + args.push('ALIGN', transformTimestampArgument(options.ALIGN)); + } + + args.push( + 'AGGREGATION', + options.AGGREGATION.type, + transformTimestampArgument(options.AGGREGATION.timeBucket) + ); + + if (options.AGGREGATION.BUCKETTIMESTAMP) { + args.push( + 'BUCKETTIMESTAMP', + options.AGGREGATION.BUCKETTIMESTAMP + ); + } + + if (options.AGGREGATION.EMPTY) { + args.push('EMPTY'); + } + } + + return args; } -export function transformReply(reply: Array): Array { - return transformRangeReply(reply); +export function transformRangeArguments( + command: RedisArgument, + key: RedisArgument, + fromTimestamp: Timestamp, + toTimestamp: Timestamp, + options?: TsRangeOptions +) { + return pushRangeArguments( + [command, key], + fromTimestamp, + toTimestamp, + options + ); } + +export default { + FIRST_KEY_INDEX: 1, + IS_READ_ONLY: true, + transformArguments: transformRangeArguments.bind(undefined, 'TS.RANGE'), + // TODO + // import { SampleReply, transformRangeReply } from '.'; + // export function transformReply(reply: Array): Array { + // return transformRangeReply(reply); + // } + transformReply: undefined as unknown as () => any +} as const satisfies Command; + diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index 6fd0f6a45a..3138e9327e 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -4,103 +4,101 @@ import { transformArguments } from './REVRANGE'; import { TimeSeriesAggregationType } from '.'; describe('TS.REVRANGE', () => { - describe('transformArguments', () => { - it('without options', () => { - assert.deepEqual( - transformArguments('key', '-', '+'), - ['TS.REVRANGE', 'key', '-', '+'] - ); - }); - - it('with FILTER_BY_TS', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0] - }), - ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0'] - ); - }); - - it('with FILTER_BY_VALUE', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_VALUE: { - min: 1, - max: 2 - } - }), - ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_VALUE', '1', '2'] - ); - }); - - it('with COUNT', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - COUNT: 1 - }), - ['TS.REVRANGE', 'key', '-', '+', 'COUNT', '1'] - ); - }); - - it('with ALIGN', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - ALIGN: '-' - }), - ['TS.REVRANGE', 'key', '-', '+', 'ALIGN', '-'] - ); - }); - - it('with AGGREGATION', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'AVG', '1'] - ); - }); - - it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { - assert.deepEqual( - transformArguments('key', '-', '+', { - FILTER_BY_TS: [0], - FILTER_BY_VALUE: { - min: 1, - max: 2 - }, - COUNT: 1, - ALIGN: '-', - AGGREGATION: { - type: TimeSeriesAggregationType.AVERAGE, - timeBucket: 1 - } - }), - [ - 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' - ] - ); - }); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key', '-', '+'), + ['TS.REVRANGE', 'key', '-', '+'] + ); }); - testUtils.testWithClient('client.ts.revRange', async client => { - await Promise.all([ - client.ts.add('key', 0, 1), - client.ts.add('key', 1, 2) - ]); + it('with FILTER_BY_TS', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_TS: [0] + }), + ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0'] + ); + }); - assert.deepEqual( - await client.ts.revRange('key', '-', '+'), - [{ - timestamp: 1, - value: 2 - }, { - timestamp: 0, - value: 1 - }] - ); - }, GLOBAL.SERVERS.OPEN); + it('with FILTER_BY_VALUE', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_VALUE: { + min: 1, + max: 2 + } + }), + ['TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_VALUE', '1', '2'] + ); + }); + + it('with COUNT', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + COUNT: 1 + }), + ['TS.REVRANGE', 'key', '-', '+', 'COUNT', '1'] + ); + }); + + it('with ALIGN', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + ALIGN: '-' + }), + ['TS.REVRANGE', 'key', '-', '+', 'ALIGN', '-'] + ); + }); + + it('with AGGREGATION', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 1 + } + }), + ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'AVG', '1'] + ); + }); + + it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { + assert.deepEqual( + transformArguments('key', '-', '+', { + FILTER_BY_TS: [0], + FILTER_BY_VALUE: { + min: 1, + max: 2 + }, + COUNT: 1, + ALIGN: '-', + AGGREGATION: { + type: TimeSeriesAggregationType.AVERAGE, + timeBucket: 1 + } + }), + [ + 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' + ] + ); + }); + }); + + testUtils.testWithClient('client.ts.revRange', async client => { + const [, , reply] = await Promise.all([ + client.ts.add('key', 0, 1), + client.ts.add('key', 1, 2), + client.ts.revRange('key', '-', '+') + ]); + + assert.deepEqual(reply, [{ + timestamp: 1, + value: 2 + }, { + timestamp: 0, + value: 1 + }]); + }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/REVRANGE.ts b/packages/time-series/lib/commands/REVRANGE.ts index 9179756b5d..1097223080 100644 --- a/packages/time-series/lib/commands/REVRANGE.ts +++ b/packages/time-series/lib/commands/REVRANGE.ts @@ -1,24 +1,9 @@ -import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { RangeOptions, Timestamp, pushRangeArguments, SampleRawReply, SampleReply, transformRangeReply } from '.'; +import { Command } from '@redis/client/dist/lib/RESP/types'; +import RANGE, { transformRangeArguments } from './RANGE'; -export const FIRST_KEY_INDEX = 1; - -export const IS_READ_ONLY = true; - -export function transformArguments( - key: string, - fromTimestamp: Timestamp, - toTimestamp: Timestamp, - options?: RangeOptions -): RedisCommandArguments { - return pushRangeArguments( - ['TS.REVRANGE', key], - fromTimestamp, - toTimestamp, - options - ); -} - -export function transformReply(reply: Array): Array { - return transformRangeReply(reply); -} +export default { + FIRST_KEY_INDEX: RANGE.FIRST_KEY_INDEX, + IS_READ_ONLY: RANGE.IS_READ_ONLY, + transformArguments: transformRangeArguments.bind(undefined, 'TS.REVRANGE'), + transformReply: RANGE.transformReply +} as const satisfies Command; diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index 2c8fb48ce6..ff82cb893e 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -1,4 +1,4 @@ -import type { RedisArgument, RedisCommands } from '@redis/client/dist/lib/RESP/types'; +import type { BlobStringReply, CommandArguments, DoubleReply, NumberReply, RedisArgument, RedisCommands, TuplesReply, UnwrapReply } from '@redis/client/dist/lib/RESP/types'; import ADD from './ADD'; import ALTER from './ALTER'; import CREATE from './CREATE'; @@ -11,15 +11,16 @@ import INCRBY from './INCRBY'; // import INFO_DEBUG from './INFO_DEBUG'; // import INFO from './INFO'; import MADD from './MADD'; -// import MGET from './MGET'; -// import MGET_WITHLABELS from './MGET_WITHLABELS'; -// import QUERYINDEX from './QUERYINDEX'; -// import RANGE from './RANGE'; -// import REVRANGE from './REVRANGE'; -// import MRANGE from './MRANGE'; -// import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; -// import MREVRANGE from './MREVRANGE'; -// import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; +import MGET_WITHLABELS from './MGET_WITHLABELS'; +import MGET from './MGET'; +import MRANGE_WITHLABELS from './MRANGE_WITHLABELS'; +import MRANGE from './MRANGE'; +import MREVRANGE_WITHLABELS from './MREVRANGE_WITHLABELS'; +import MREVRANGE from './MREVRANGE'; +import QUERYINDEX from './QUERYINDEX'; +import RANGE from './RANGE'; +import REVRANGE from './REVRANGE'; +import { RedisVariadicArgument, pushVariadicArguments } from '@redis/client/dist/lib/commands/generic-transformers'; export default { ADD, @@ -46,24 +47,24 @@ export default { // info: INFO, MADD, mAdd: MADD, - // MGET, - // mGet: MGET, - // MGET_WITHLABELS, - // mGetWithLabels: MGET_WITHLABELS, - // QUERYINDEX, - // queryIndex: QUERYINDEX, - // RANGE, - // range: RANGE, - // REVRANGE, - // revRange: REVRANGE, - // MRANGE, - // mRange: MRANGE, - // MRANGE_WITHLABELS, - // mRangeWithLabels: MRANGE_WITHLABELS, - // MREVRANGE, - // mRevRange: MREVRANGE, - // MREVRANGE_WITHLABELS, - // mRevRangeWithLabels: MREVRANGE_WITHLABELS + MGET_WITHLABELS, + mGetWithLabels: MGET_WITHLABELS, + MGET, + mGet: MGET, + MRANGE_WITHLABELS, + mRangeWithLabels: MRANGE_WITHLABELS, + MRANGE, + mRange: MRANGE, + MREVRANGE_WITHLABELS, + mRevRangeWithLabels: MREVRANGE_WITHLABELS, + MREVRANGE, + mRevRange: MREVRANGE, + QUERYINDEX, + queryIndex: QUERYINDEX, + RANGE, + range: RANGE, + REVRANGE, + revRange: REVRANGE } as const satisfies RedisCommands; export function pushRetentionArgument(args: Array, retention?: number) { @@ -148,20 +149,22 @@ export function pushLabelsArgument(args: Array, labels?: Labels) // return labels // } - -// export type SampleRawReply = [timestamp: number, value: string]; - -// export interface SampleReply { -// timestamp: number; -// value: number; -// } - -// export function transformSampleReply(reply: SampleRawReply): SampleReply { -// return { -// timestamp: reply[0], -// value: Number(reply[1]) -// }; -// } +export const transformSampleReply = { + 2(reply: TuplesReply<[timestamp: NumberReply, value: BlobStringReply]>) { + const [timestamp, value] = reply as unknown as UnwrapReply; + return { + timestamp, + value: Number(value) + }; + }, + 3(reply: TuplesReply<[timestamp: NumberReply, value: DoubleReply]>) { + const [timestamp, value] = reply as unknown as UnwrapReply; + return { + timestamp, + value + }; + } +}; // export enum TimeSeriesBucketTimestamp { // LOW = '-', @@ -293,16 +296,15 @@ export function pushLabelsArgument(args: Array, labels?: Labels) // export type SelectedLabels = string | Array; -// export function pushWithLabelsArgument(args: RedisCommandArguments, selectedLabels?: SelectedLabels): RedisCommandArguments { -// if (!selectedLabels) { -// args.push('WITHLABELS'); -// } else { -// args.push('SELECTED_LABELS'); -// args = pushVariadicArguments(args, selectedLabels); -// } - -// return args; -// } +export function pushWithLabelsArgument(args: CommandArguments, selectedLabels?: RedisVariadicArgument) { + if (!selectedLabels) { + args.push('WITHLABELS'); + return args; + } else { + args.push('SELECTED_LABELS'); + return pushVariadicArguments(args, selectedLabels); + } +} // export interface MRangeWithLabelsOptions extends MRangeOptions { // SELECTED_LABELS?: SelectedLabels; diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts index e0b3c96705..9372621b00 100644 --- a/packages/time-series/lib/index.ts +++ b/packages/time-series/lib/index.ts @@ -4,8 +4,8 @@ export { TIME_SERIES_ENCODING, TimeSeriesEncoding, TIME_SERIES_DUPLICATE_POLICIES, - TimeSeriesDuplicatePolicies, - // TimeSeriesBucketTimestamp + TimeSeriesDuplicatePolicies } from './commands'; export { TIME_SERIES_AGGREGATION_TYPE, TimeSeriesAggregationType } from './commands/CREATERULE'; +export { TIME_SERIES_BUCKET_TIMESTAMP, TimeSeriesBucketTimestamp } from './commands/RANGE'; diff --git a/packages/time-series/package.json b/packages/time-series/package.json index 20e666efe4..03a3293463 100644 --- a/packages/time-series/package.json +++ b/packages/time-series/package.json @@ -5,7 +5,8 @@ "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ - "dist/" + "dist/", + "!dist/tsconfig.tsbuildinfo" ], "scripts": { "test": "nyc -r text-summary -r lcov mocha -r source-map-support/register -r ts-node/register './lib/**/*.spec.ts'"