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

close #2192 close #2193 close #2194 close #2195 close #2196 close #2197 close #2198 - support for TimeSeries 1.8 (#2200)

This commit is contained in:
Leibale Eidelman
2022-08-31 09:25:13 -04:00
committed by GitHub
parent b10a6567dc
commit 5dd7d3149a
25 changed files with 369 additions and 181 deletions

View File

@@ -1,4 +1,5 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import { TimeSeriesDuplicatePolicies } from '.';
import testUtils, { GLOBAL } from '../test-utils'; import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './ALTER'; 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', () => { it('with LABELS', () => {
assert.deepEqual( assert.deepEqual(
transformArguments('key', { transformArguments('key', {
@@ -29,19 +48,21 @@ describe('ALTER', () => {
); );
}); });
it('with RETENTION, LABELS', () => { it('with RETENTION, CHUNK_SIZE, DUPLICATE_POLICY, LABELS', () => {
assert.deepEqual( assert.deepEqual(
transformArguments('key', { transformArguments('key', {
RETENTION: 1, RETENTION: 1,
CHUNK_SIZE: 1,
DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK,
LABELS: { label: 'value' } 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 => { testUtils.testWithClient('client.ts.alter', async client => {
await client.ts.create('key'); await client.ts.create('key');
assert.equal( assert.equal(
await client.ts.alter('key', { RETENTION: 1 }), await client.ts.alter('key', { RETENTION: 1 }),

View File

@@ -1,9 +1,11 @@
import { pushRetentionArgument, Labels, pushLabelsArgument } from '.'; import { pushRetentionArgument, Labels, pushLabelsArgument, TimeSeriesDuplicatePolicies, pushChunkSizeArgument, pushDuplicatePolicy } from '.';
export const FIRST_KEY_INDEX = 1; export const FIRST_KEY_INDEX = 1;
interface AlterOptions { interface AlterOptions {
RETENTION?: number; RETENTION?: number;
CHUNK_SIZE?: number;
DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies;
LABELS?: Labels; LABELS?: Labels;
} }
@@ -12,6 +14,10 @@ export function transformArguments(key: string, options?: AlterOptions): Array<s
pushRetentionArgument(args, options?.RETENTION); pushRetentionArgument(args, options?.RETENTION);
pushChunkSizeArgument(args, options?.CHUNK_SIZE);
pushDuplicatePolicy(args, options?.DUPLICATE_POLICY);
pushLabelsArgument(args, options?.LABELS); pushLabelsArgument(args, options?.LABELS);
return args; return args;

View File

@@ -5,7 +5,8 @@ import {
pushChunkSizeArgument, pushChunkSizeArgument,
TimeSeriesDuplicatePolicies, TimeSeriesDuplicatePolicies,
Labels, Labels,
pushLabelsArgument pushLabelsArgument,
pushDuplicatePolicy
} from '.'; } from '.';
export const FIRST_KEY_INDEX = 1; export const FIRST_KEY_INDEX = 1;
@@ -27,12 +28,7 @@ export function transformArguments(key: string, options?: CreateOptions): Array<
pushChunkSizeArgument(args, options?.CHUNK_SIZE); pushChunkSizeArgument(args, options?.CHUNK_SIZE);
if (options?.DUPLICATE_POLICY) { pushDuplicatePolicy(args, options?.DUPLICATE_POLICY);
args.push(
'DUPLICATE_POLICY',
options.DUPLICATE_POLICY
);
}
pushLabelsArgument(args, options?.LABELS); pushLabelsArgument(args, options?.LABELS);

View File

@@ -4,11 +4,20 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './CREATERULE'; import { transformArguments } from './CREATERULE';
describe('CREATERULE', () => { describe('CREATERULE', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('without options', () => {
transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), assert.deepEqual(
['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] 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 => { testUtils.testWithClient('client.ts.createRule', async client => {

View File

@@ -6,16 +6,23 @@ export function transformArguments(
sourceKey: string, sourceKey: string,
destinationKey: string, destinationKey: string,
aggregationType: TimeSeriesAggregationType, aggregationType: TimeSeriesAggregationType,
timeBucket: number bucketDuration: number,
alignTimestamp?: number
): Array<string> { ): Array<string> {
return [ const args = [
'TS.CREATERULE', 'TS.CREATERULE',
sourceKey, sourceKey,
destinationKey, destinationKey,
'AGGREGATION', 'AGGREGATION',
aggregationType, aggregationType,
timeBucket.toString() bucketDuration.toString()
]; ];
if (alignTimestamp) {
args.push(alignTimestamp.toString());
}
return args;
} }
export declare function transformReply(): 'OK'; export declare function transformReply(): 'OK';

View File

@@ -4,7 +4,7 @@ export function transformArguments(sourceKey: string, destinationKey: string): A
return [ return [
'TS.DELETERULE', 'TS.DELETERULE',
sourceKey, sourceKey,
destinationKey, destinationKey
]; ];
} }

View File

@@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './GET'; import { transformArguments } from './GET';
describe('GET', () => { describe('GET', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('without options', () => {
transformArguments('key'), assert.deepEqual(
['TS.GET', 'key'] transformArguments('key'),
); ['TS.GET', 'key']
);
});
it('with LATEST', () => {
assert.deepEqual(
transformArguments('key', {
LATEST: true
}),
['TS.GET', 'key', 'LATEST']
);
});
}); });
describe('client.ts.get', () => { describe('client.ts.get', () => {

View File

@@ -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 FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true; export const IS_READ_ONLY = true;
export function transformArguments(key: string): Array<string> { interface GetOptions {
return ['TS.GET', key]; LATEST?: boolean;
}
export function transformArguments(key: string, options?: GetOptions): RedisCommandArguments {
return pushLatestArgument(['TS.GET', key], options?.LATEST);
} }
export function transformReply(reply: [] | SampleRawReply): null | SampleReply { export function transformReply(reply: [] | SampleRawReply): null | SampleReply {

View File

@@ -1,7 +1,7 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.';
import testUtils, { GLOBAL } from '../test-utils'; import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './INFO'; import { InfoReply, transformArguments } from './INFO';
describe('INFO', () => { describe('INFO', () => {
it('transformArguments', () => { it('transformArguments', () => {
@@ -14,7 +14,7 @@ describe('INFO', () => {
testUtils.testWithClient('client.ts.info', async client => { testUtils.testWithClient('client.ts.info', async client => {
await Promise.all([ await Promise.all([
client.ts.create('key', { client.ts.create('key', {
LABELS: { id: "2" }, LABELS: { id: '1' },
DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST
}), }),
client.ts.create('key2'), client.ts.create('key2'),
@@ -22,29 +22,32 @@ describe('INFO', () => {
client.ts.add('key', 1, 10) client.ts.add('key', 1, 10)
]); ]);
assert.deepEqual( assertInfo(await client.ts.info('key'));
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
}
);
}, GLOBAL.SERVERS.OPEN); }, 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');
}

View File

@@ -9,30 +9,30 @@ export function transformArguments(key: string): Array<string> {
} }
export type InfoRawReply = [ export type InfoRawReply = [
_: string, 'totalSamples',
totalSamples: number, number,
_: string, 'memoryUsage',
memoryUsage: number, number,
_: string, 'firstTimestamp',
firstTimestamp: number, number,
_: string, 'lastTimestamp',
lastTimestamp: number, number,
_: string, 'retentionTime',
retentionTime: number, number,
_: string, 'chunkCount',
chunkCount: number, number,
_: string, 'chunkSize',
chunkSize: number, number,
_: string, 'chunkType',
chunkType: string, string,
_: string, 'duplicatePolicy',
duplicatePolicy: TimeSeriesDuplicatePolicies | null, TimeSeriesDuplicatePolicies | null,
_: string, 'labels',
labels: Array<[name: string, value: string]>, Array<[name: string, value: string]>,
_: string, 'sourceKey',
sourceKey: string | null, string | null,
_: string, 'rules',
rules: Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]> Array<[key: string, timeBucket: number, aggregationType: TimeSeriesAggregationType]>
]; ];
export interface InfoReply { export interface InfoReply {

View File

@@ -1,6 +1,7 @@
import { strict as assert } from 'assert'; import { strict as assert } from 'assert';
import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.'; import { TimeSeriesAggregationType, TimeSeriesDuplicatePolicies } from '.';
import testUtils, { GLOBAL } from '../test-utils'; import testUtils, { GLOBAL } from '../test-utils';
import { assertInfo } from './INFO.spec';
import { transformArguments } from './INFO_DEBUG'; import { transformArguments } from './INFO_DEBUG';
describe('INFO_DEBUG', () => { describe('INFO_DEBUG', () => {
@@ -14,7 +15,7 @@ describe('INFO_DEBUG', () => {
testUtils.testWithClient('client.ts.get', async client => { testUtils.testWithClient('client.ts.get', async client => {
await Promise.all([ await Promise.all([
client.ts.create('key', { client.ts.create('key', {
LABELS: { id: "2" }, LABELS: { id: '1' },
DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.LAST
}), }),
client.ts.create('key2'), client.ts.create('key2'),
@@ -22,37 +23,17 @@ describe('INFO_DEBUG', () => {
client.ts.add('key', 1, 10) client.ts.add('key', 1, 10)
]); ]);
assert.deepEqual( const infoDebug = await client.ts.infoDebug('key');
await client.ts.infoDebug('key'), assertInfo(infoDebug);
{ assert.equal(typeof infoDebug.keySelfName, 'string');
totalSamples: 1, assert.ok(Array.isArray(infoDebug.chunks));
memoryUsage: 4261, for (const chunk of infoDebug.chunks) {
firstTimestamp: 1, assert.equal(typeof chunk, 'object');
lastTimestamp: 1, assert.equal(typeof chunk.startTimestamp, 'number');
retentionTime: 0, assert.equal(typeof chunk.endTimestamp, 'number');
chunkCount: 1, assert.equal(typeof chunk.samples, 'number');
chunkSize: 4096, assert.equal(typeof chunk.size, 'number');
chunkType: 'compressed', assert.equal(typeof chunk.bytesPerSample, 'string');
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'
}]
}
);
}, GLOBAL.SERVERS.OPEN); }, GLOBAL.SERVERS.OPEN);
}); });

View File

@@ -14,23 +14,23 @@ export function transformArguments(key: string): Array<string> {
} }
type InfoDebugRawReply = [ type InfoDebugRawReply = [
...infoArgs: InfoRawReply, ...InfoRawReply,
_: string, 'keySelfName',
keySelfName: string, string,
_: string, 'chunks',
chunks: Array<[ Array<[
_: string, 'startTimestamp',
startTimestamp: number, number,
_: string, 'endTimestamp',
endTimestamp: number, number,
_: string, 'samples',
samples: number, number,
_: string, 'size',
size: number, number,
_: string, 'bytesPerSample',
bytesPerSample: string string
]> ]>
] ];
interface InfoDebugReply extends InfoReply { interface InfoDebugReply extends InfoReply {
keySelfName: string; keySelfName: string;

View File

@@ -3,11 +3,22 @@ import testUtils, { GLOBAL } from '../test-utils';
import { transformArguments } from './MGET'; import { transformArguments } from './MGET';
describe('MGET', () => { describe('MGET', () => {
it('transformArguments', () => { describe('transformArguments', () => {
assert.deepEqual( it('without options', () => {
transformArguments('label=value'), assert.deepEqual(
['TS.MGET', 'FILTER', 'label=value'] 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 => { testUtils.testWithClient('client.ts.mGet', async client => {

View File

@@ -1,10 +1,15 @@
import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; 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 const IS_READ_ONLY = true;
export function transformArguments(filter: Filter): RedisCommandArguments { export interface MGetOptions {
return pushFilterArgument(['TS.MGET'], filter); 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<[ export type MGetRawReply = Array<[

View File

@@ -7,12 +7,12 @@ import {
Filter, Filter,
pushFilterArgument pushFilterArgument
} from '.'; } from '.';
import { MGetRawReply, MGetReply } from './MGET'; import { MGetOptions, MGetRawReply, MGetReply } from './MGET';
import { RedisCommandArguments } from '@redis/client/dist/lib/commands'; import { RedisCommandArguments } from '@redis/client/dist/lib/commands';
export const IS_READ_ONLY = true; export const IS_READ_ONLY = true;
interface MGetWithLabelsOptions { interface MGetWithLabelsOptions extends MGetOptions {
SELECTED_LABELS?: SelectedLabels; SELECTED_LABELS?: SelectedLabels;
} }

View File

@@ -24,8 +24,8 @@ describe('MRANGE', () => {
}, },
}), }),
['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1',
'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value',
'GROUPBY', 'label', 'REDUCE', 'sum'] 'GROUPBY', 'label', 'REDUCE', 'SUM']
); );
}); });

View File

@@ -25,8 +25,8 @@ describe('MRANGE_WITHLABELS', () => {
}, },
}), }),
['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', ['TS.MRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1',
'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label',
'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM']
); );
}); });

View File

@@ -24,8 +24,8 @@ describe('MREVRANGE', () => {
}, },
}), }),
['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1',
'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'FILTER', 'label=value', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'FILTER', 'label=value',
'GROUPBY', 'label', 'REDUCE', 'sum'] 'GROUPBY', 'label', 'REDUCE', 'SUM']
); );
}); });

View File

@@ -25,8 +25,8 @@ describe('MREVRANGE_WITHLABELS', () => {
}, },
}), }),
['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1', ['TS.MREVRANGE', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', '0', '1',
'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'avg', '1', 'SELECTED_LABELS', 'label', 'COUNT', '1', 'ALIGN', '-', 'AGGREGATION', 'AVG', '1', 'SELECTED_LABELS', 'label',
'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'sum'] 'FILTER', 'label=value', 'GROUPBY', 'label', 'REDUCE', 'SUM']
); );
}); });

View File

@@ -20,7 +20,7 @@ describe('RANGE', () => {
} }
}), }),
['TS.RANGE', 'key', '-', '+', 'FILTER_BY_TS', '0', 'FILTER_BY_VALUE', ['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']
); );
}); });

View File

@@ -59,7 +59,7 @@ describe('REVRANGE', () => {
timeBucket: 1 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', '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'
] ]
); );
}); });

View File

@@ -6,6 +6,7 @@ import {
TimeSeriesEncoding, TimeSeriesEncoding,
pushEncodingArgument, pushEncodingArgument,
pushChunkSizeArgument, pushChunkSizeArgument,
pushDuplicatePolicy,
pushLabelsArgument, pushLabelsArgument,
transformIncrDecrArguments, transformIncrDecrArguments,
transformSampleReply, transformSampleReply,
@@ -19,7 +20,10 @@ import {
pushMRangeWithLabelsArguments, pushMRangeWithLabelsArguments,
transformRangeReply, transformRangeReply,
transformMRangeReply, transformMRangeReply,
transformMRangeWithLabelsReply transformMRangeWithLabelsReply,
TimeSeriesDuplicatePolicies,
pushLatestArgument,
TimeSeriesBucketTimestamp
} from '.'; } from '.';
describe('transformTimestampArgument', () => { describe('transformTimestampArgument', () => {
@@ -87,6 +91,17 @@ describe('pushChunkSizeArgument', () => {
}); });
}); });
describe('pushDuplicatePolicy', () => {
testOptionalArgument(pushDuplicatePolicy);
it('BLOCK', () => {
assert.deepEqual(
pushDuplicatePolicy([], TimeSeriesDuplicatePolicies.BLOCK),
['DUPLICATE_POLICY', 'BLOCK']
);
});
});
describe('pushLabelsArgument', () => { describe('pushLabelsArgument', () => {
testOptionalArgument(pushLabelsArgument); testOptionalArgument(pushLabelsArgument);
@@ -202,16 +217,44 @@ describe('pushRangeArguments', () => {
); );
}); });
it('with AGGREGATION', () => { describe('with AGGREGATION', () => {
assert.deepEqual( it('without options', () => {
pushRangeArguments([], '-', '+', { assert.deepEqual(
AGGREGATION: { pushRangeArguments([], '-', '+', {
type: TimeSeriesAggregationType.FIRST, AGGREGATION: {
timeBucket: 1 type: TimeSeriesAggregationType.FIRST,
} timeBucket: 1
}), }
['-', '+', 'AGGREGATION', 'first', '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', () => { it('with FILTER_BY_TS, FILTER_BY_VALUE, COUNT, ALIGN, AGGREGATION', () => {
@@ -226,11 +269,13 @@ describe('pushRangeArguments', () => {
ALIGN: 1, ALIGN: 1,
AGGREGATION: { AGGREGATION: {
type: TimeSeriesAggregationType.FIRST, type: TimeSeriesAggregationType.FIRST,
timeBucket: 1 timeBucket: 1,
BUCKETTIMESTAMP: TimeSeriesBucketTimestamp.LOW,
EMPTY: true
} }
}), }),
['-', '+', 'FILTER_BY_TS', 'ts', 'FILTER_BY_VALUE', '1', '2', ['-', '+', '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', label: 'label',
reducer: TimeSeriesReducers.MAXIMUM reducer: TimeSeriesReducers.MAXIMUM
}), }),
['GROUPBY', 'label', 'REDUCE', 'max'] ['GROUPBY', 'label', 'REDUCE', 'MAX']
); );
}); });
}); });
@@ -286,7 +331,7 @@ describe('pushMRangeArguments', () => {
reducer: TimeSeriesReducers.MAXIMUM 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']
);
});
})

View File

@@ -68,18 +68,25 @@ export default {
}; };
export enum TimeSeriesAggregationType { export enum TimeSeriesAggregationType {
AVERAGE = 'avg', AVG = 'AVG',
SUM = 'sum', // @deprecated
MINIMUM = 'min', AVERAGE = 'AVG',
MAXIMUM = 'max', FIRST = 'FIRST',
RANGE = 'range', LAST = 'LAST',
COUNT = 'count', MIN = 'MIN',
FIRST = 'first', // @deprecated
LAST = 'last', MINIMUM = 'MIN',
STD_P = 'std.p', MAX = 'MAX',
STD_S = 'std.s', // @deprecated
VAR_P = 'var.p', MAXIMUM = 'MAX',
VAR_S = 'var.s' 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 { export enum TimeSeriesDuplicatePolicies {
@@ -92,9 +99,20 @@ export enum TimeSeriesDuplicatePolicies {
} }
export enum TimeSeriesReducers { export enum TimeSeriesReducers {
SUM = 'sum', AVG = 'AVG',
MINIMUM = 'min', SUM = 'SUM',
MAXIMUM = 'max', 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; export type Timestamp = number | Date | string;
@@ -147,6 +165,17 @@ export function pushChunkSizeArgument(args: RedisCommandArguments, chunkSize?: n
return args; 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 RawLabels = Array<[label: string, value: string]>;
export type Labels = { export type Labels = {
@@ -226,7 +255,14 @@ export function transformSampleReply(reply: SampleRawReply): SampleReply {
}; };
} }
export enum TimeSeriesBucketTimestamp {
LOW = '-',
HIGH = '+',
MID = '~'
}
export interface RangeOptions { export interface RangeOptions {
LATEST?: boolean;
FILTER_BY_TS?: Array<Timestamp>; FILTER_BY_TS?: Array<Timestamp>;
FILTER_BY_VALUE?: { FILTER_BY_VALUE?: {
min: number; min: number;
@@ -237,6 +273,8 @@ export interface RangeOptions {
AGGREGATION?: { AGGREGATION?: {
type: TimeSeriesAggregationType; type: TimeSeriesAggregationType;
timeBucket: Timestamp; timeBucket: Timestamp;
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
EMPTY?: boolean;
}; };
} }
@@ -251,9 +289,11 @@ export function pushRangeArguments(
transformTimestampArgument(toTimestamp) transformTimestampArgument(toTimestamp)
); );
pushLatestArgument(args, options?.LATEST);
if (options?.FILTER_BY_TS) { if (options?.FILTER_BY_TS) {
args.push('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)); args.push(transformTimestampArgument(ts));
} }
} }
@@ -286,6 +326,17 @@ export function pushRangeArguments(
options.AGGREGATION.type, options.AGGREGATION.type,
transformTimestampArgument(options.AGGREGATION.timeBucket) transformTimestampArgument(options.AGGREGATION.timeBucket)
); );
if (options.AGGREGATION.BUCKETTIMESTAMP) {
args.push(
'BUCKETTIMESTAMP',
options.AGGREGATION.BUCKETTIMESTAMP
);
}
if (options.AGGREGATION.EMPTY) {
args.push('EMPTY');
}
} }
return args; return args;
@@ -406,3 +457,11 @@ export function transformMRangeWithLabelsReply(reply: MRangeRawReply): Array<MRa
return args; return args;
} }
export function pushLatestArgument(args: RedisCommandArguments, latest?: boolean): RedisCommandArguments {
if (latest) {
args.push('LATEST');
}
return args;
}

View File

@@ -1,3 +1,9 @@
export { default } from './commands'; export { default } from './commands';
export { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from './commands'; export {
TimeSeriesDuplicatePolicies,
TimeSeriesEncoding,
TimeSeriesAggregationType,
TimeSeriesReducers,
TimeSeriesBucketTimestamp
} from './commands';

View File

@@ -4,7 +4,7 @@ import TimeSeries from '.';
export default new TestUtils({ export default new TestUtils({
dockerImageName: 'redislabs/redistimeseries', dockerImageName: 'redislabs/redistimeseries',
dockerImageVersionArgument: 'timeseries-version', dockerImageVersionArgument: 'timeseries-version',
defaultDockerVersion: '1.6.16' defaultDockerVersion: '1.8.0'
}); });
export const GLOBAL = { export const GLOBAL = {