Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions packages/time-series/lib/commands/MRANGE.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ describe('TS.MRANGE', () => {
);
});

it('transformArguments with EXCLUDEEMPTY', () => {
assert.deepEqual(
parseArgs(MRANGE, '-', '+', 'label=value', {
COUNT: 1,
EXCLUDEEMPTY: true
}),
[
'TS.MRANGE', '-', '+',
'COUNT', '1',
'EXCLUDEEMPTY',
'FILTER', 'label=value'
]
);
});

testUtils.testWithClient('client.ts.mRange EXCLUDEEMPTY omits empty series', async client => {
await Promise.all([
client.ts.add('s', 100, 100, { LABELS: { sensor: '1' } }),
client.ts.add('t', 100, 100, { LABELS: { sensor: '1' } }),
client.ts.create('u', { LABELS: { sensor: '1' } })
]);
await client.ts.add('u', 2000, 2000);

const reply = await client.ts.mRange('-', 500, 'sensor=1', {
EXCLUDEEMPTY: true
});

assert.ok('s' in reply);
assert.ok('t' in reply);
assert.ok(!('u' in reply), 'series "u" has no samples in range and must be omitted');
}, {
...GLOBAL.SERVERS.OPEN,
minimumDockerVersion: [8, 10]
});

testUtils.testWithClient('client.ts.mRange', async client => {
const [, reply] = await Promise.all([
client.ts.add('key', 0, 0, {
Expand Down
10 changes: 6 additions & 4 deletions packages/time-series/lib/commands/MRANGE.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers';
import { TsRangeOptions, parseRangeArguments } from './RANGE';
import { parseExcludeEmptyArgument, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers';
import { TsMRangeOptions, parseRangeArguments } from './RANGE';
import { parseFilterArgument } from './MGET';

export type TsMRangeRawReply2 = ArrayReply<
Expand Down Expand Up @@ -32,7 +32,7 @@ export function createTransformMRangeArguments(command: RedisArgument) {
fromTimestamp: Timestamp,
toTimestamp: Timestamp,
filter: RedisVariadicArgument,
options?: TsRangeOptions
options?: TsMRangeOptions
) => {
parser.push(command);
parseRangeArguments(
Expand All @@ -41,7 +41,9 @@ export function createTransformMRangeArguments(command: RedisArgument) {
toTimestamp,
options
);


parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parseFilterArgument(parser, filter);
};
}
Expand Down
21 changes: 21 additions & 0 deletions packages/time-series/lib/commands/MRANGE_MULTIAGGR.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,27 @@ describe('TS.MRANGE_MULTIAGGR', () => {
);
});

it('transformArguments with EXCLUDEEMPTY', () => {
assert.deepEqual(
parseArgs(MRANGE_MULTIAGGR, '-', '+', 'label=value', {
AGGREGATION: {
types: [
TIME_SERIES_AGGREGATION_TYPE.MIN,
TIME_SERIES_AGGREGATION_TYPE.MAX
],
timeBucket: 1
},
EXCLUDEEMPTY: true
}),
[
'TS.MRANGE', '-', '+',
'AGGREGATION', 'MIN,MAX', '1',
'EXCLUDEEMPTY',
'FILTER', 'label=value'
]
);
});

testUtils.testWithClient('client.ts.mRangeMultiAggr', async client => {
await client.ts.add('mrange-multi', 1000, 0, {
LABELS: {
Expand Down
7 changes: 5 additions & 2 deletions packages/time-series/lib/commands/MRANGE_MULTIAGGR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import {
parseExcludeEmptyArgument,
resp2MapToValue,
resp3MapToValue,
MultiAggregationSampleRawReply,
Timestamp,
transformMultiAggregationSamplesReply
} from './helpers';
import { TsRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { TsMRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { parseFilterArgument } from './MGET';

export type TsMRangeMultiRawReply2 = ArrayReply<
Expand Down Expand Up @@ -38,7 +39,7 @@ export function createTransformMRangeMultiArguments(command: RedisArgument) {
fromTimestamp: Timestamp,
toTimestamp: Timestamp,
filter: RedisVariadicArgument,
options: TsRangeMultiAggrOptions
options: TsMRangeMultiAggrOptions
) => {
parser.push(command);
parseRangeMultiArguments(
Expand All @@ -48,6 +49,8 @@ export function createTransformMRangeMultiArguments(command: RedisArgument) {
options
);

parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parseFilterArgument(parser, filter);
};
}
Expand Down
10 changes: 6 additions & 4 deletions packages/time-series/lib/commands/MRANGE_SELECTED_LABELS.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import { parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from './helpers';
import { TsRangeOptions, parseRangeArguments } from './RANGE';
import { parseExcludeEmptyArgument, parseSelectedLabelsArguments, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformRESP2Labels, transformSamplesReply } from './helpers';
import { TsMRangeOptions, parseRangeArguments } from './RANGE';
import { parseFilterArgument } from './MGET';

export type TsMRangeSelectedLabelsRawReply2 = ArrayReply<
Expand Down Expand Up @@ -38,7 +38,7 @@ export function createTransformMRangeSelectedLabelsArguments(command: RedisArgum
toTimestamp: Timestamp,
selectedLabels: RedisVariadicArgument,
filter: RedisVariadicArgument,
options?: TsRangeOptions
options?: TsMRangeOptions
) => {
parser.push(command);
parseRangeArguments(
Expand All @@ -47,7 +47,9 @@ export function createTransformMRangeSelectedLabelsArguments(command: RedisArgum
toTimestamp,
options
);


parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parseSelectedLabelsArguments(parser, selectedLabels);

parseFilterArgument(parser, filter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, NullReply, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import {
parseExcludeEmptyArgument,
parseSelectedLabelsArguments,
resp2MapToValue,
resp3MapToValue,
Expand All @@ -10,7 +11,7 @@ import {
transformRESP2Labels,
transformMultiAggregationSamplesReply
} from './helpers';
import { TsRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { TsMRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { parseFilterArgument } from './MGET';

export type TsMRangeSelectedLabelsMultiRawReply2 = ArrayReply<
Expand Down Expand Up @@ -44,7 +45,7 @@ export function createTransformMRangeSelectedLabelsMultiArguments(command: Redis
toTimestamp: Timestamp,
selectedLabels: RedisVariadicArgument,
filter: RedisVariadicArgument,
options: TsRangeMultiAggrOptions
options: TsMRangeMultiAggrOptions
) => {
parser.push(command);
parseRangeMultiArguments(
Expand All @@ -54,6 +55,8 @@ export function createTransformMRangeSelectedLabelsMultiArguments(command: Redis
options
);

parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parseSelectedLabelsArguments(parser, selectedLabels);

parseFilterArgument(parser, filter);
Expand Down
14 changes: 14 additions & 0 deletions packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ describe('TS.MRANGE_WITHLABELS', () => {
);
});

it('transformArguments with EXCLUDEEMPTY', () => {
assert.deepEqual(
parseArgs(MRANGE_WITHLABELS, '-', '+', 'label=value', {
EXCLUDEEMPTY: true
}),
[
'TS.MRANGE', '-', '+',
'EXCLUDEEMPTY',
'WITHLABELS',
'FILTER', 'label=value'
]
);
});

testUtils.testWithClient('client.ts.mRangeWithLabels', async client => {
const [, reply] = await Promise.all([
client.ts.add('key', 0, 0, {
Expand Down
8 changes: 5 additions & 3 deletions packages/time-series/lib/commands/MRANGE_WITHLABELS.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import { resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers';
import { TsRangeOptions, parseRangeArguments } from './RANGE';
import { parseExcludeEmptyArgument, resp2MapToValue, resp3MapToValue, SampleRawReply, Timestamp, transformSamplesReply } from './helpers';
import { TsMRangeOptions, parseRangeArguments } from './RANGE';
import { parseFilterArgument } from './MGET';

export type TsMRangeWithLabelsRawReply2 = ArrayReply<
Expand Down Expand Up @@ -35,7 +35,7 @@ export function createTransformMRangeWithLabelsArguments(command: RedisArgument)
fromTimestamp: Timestamp,
toTimestamp: Timestamp,
filter: RedisVariadicArgument,
options?: TsRangeOptions
options?: TsMRangeOptions
) => {
parser.push(command);
parseRangeArguments(
Expand All @@ -45,6 +45,8 @@ export function createTransformMRangeWithLabelsArguments(command: RedisArgument)
options
);

parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parser.push('WITHLABELS');

parseFilterArgument(parser, filter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { CommandParser } from '@redis/client/dist/lib/client/parser';
import { Command, UnwrapReply, ArrayReply, BlobStringReply, Resp2Reply, MapReply, TuplesReply, TypeMapping, RedisArgument } from '@redis/client/dist/lib/RESP/types';
import { RedisVariadicArgument } from '@redis/client/dist/lib/commands/generic-transformers';
import {
parseExcludeEmptyArgument,
resp2MapToValue,
resp3MapToValue,
MultiAggregationSampleRawReply,
Timestamp,
transformMultiAggregationSamplesReply
} from './helpers';
import { TsRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { TsMRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR';
import { parseFilterArgument } from './MGET';

export type TsMRangeWithLabelsMultiRawReply2 = ArrayReply<
Expand Down Expand Up @@ -41,7 +42,7 @@ export function createTransformMRangeWithLabelsMultiArguments(command: RedisArgu
fromTimestamp: Timestamp,
toTimestamp: Timestamp,
filter: RedisVariadicArgument,
options: TsRangeMultiAggrOptions
options: TsMRangeMultiAggrOptions
) => {
parser.push(command);
parseRangeMultiArguments(
Expand All @@ -51,6 +52,8 @@ export function createTransformMRangeWithLabelsMultiArguments(command: RedisArgu
options
);

parseExcludeEmptyArgument(parser, options?.EXCLUDEEMPTY);

parser.push('WITHLABELS');

parseFilterArgument(parser, filter);
Expand Down
35 changes: 35 additions & 0 deletions packages/time-series/lib/commands/MREVRANGE.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,41 @@ describe('TS.MREVRANGE', () => {
);
});

it('transformArguments with EXCLUDEEMPTY', () => {
assert.deepEqual(
parseArgs(MREVRANGE, '-', '+', 'label=value', {
COUNT: 1,
EXCLUDEEMPTY: true
}),
[
'TS.MREVRANGE', '-', '+',
'COUNT', '1',
'EXCLUDEEMPTY',
'FILTER', 'label=value'
]
);
});

testUtils.testWithClient('client.ts.mRevRange EXCLUDEEMPTY omits empty series', async client => {
await Promise.all([
client.ts.add('s', 100, 100, { LABELS: { sensor: '1' } }),
client.ts.add('t', 100, 100, { LABELS: { sensor: '1' } }),
client.ts.create('u', { LABELS: { sensor: '1' } })
]);
await client.ts.add('u', 2000, 2000);

const reply = await client.ts.mRevRange('-', 500, 'sensor=1', {
EXCLUDEEMPTY: true
});

assert.ok('s' in reply);
assert.ok('t' in reply);
assert.ok(!('u' in reply), 'series "u" has no samples in range and must be omitted');
}, {
...GLOBAL.SERVERS.OPEN,
minimumDockerVersion: [8, 10]
});

testUtils.testWithClient('client.ts.mRevRange', async client => {
const [, reply] = await Promise.all([
client.ts.add('key', 0, 0, {
Expand Down
14 changes: 14 additions & 0 deletions packages/time-series/lib/commands/RANGE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ export interface TsRangeOptions extends TsRangeCommonOptions {
};
}

/**
* `TS.MRANGE`/`TS.MREVRANGE` (non-`GROUPBY`) options: the single-key range options
* plus the multi-range-only `EXCLUDEEMPTY` flag.
*/
export interface TsMRangeOptions extends TsRangeOptions {
/**
* Omit matching series whose reported samples array is empty from the reply.
* Cannot be combined with `GROUPBY`.
*
* @since Redis 8.10
*/
EXCLUDEEMPTY?: boolean;
}

export function parseRangeArguments(
parser: CommandParser,
fromTimestamp: Timestamp,
Expand Down
14 changes: 14 additions & 0 deletions packages/time-series/lib/commands/RANGE_MULTIAGGR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,20 @@ export interface TsRangeMultiAggrOptions extends TsRangeCommonOptions {
};
}

/**
* `TS.MRANGE`/`TS.MREVRANGE` multi-aggregation (non-`GROUPBY`) options: the
* multi-aggregation range options plus the multi-range-only `EXCLUDEEMPTY` flag.
*/
export interface TsMRangeMultiAggrOptions extends TsRangeMultiAggrOptions {
/**
* Omit matching series whose reported samples array is empty from the reply.
* Cannot be combined with `GROUPBY`.
*
* @since Redis 8.10
*/
EXCLUDEEMPTY?: boolean;
}

export function parseRangeMultiArguments(
parser: CommandParser,
fromTimestamp: Timestamp,
Expand Down
13 changes: 13 additions & 0 deletions packages/time-series/lib/commands/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ export function parseRangeCommonArguments(
}
}

/**
* Pushes the bare `EXCLUDEEMPTY` flag for `TS.MRANGE`/`TS.MREVRANGE` when requested.
* The flag omits matching series with an empty samples array from the reply. It is
* not valid on single-key `TS.RANGE`/`TS.REVRANGE` nor combinable with `GROUPBY`.
*
* @since Redis 8.10
*/
export function parseExcludeEmptyArgument(parser: CommandParser, excludeEmpty?: boolean) {
if (excludeEmpty) {
parser.push('EXCLUDEEMPTY');
}
}

export type Labels = {
[label: string]: string;
};
Expand Down