From 5dd7d3149a633b762ece0ce8006ecbb4311a547f Mon Sep 17 00:00:00 2001 From: Leibale Eidelman Date: Wed, 31 Aug 2022 09:25:13 -0400 Subject: [PATCH] close #2192 close #2193 close #2194 close #2195 close #2196 close #2197 close #2198 - support for TimeSeries 1.8 (#2200) --- .../time-series/lib/commands/ALTER.spec.ts | 27 ++++- packages/time-series/lib/commands/ALTER.ts | 8 +- packages/time-series/lib/commands/CREATE.ts | 10 +- .../lib/commands/CREATERULE.spec.ts | 19 +++- .../time-series/lib/commands/CREATERULE.ts | 13 ++- .../time-series/lib/commands/DELETERULE.ts | 2 +- packages/time-series/lib/commands/GET.spec.ts | 21 +++- packages/time-series/lib/commands/GET.ts | 11 ++- .../time-series/lib/commands/INFO.spec.ts | 55 ++++++----- packages/time-series/lib/commands/INFO.ts | 48 ++++----- .../lib/commands/INFO_DEBUG.spec.ts | 47 +++------ .../time-series/lib/commands/INFO_DEBUG.ts | 32 +++--- .../time-series/lib/commands/MGET.spec.ts | 21 +++- packages/time-series/lib/commands/MGET.ts | 11 ++- .../lib/commands/MGET_WITHLABELS.ts | 4 +- .../time-series/lib/commands/MRANGE.spec.ts | 4 +- .../lib/commands/MRANGE_WITHLABELS.spec.ts | 4 +- .../lib/commands/MREVRANGE.spec.ts | 4 +- .../lib/commands/MREVRANGE_WITHLABELS.spec.ts | 4 +- .../time-series/lib/commands/RANGE.spec.ts | 2 +- .../time-series/lib/commands/REVRANGE.spec.ts | 4 +- .../time-series/lib/commands/index.spec.ts | 98 ++++++++++++++++--- packages/time-series/lib/commands/index.ts | 91 ++++++++++++++--- packages/time-series/lib/index.ts | 8 +- packages/time-series/lib/test-utils.ts | 2 +- 25 files changed, 369 insertions(+), 181 deletions(-) diff --git a/packages/time-series/lib/commands/ALTER.spec.ts b/packages/time-series/lib/commands/ALTER.spec.ts index 989a036535..cd066533aa 100644 --- a/packages/time-series/lib/commands/ALTER.spec.ts +++ b/packages/time-series/lib/commands/ALTER.spec.ts @@ -1,4 +1,5 @@ import { strict as assert } from 'assert'; +import { TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './ALTER'; @@ -20,6 +21,24 @@ describe('ALTER', () => { ); }); + it('with CHUNK_SIZE', () => { + assert.deepEqual( + transformArguments('key', { + CHUNK_SIZE: 1 + }), + ['TS.ALTER', 'key', 'CHUNK_SIZE', '1'] + ); + }); + + it('with DUPLICATE_POLICY', () => { + assert.deepEqual( + transformArguments('key', { + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK + }), + ['TS.ALTER', 'key', 'DUPLICATE_POLICY', 'BLOCK'] + ); + }); + it('with LABELS', () => { assert.deepEqual( transformArguments('key', { @@ -29,19 +48,21 @@ describe('ALTER', () => { ); }); - it('with RETENTION, LABELS', () => { + it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => { assert.deepEqual( transformArguments('key', { RETENTION: 1, + CHUNK_SIZE: 1, + DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK, LABELS: { label: 'value' } }), - ['TS.ALTER', 'key', 'RETENTION', '1', 'LABELS', 'label', 'value'] + ['TS.ALTER', 'key', 'RETENTION', '1', 'CHUNK_SIZE', '1', 'DUPLICATE_POLICY', 'BLOCK', 'LABELS', 'label', 'value'] ); }); }); testUtils.testWithClient('client.ts.alter', async client => { - await client.ts.create('key'); + await client.ts.create('key'); assert.equal( await client.ts.alter('key', { RETENTION: 1 }), diff --git a/packages/time-series/lib/commands/ALTER.ts b/packages/time-series/lib/commands/ALTER.ts index c2c6b35052..7b9e1e774c 100644 --- a/packages/time-series/lib/commands/ALTER.ts +++ b/packages/time-series/lib/commands/ALTER.ts @@ -1,9 +1,11 @@ -import { pushRetentionArgument, Labels, pushLabelsArgument } from '.'; +import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy } from '.'; export const FIRST_KEY_INDEX = 1; interface AlterOptions { RETENTION?: number; + CHUNK_SIZE?: number; + DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies; LABELS?: Labels; } @@ -12,6 +14,10 @@ export function transformArguments(key: string, options?: AlterOptions): Array { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), - ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1'] + ); + }); + + it('with alignTimestamp', () => { + assert.deepEqual( + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1, 1), + ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'AVG', '1', '1'] + ); + }); }); testUtils.testWithClient('client.ts.createRule', async client => { diff --git a/packages/time-series/lib/commands/CREATERULE.ts b/packages/time-series/lib/commands/CREATERULE.ts index 9e3d25a4c8..87b8579a6e 100644 --- a/packages/time-series/lib/commands/CREATERULE.ts +++ b/packages/time-series/lib/commands/CREATERULE.ts @@ -6,16 +6,23 @@ export function transformArguments( sourceKey: string, destinationKey: string, aggregationType: TimeSeriesAggregationType, - timeBucket: number + bucketDuration: number, + alignTimestamp?: number ): Array { - return [ + const args = [ 'TS.CREATERULE', sourceKey, destinationKey, 'AGGREGATION', aggregationType, - timeBucket.toString() + bucketDuration.toString() ]; + + if (alignTimestamp) { + args.push(alignTimestamp.toString()); + } + + return args; } export declare function transformReply(): 'OK'; diff --git a/packages/time-series/lib/commands/DELETERULE.ts b/packages/time-series/lib/commands/DELETERULE.ts index 1714d51093..7d2cfaeed9 100644 --- a/packages/time-series/lib/commands/DELETERULE.ts +++ b/packages/time-series/lib/commands/DELETERULE.ts @@ -4,7 +4,7 @@ export function transformArguments(sourceKey: string, destinationKey: string): A return [ 'TS.DELETERULE', sourceKey, - destinationKey, + destinationKey ]; } diff --git a/packages/time-series/lib/commands/GET.spec.ts b/packages/time-series/lib/commands/GET.spec.ts index 0c0113f35d..29634cd775 100644 --- a/packages/time-series/lib/commands/GET.spec.ts +++ b/packages/time-series/lib/commands/GET.spec.ts @@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './GET'; describe('GET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('key'), - ['TS.GET', 'key'] - ); + describe('transformArguments', () => { + it('without options', () => { + assert.deepEqual( + transformArguments('key'), + ['TS.GET', 'key'] + ); + }); + + it('with LATEST', () => { + assert.deepEqual( + transformArguments('key', { + LATEST: true + }), + ['TS.GET', 'key', 'LATEST'] + ); + }); }); describe('client.ts.get', () => { diff --git a/packages/time-series/lib/commands/GET.ts b/packages/time-series/lib/commands/GET.ts index ec3b1f5f80..6d74f97c9c 100644 --- a/packages/time-series/lib/commands/GET.ts +++ b/packages/time-series/lib/commands/GET.ts @@ -1,11 +1,16 @@ -import { SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; +import { pushLatestArgument, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const FIRST_KEY_INDEX = 1; export const IS_READ_ONLY = true; -export function transformArguments(key: string): Array { - return ['TS.GET', key]; +interface GetOptions { + LATEST?: boolean; +} + +export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments { + return pushLatestArgument(['TS.GET', key], options?.LATEST); } export function transformReply(reply: [] | SampleRawReply): null | SampleReply { diff --git a/packages/time-series/lib/commands/INFO.spec.ts b/packages/time-series/lib/commands/INFO.spec.ts index 83bad872c6..c02cdd6da4 100644 --- a/packages/time-series/lib/commands/INFO.spec.ts +++ b/packages/time-series/lib/commands/INFO.spec.ts @@ -1,7 +1,7 @@ import { strict as assert } from 'assert'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; -import { transformArguments } from './INFO'; +import { InfoReply, transformArguments } from './INFO'; describe('INFO', () => { it('transformArguments', () => { @@ -14,7 +14,7 @@ describe('INFO', () => { testUtils.testWithClient('client.ts.info', async client => { await Promise.all([ client.ts.create('key', { - LABELS: { id: "2" }, + LABELS: { id: '1' }, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST }), client.ts.create('key2'), @@ -22,29 +22,32 @@ describe('INFO', () => { client.ts.add('key', 1, 10) ]); - assert.deepEqual( - await client.ts.info('key'), - { - totalSamples: 1, - memoryUsage: 4261, - firstTimestamp: 1, - lastTimestamp: 1, - retentionTime: 0, - chunkCount: 1, - chunkSize: 4096, - chunkType: 'compressed', - duplicatePolicy: 'last', - labels: [{ - name: 'id', - value: '2' - }], - rules: [{ - aggregationType: 'COUNT', - key: 'key2', - timeBucket: 5 - }], - sourceKey: null - } - ); + assertInfo(await client.ts.info('key')); }, GLOBAL.SERVERS.OPEN); }); + +export function assertInfo(info: InfoReply): void { + assert.equal(typeof info.totalSamples, 'number'); + assert.equal(typeof info.memoryUsage, 'number'); + assert.equal(typeof info.firstTimestamp, 'number'); + assert.equal(typeof info.lastTimestamp, 'number'); + assert.equal(typeof info.retentionTime, 'number'); + assert.equal(typeof info.chunkCount, 'number'); + assert.equal(typeof info.chunkSize, 'number'); + assert.equal(typeof info.chunkType, 'string'); + assert.equal(typeof info.duplicatePolicy, 'string'); + assert.ok(Array.isArray(info.labels)); + for (const label of info.labels) { + assert.equal(typeof label, 'object'); + assert.equal(typeof label.name, 'string'); + assert.equal(typeof label.value, 'string'); + } + assert.ok(Array.isArray(info.rules)); + for (const rule of info.rules) { + assert.equal(typeof rule, 'object'); + assert.equal(typeof rule.aggregationType, 'string'); + assert.equal(typeof rule.key, 'string'); + assert.equal(typeof rule.timeBucket, 'number'); + } + assert.ok(info.sourceKey === null || typeof info.sourceKey === 'string'); +} diff --git a/packages/time-series/lib/commands/INFO.ts b/packages/time-series/lib/commands/INFO.ts index 00e04a1985..25ce3ef54e 100644 --- a/packages/time-series/lib/commands/INFO.ts +++ b/packages/time-series/lib/commands/INFO.ts @@ -9,30 +9,30 @@ export function transformArguments(key: string): Array { } export type InfoRawReply = [ - _: string, - totalSamples: number, - _: string, - memoryUsage: number, - _: string, - firstTimestamp: number, - _: string, - lastTimestamp: number, - _: string, - retentionTime: number, - _: string, - chunkCount: number, - _: string, - chunkSize: number, - _: string, - chunkType: string, - _: string, - duplicatePolicy: TimeSeriesDuplicatePolicies | null, - _: string, - labels: Array<[name: string, value: string]>, - _: string, - sourceKey: string | null, - _: string, - rules: Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> + 'totalSamples', + number, + 'memoryUsage', + number, + 'firstTimestamp', + number, + 'lastTimestamp', + number, + 'retentionTime', + number, + 'chunkCount', + number, + 'chunkSize', + number, + 'chunkType', + string, + 'duplicatePolicy', + TimeSeriesDuplicatePolicies | null, + 'labels', + Array<[name: string, value: string]>, + 'sourceKey', + string | null, + 'rules', + Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> ]; export interface InfoReply { diff --git a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts index 00101d980a..666689f519 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.spec.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.spec.ts @@ -1,6 +1,7 @@ import { strict as assert } from 'assert'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import testUtils, { GLOBAL } from '../test-utils'; +import { assertInfo } from './INFO.spec'; import { transformArguments } from './INFO_DEBUG'; describe('INFO_DEBUG', () => { @@ -14,7 +15,7 @@ describe('INFO_DEBUG', () => { testUtils.testWithClient('client.ts.get', async client => { await Promise.all([ client.ts.create('key', { - LABELS: { id: "2" }, + LABELS: { id: '1' }, DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST }), client.ts.create('key2'), @@ -22,37 +23,17 @@ describe('INFO_DEBUG', () => { client.ts.add('key', 1, 10) ]); - assert.deepEqual( - await client.ts.infoDebug('key'), - { - totalSamples: 1, - memoryUsage: 4261, - firstTimestamp: 1, - lastTimestamp: 1, - retentionTime: 0, - chunkCount: 1, - chunkSize: 4096, - chunkType: 'compressed', - duplicatePolicy: 'last', - labels: [{ - name: 'id', - value: '2' - }], - sourceKey: null, - rules: [{ - aggregationType: 'COUNT', - key: 'key2', - timeBucket: 5 - }], - keySelfName: 'key', - chunks: [{ - startTimestamp: 1, - endTimestamp: 1, - samples: 1, - size: 4096, - bytesPerSample: '4096' - }] - } - ); + const infoDebug = await client.ts.infoDebug('key'); + assertInfo(infoDebug); + assert.equal(typeof infoDebug.keySelfName, 'string'); + assert.ok(Array.isArray(infoDebug.chunks)); + for (const chunk of infoDebug.chunks) { + assert.equal(typeof chunk, 'object'); + assert.equal(typeof chunk.startTimestamp, 'number'); + assert.equal(typeof chunk.endTimestamp, 'number'); + assert.equal(typeof chunk.samples, 'number'); + assert.equal(typeof chunk.size, 'number'); + assert.equal(typeof chunk.bytesPerSample, 'string'); + } }, GLOBAL.SERVERS.OPEN); }); diff --git a/packages/time-series/lib/commands/INFO_DEBUG.ts b/packages/time-series/lib/commands/INFO_DEBUG.ts index ad9522d2a6..20d6ff5e24 100644 --- a/packages/time-series/lib/commands/INFO_DEBUG.ts +++ b/packages/time-series/lib/commands/INFO_DEBUG.ts @@ -14,23 +14,23 @@ export function transformArguments(key: string): Array { } type InfoDebugRawReply = [ - ...infoArgs: InfoRawReply, - _: string, - keySelfName: string, - _: string, - chunks: Array<[ - _: string, - startTimestamp: number, - _: string, - endTimestamp: number, - _: string, - samples: number, - _: string, - size: number, - _: string, - bytesPerSample: string + ...InfoRawReply, + 'keySelfName', + string, + 'chunks', + Array<[ + 'startTimestamp', + number, + 'endTimestamp', + number, + 'samples', + number, + 'size', + number, + 'bytesPerSample', + string ]> -] +]; interface InfoDebugReply extends InfoReply { keySelfName: string; diff --git a/packages/time-series/lib/commands/MGET.spec.ts b/packages/time-series/lib/commands/MGET.spec.ts index 7c6c32927d..61da3b9638 100644 --- a/packages/time-series/lib/commands/MGET.spec.ts +++ b/packages/time-series/lib/commands/MGET.spec.ts @@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils'; import { transformArguments } from './MGET'; describe('MGET', () => { - it('transformArguments', () => { - assert.deepEqual( - transformArguments('label=value'), - ['TS.MGET', 'FILTER', 'label=value'] - ); + 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'] + ); + }); }); testUtils.testWithClient('client.ts.mGet', async client => { diff --git a/packages/time-series/lib/commands/MGET.ts b/packages/time-series/lib/commands/MGET.ts index 45d970ec81..67315722eb 100644 --- a/packages/time-series/lib/commands/MGET.ts +++ b/packages/time-series/lib/commands/MGET.ts @@ -1,10 +1,15 @@ import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; -import { Filter, pushFilterArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; +import { Filter, pushFilterArgument, pushLatestArgument, RawLabels, SampleRawReply, SampleReply, transformSampleReply } from '.'; export const IS_READ_ONLY = true; -export function transformArguments(filter: Filter): RedisCommandArguments { - return pushFilterArgument(['TS.MGET'], filter); +export interface MGetOptions { + LATEST?: boolean; +} + +export function transformArguments(filter: Filter, options?: MGetOptions): RedisCommandArguments { + const args = pushLatestArgument(['TS.MGET'], options?.LATEST); + return pushFilterArgument(args, filter); } export type MGetRawReply = Array<[ diff --git a/packages/time-series/lib/commands/MGET_WITHLABELS.ts b/packages/time-series/lib/commands/MGET_WITHLABELS.ts index b0875cefe0..232c17a0ad 100644 --- a/packages/time-series/lib/commands/MGET_WITHLABELS.ts +++ b/packages/time-series/lib/commands/MGET_WITHLABELS.ts @@ -7,12 +7,12 @@ import { Filter, pushFilterArgument } from '.'; -import { MGetRawReply, MGetReply } from './MGET'; +import { MGetOptions, MGetRawReply, MGetReply } from './MGET'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; export const IS_READ_ONLY = true; -interface MGetWithLabelsOptions { +interface MGetWithLabelsOptions extends MGetOptions { SELECTED_LABELS?: SelectedLabels; } diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index 1913576ce6..4228cc06fb 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -24,8 +24,8 @@ describe('MRANGE', () => { }, }), ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index 441124ccc9..983114f840 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -25,8 +25,8 @@ describe('MRANGE_WITHLABELS', () => { }, }), ['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'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 764fbb4845..6e5825d36d 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -24,8 +24,8 @@ describe('MREVRANGE', () => { }, }), ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', - 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', - 'GROUPBY', 'label', 'REDUCE', 'sum'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value', + 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index 279bcf6528..7e80e965d4 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -25,8 +25,8 @@ describe('MREVRANGE_WITHLABELS', () => { }, }), ['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'] + 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label', + 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM'] ); }); diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index 10b2f452bd..1e6a995880 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -20,7 +20,7 @@ describe('RANGE', () => { } }), ['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1'] + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1'] ); }); diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index ae6722dbf4..ffd90268c8 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -59,7 +59,7 @@ describe('REVRANGE', () => { timeBucket: 1 } }), - ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'avg', '1'] + ['TS.REVRANGE', 'key', '-', '+', 'AGGREGATION', 'AVG', '1'] ); }); @@ -80,7 +80,7 @@ describe('REVRANGE', () => { }), [ 'TS.REVRANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', - '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1' + '1', '2', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1' ] ); }); diff --git a/packages/time-series/lib/commands/index.spec.ts b/packages/time-series/lib/commands/index.spec.ts index 8914b1fb13..a29eefe860 100644 --- a/packages/time-series/lib/commands/index.spec.ts +++ b/packages/time-series/lib/commands/index.spec.ts @@ -6,6 +6,7 @@ import { TimeSeriesEncoding, pushEncodingArgument, pushChunkSizeArgument, + pushDuplicatePolicy, pushLabelsArgument, transformIncrDecrArguments, transformSampleReply, @@ -19,7 +20,10 @@ import { pushMRangeWithLabelsArguments, transformRangeReply, transformMRangeReply, - transformMRangeWithLabelsReply + transformMRangeWithLabelsReply, + TimeSeriesDuplicatePolicies, + pushLatestArgument, + TimeSeriesBucketTimestamp } from '.'; describe('transformTimestampArgument', () => { @@ -87,6 +91,17 @@ describe('pushChunkSizeArgument', () => { }); }); +describe('pushDuplicatePolicy', () => { + testOptionalArgument(pushDuplicatePolicy); + + it('BLOCK', () => { + assert.deepEqual( + pushDuplicatePolicy([], TimeSeriesDuplicatePolicies.BLOCK), + ['DUPLICATE_POLICY', 'BLOCK'] + ); + }); +}); + describe('pushLabelsArgument', () => { testOptionalArgument(pushLabelsArgument); @@ -202,16 +217,44 @@ describe('pushRangeArguments', () => { ); }); - it('with AGGREGATION', () => { - assert.deepEqual( - pushRangeArguments([], '-', '+', { - AGGREGATION: { - type: TimeSeriesAggregationType.FIRST, - timeBucket: 1 - } - }), - ['-', '+', 'AGGREGATION', 'first', '1'] - ); + describe('with AGGREGATION', () => { + it('without options', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1 + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1'] + ); + }); + + it('with BUCKETTIMESTAMP', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1, + BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-'] + ); + }); + + it('with BUCKETTIMESTAMP', () => { + assert.deepEqual( + pushRangeArguments([], '-', '+', { + AGGREGATION: { + type: TimeSeriesAggregationType.FIRST, + timeBucket: 1, + EMPTY: true + } + }), + ['-', '+', 'AGGREGATION', 'FIRST', '1', 'EMPTY'] + ); + }); }); it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => { @@ -226,11 +269,13 @@ describe('pushRangeArguments', () => { ALIGN: 1, AGGREGATION: { type: TimeSeriesAggregationType.FIRST, - timeBucket: 1 + timeBucket: 1, + BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW, + EMPTY: true } }), ['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', - 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'first', '1'] + 'COUNT', '1', 'ALIGN', '1', 'AGGREGATION', 'FIRST', '1', 'BUCKETTIMESTAMP', '-', 'EMPTY'] ); }); }); @@ -249,7 +294,7 @@ describe('pushMRangeGroupByArguments', () => { label: 'label', reducer: TimeSeriesReducers.MAXIMUM }), - ['GROUPBY', 'label', 'REDUCE', 'max'] + ['GROUPBY', 'label', 'REDUCE', 'MAX'] ); }); }); @@ -286,7 +331,7 @@ describe('pushMRangeArguments', () => { reducer: TimeSeriesReducers.MAXIMUM } }), - ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'max'] + ['-', '+', 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'MAX'] ); }); }); @@ -369,3 +414,26 @@ describe('transformMRangeWithLabelsReply', () => { }] ); }); + +describe('pushLatestArgument', () => { + it('undefined', () => { + assert.deepEqual( + pushLatestArgument([]), + [] + ); + }); + + it('false', () => { + assert.deepEqual( + pushLatestArgument([], false), + [] + ); + }); + + it('true', () => { + assert.deepEqual( + pushLatestArgument([], true), + ['LATEST'] + ); + }); +}) diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index aba3ae2de9..19cd075ba4 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -68,18 +68,25 @@ export default { }; export enum TimeSeriesAggregationType { - AVERAGE = 'avg', - SUM = 'sum', - MINIMUM = 'min', - MAXIMUM = 'max', - RANGE = 'range', - COUNT = 'count', - FIRST = 'first', - LAST = 'last', - STD_P = 'std.p', - STD_S = 'std.s', - VAR_P = 'var.p', - VAR_S = 'var.s' + AVG = 'AVG', + // @deprecated + AVERAGE = 'AVG', + FIRST = 'FIRST', + LAST = 'LAST', + MIN = 'MIN', + // @deprecated + MINIMUM = 'MIN', + MAX = 'MAX', + // @deprecated + MAXIMUM = 'MAX', + SUM = 'SUM', + RANGE = 'RANGE', + COUNT = 'COUNT', + STD_P = 'STD.P', + STD_S = 'STD.S', + VAR_P = 'VAR.P', + VAR_S = 'VAR.S', + TWA = 'TWA' } export enum TimeSeriesDuplicatePolicies { @@ -92,9 +99,20 @@ export enum TimeSeriesDuplicatePolicies { } export enum TimeSeriesReducers { - SUM = 'sum', - MINIMUM = 'min', - MAXIMUM = 'max', + AVG = 'AVG', + SUM = 'SUM', + MIN = 'MIN', + // @deprecated + MINIMUM = 'MIN', + MAX = 'MAX', + // @deprecated + MAXIMUM = 'MAX', + RANGE = 'range', + COUNT = 'COUNT', + STD_P = 'STD.P', + STD_S = 'STD.S', + VAR_P = 'VAR.P', + VAR_S = 'VAR.S', } export type Timestamp = number | Date | string; @@ -147,6 +165,17 @@ export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: n return args; } +export function pushDuplicatePolicy(args: RedisCommandArguments, duplicatePolicy?: TimeSeriesDuplicatePolicies): RedisCommandArguments { + if (duplicatePolicy) { + args.push( + 'DUPLICATE_POLICY', + duplicatePolicy + ); + } + + return args; +} + export type RawLabels = Array<[label: string, value: string]>; export type Labels = { @@ -226,7 +255,14 @@ export function transformSampleReply(reply: SampleRawReply): SampleReply { }; } +export enum TimeSeriesBucketTimestamp { + LOW = '-', + HIGH = '+', + MID = '~' +} + export interface RangeOptions { + LATEST?: boolean; FILTER_BY_TS?: Array; FILTER_BY_VALUE?: { min: number; @@ -237,6 +273,8 @@ export interface RangeOptions { AGGREGATION?: { type: TimeSeriesAggregationType; timeBucket: Timestamp; + BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp; + EMPTY?: boolean; }; } @@ -251,9 +289,11 @@ export function pushRangeArguments( transformTimestampArgument(toTimestamp) ); + pushLatestArgument(args, options?.LATEST); + if (options?.FILTER_BY_TS) { args.push('FILTER_BY_TS'); - for(const ts of options.FILTER_BY_TS) { + for (const ts of options.FILTER_BY_TS) { args.push(transformTimestampArgument(ts)); } } @@ -286,6 +326,17 @@ export function pushRangeArguments( 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; @@ -406,3 +457,11 @@ export function transformMRangeWithLabelsReply(reply: MRangeRawReply): Array