-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat(time-series): add multi-aggregation range APIs #3249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nkaradzhov
wants to merge
4
commits into
redis:master
Choose a base branch
from
nkaradzhov:ts-multiple-aggregators
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
cf76a1e
feat(time-series): add multi-aggregation range APIs with MultiAggr na…
nkaradzhov 16e2160
test(time-series): add server tests for reverse multiaggr commands
nkaradzhov a17df68
docs(time-series): use TIME_SERIES_AGGREGATION_TYPE in README examples
nkaradzhov 1479e42
Add not-keyed metadata to selected labels multi-aggregation
nkaradzhov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
76 changes: 76 additions & 0 deletions
76
packages/time-series/lib/commands/MRANGE_MULTIAGGR.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| import { strict as assert } from 'node:assert'; | ||
| import testUtils, { GLOBAL } from '../test-utils'; | ||
| import MRANGE_MULTIAGGR from './MRANGE_MULTIAGGR'; | ||
| import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; | ||
| import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; | ||
|
|
||
| describe('TS.MRANGE_MULTIAGGR', () => { | ||
| it('transformArguments', () => { | ||
| assert.deepEqual( | ||
| parseArgs(MRANGE_MULTIAGGR, '-', '+', 'label=value', { | ||
| LATEST: true, | ||
| FILTER_BY_TS: [0], | ||
| FILTER_BY_VALUE: { | ||
| min: 0, | ||
| max: 1 | ||
| }, | ||
| COUNT: 1, | ||
| ALIGN: '-', | ||
| AGGREGATION: { | ||
| types: [ | ||
| TIME_SERIES_AGGREGATION_TYPE.MIN, | ||
| TIME_SERIES_AGGREGATION_TYPE.MAX | ||
| ], | ||
| timeBucket: 1 | ||
| } | ||
| }), | ||
| [ | ||
| 'TS.MRANGE', '-', '+', | ||
| 'LATEST', | ||
| 'FILTER_BY_TS', '0', | ||
| 'FILTER_BY_VALUE', '0', '1', | ||
| 'COUNT', '1', | ||
| 'ALIGN', '-', | ||
| 'AGGREGATION', 'MIN,MAX', '1', | ||
| 'FILTER', 'label=value' | ||
| ] | ||
| ); | ||
| }); | ||
|
|
||
| testUtils.testWithClient('client.ts.mRangeMultiAggr', async client => { | ||
| await client.ts.add('mrange-multi', 1000, 0, { | ||
| LABELS: { | ||
| label: 'value' | ||
| } | ||
| }); | ||
| await client.ts.add('mrange-multi', 1001, 1); | ||
| await client.ts.add('mrange-multi', 1002, 2); | ||
|
|
||
| const reply = await client.ts.mRangeMultiAggr('-', '+', 'label=value', { | ||
| AGGREGATION: { | ||
| types: [ | ||
| TIME_SERIES_AGGREGATION_TYPE.MIN, | ||
| TIME_SERIES_AGGREGATION_TYPE.MAX | ||
| ], | ||
| timeBucket: 10 | ||
| } | ||
| }); | ||
|
|
||
| assert.deepStrictEqual( | ||
| reply, | ||
| Object.create(null, { | ||
| 'mrange-multi': { | ||
| configurable: true, | ||
| enumerable: true, | ||
| value: [{ | ||
| timestamp: 1000, | ||
| values: [0, 2] | ||
| }] | ||
| } | ||
| }) | ||
| ); | ||
| }, { | ||
| ...GLOBAL.SERVERS.OPEN, | ||
| minimumDockerVersion: [8, 8] | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| 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, | ||
| MultiAggregationSampleRawReply, | ||
| Timestamp, | ||
| transformMultiAggregationSamplesReply | ||
| } from './helpers'; | ||
| import { TsRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR'; | ||
| import { parseFilterArgument } from './MGET'; | ||
|
|
||
| export type TsMRangeMultiRawReply2 = ArrayReply< | ||
| TuplesReply<[ | ||
| key: BlobStringReply, | ||
| labels: never, // empty array without WITHLABELS or SELECTED_LABELS | ||
| samples: ArrayReply<Resp2Reply<MultiAggregationSampleRawReply>> | ||
| ]> | ||
| >; | ||
|
|
||
| export type TsMRangeMultiRawReply3 = MapReply< | ||
| BlobStringReply, | ||
| TuplesReply<[ | ||
| labels: never, // empty hash without WITHLABELS or SELECTED_LABELS | ||
| metadata: never, // ?! | ||
| samples: ArrayReply<MultiAggregationSampleRawReply> | ||
| ]> | ||
| >; | ||
|
|
||
| /** | ||
| * Creates a function that parses arguments for multi-range commands with multiple aggregators | ||
| * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) | ||
| */ | ||
| export function createTransformMRangeMultiArguments(command: RedisArgument) { | ||
| return ( | ||
| parser: CommandParser, | ||
| fromTimestamp: Timestamp, | ||
| toTimestamp: Timestamp, | ||
| filter: RedisVariadicArgument, | ||
| options: TsRangeMultiAggrOptions | ||
| ) => { | ||
| parser.push(command); | ||
| parseRangeMultiArguments( | ||
| parser, | ||
| fromTimestamp, | ||
| toTimestamp, | ||
| options | ||
| ); | ||
|
|
||
| parseFilterArgument(parser, filter); | ||
| }; | ||
| } | ||
|
|
||
| export default { | ||
| NOT_KEYED_COMMAND: true, | ||
| IS_READ_ONLY: true, | ||
| /** | ||
| * Gets multi-aggregation samples for time series matching a specific filter within a time range | ||
| * @param parser - The command parser | ||
| * @param fromTimestamp - Start timestamp for range | ||
| * @param toTimestamp - End timestamp for range | ||
| * @param filter - Filter to match time series keys | ||
| * @param options - Optional parameters for the command | ||
| */ | ||
| parseCommand: createTransformMRangeMultiArguments('TS.MRANGE'), | ||
| transformReply: { | ||
| 2(reply: TsMRangeMultiRawReply2, _?: any, typeMapping?: TypeMapping) { | ||
| return resp2MapToValue(reply, ([_key, _labels, samples]) => { | ||
| return transformMultiAggregationSamplesReply[2](samples); | ||
| }, typeMapping); | ||
| }, | ||
| 3(reply: TsMRangeMultiRawReply3) { | ||
| return resp3MapToValue(reply, ([_labels, _metadata, samples]) => { | ||
| return transformMultiAggregationSamplesReply[3](samples); | ||
| }); | ||
| } | ||
| }, | ||
| } as const satisfies Command; |
87 changes: 87 additions & 0 deletions
87
packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_MULTIAGGR.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| import { strict as assert } from 'node:assert'; | ||
| import testUtils, { GLOBAL } from '../test-utils'; | ||
| import MRANGE_SELECTED_LABELS_MULTIAGGR from './MRANGE_SELECTED_LABELS_MULTIAGGR'; | ||
| import { TIME_SERIES_AGGREGATION_TYPE } from './CREATERULE'; | ||
| import { parseArgs } from '@redis/client/lib/commands/generic-transformers'; | ||
|
|
||
| describe('TS.MRANGE_SELECTED_LABELS_MULTIAGGR', () => { | ||
| it('transformArguments', () => { | ||
| assert.deepEqual( | ||
| parseArgs(MRANGE_SELECTED_LABELS_MULTIAGGR, '-', '+', 'label', 'label=value', { | ||
| FILTER_BY_TS: [0], | ||
| FILTER_BY_VALUE: { | ||
| min: 0, | ||
| max: 1 | ||
| }, | ||
| COUNT: 1, | ||
| ALIGN: '-', | ||
| AGGREGATION: { | ||
| types: [ | ||
| TIME_SERIES_AGGREGATION_TYPE.MIN, | ||
| TIME_SERIES_AGGREGATION_TYPE.MAX | ||
| ], | ||
| timeBucket: 1 | ||
| } | ||
| }), | ||
| [ | ||
| 'TS.MRANGE', '-', '+', | ||
| 'FILTER_BY_TS', '0', | ||
| 'FILTER_BY_VALUE', '0', '1', | ||
| 'COUNT', '1', | ||
| 'ALIGN', '-', | ||
| 'AGGREGATION', 'MIN,MAX', '1', | ||
| 'SELECTED_LABELS', 'label', | ||
| 'FILTER', 'label=value' | ||
| ] | ||
| ); | ||
| }); | ||
|
|
||
| testUtils.testWithClient('client.ts.mRangeSelectedLabelsMultiAggr', async client => { | ||
| await client.ts.add('mrange-selectedlabels-multi', 1000, 0, { | ||
| LABELS: { label: 'value' } | ||
| }); | ||
| await client.ts.add('mrange-selectedlabels-multi', 1001, 1); | ||
| await client.ts.add('mrange-selectedlabels-multi', 1002, 2); | ||
|
|
||
| const reply = await client.ts.mRangeSelectedLabelsMultiAggr('-', '+', ['label', 'NX'], 'label=value', { | ||
| AGGREGATION: { | ||
| types: [ | ||
| TIME_SERIES_AGGREGATION_TYPE.MIN, | ||
| TIME_SERIES_AGGREGATION_TYPE.MAX | ||
| ], | ||
| timeBucket: 10 | ||
| } | ||
| }); | ||
|
|
||
| assert.deepStrictEqual( | ||
| reply, | ||
| Object.create(null, { | ||
| 'mrange-selectedlabels-multi': { | ||
| configurable: true, | ||
| enumerable: true, | ||
| value: { | ||
| labels: Object.create(null, { | ||
| label: { | ||
| configurable: true, | ||
| enumerable: true, | ||
| value: 'value' | ||
| }, | ||
| NX: { | ||
| configurable: true, | ||
| enumerable: true, | ||
| value: null | ||
| } | ||
| }), | ||
| samples: [{ | ||
| timestamp: 1000, | ||
| values: [0, 2] | ||
| }] | ||
| } | ||
| } | ||
| }) | ||
| ); | ||
| }, { | ||
| ...GLOBAL.SERVERS.OPEN, | ||
| minimumDockerVersion: [8, 8] | ||
| }); | ||
| }); |
94 changes: 94 additions & 0 deletions
94
packages/time-series/lib/commands/MRANGE_SELECTED_LABELS_MULTIAGGR.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| 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, | ||
| MultiAggregationSampleRawReply, | ||
| Timestamp, | ||
| transformRESP2Labels, | ||
| transformMultiAggregationSamplesReply | ||
| } from './helpers'; | ||
| import { TsRangeMultiAggrOptions, parseRangeMultiArguments } from './RANGE_MULTIAGGR'; | ||
| import { parseFilterArgument } from './MGET'; | ||
|
|
||
| export type TsMRangeSelectedLabelsMultiRawReply2 = ArrayReply< | ||
| TuplesReply<[ | ||
| key: BlobStringReply, | ||
| labels: ArrayReply<TuplesReply<[ | ||
| label: BlobStringReply, | ||
| value: BlobStringReply | NullReply | ||
| ]>>, | ||
| samples: ArrayReply<Resp2Reply<MultiAggregationSampleRawReply>> | ||
| ]> | ||
| >; | ||
|
|
||
| export type TsMRangeSelectedLabelsMultiRawReply3 = MapReply< | ||
| BlobStringReply, | ||
| TuplesReply<[ | ||
| labels: MapReply<BlobStringReply, BlobStringReply | NullReply>, | ||
| metadata: never, // ?! | ||
| samples: ArrayReply<MultiAggregationSampleRawReply> | ||
| ]> | ||
| >; | ||
|
|
||
| /** | ||
| * Creates a function that parses arguments for multi-range commands with selected labels and multiple aggregators | ||
| * @param command - The command name to use (TS.MRANGE or TS.MREVRANGE) | ||
| */ | ||
| export function createTransformMRangeSelectedLabelsMultiArguments(command: RedisArgument) { | ||
| return ( | ||
| parser: CommandParser, | ||
| fromTimestamp: Timestamp, | ||
| toTimestamp: Timestamp, | ||
| selectedLabels: RedisVariadicArgument, | ||
| filter: RedisVariadicArgument, | ||
| options: TsRangeMultiAggrOptions | ||
| ) => { | ||
| parser.push(command); | ||
| parseRangeMultiArguments( | ||
| parser, | ||
| fromTimestamp, | ||
| toTimestamp, | ||
| options | ||
| ); | ||
|
|
||
| parseSelectedLabelsArguments(parser, selectedLabels); | ||
|
|
||
| parseFilterArgument(parser, filter); | ||
| }; | ||
| } | ||
|
|
||
| export default { | ||
| NOT_KEYED_COMMAND: true, | ||
| IS_READ_ONLY: true, | ||
| /** | ||
| * Gets multi-aggregation samples for time series matching a filter with selected labels | ||
| * @param parser - The command parser | ||
| * @param fromTimestamp - Start timestamp for range | ||
| * @param toTimestamp - End timestamp for range | ||
| * @param selectedLabels - Labels to include in the output | ||
| * @param filter - Filter to match time series keys | ||
| * @param options - Optional parameters for the command | ||
| */ | ||
| parseCommand: createTransformMRangeSelectedLabelsMultiArguments('TS.MRANGE'), | ||
| transformReply: { | ||
| 2(reply: TsMRangeSelectedLabelsMultiRawReply2, _?: any, typeMapping?: TypeMapping) { | ||
| return resp2MapToValue(reply, ([_key, labels, samples]) => { | ||
| return { | ||
| labels: transformRESP2Labels(labels, typeMapping), | ||
| samples: transformMultiAggregationSamplesReply[2](samples) | ||
| }; | ||
| }, typeMapping); | ||
| }, | ||
| 3(reply: TsMRangeSelectedLabelsMultiRawReply3) { | ||
| return resp3MapToValue(reply, ([labels, _metadata, samples]) => { | ||
| return { | ||
| labels, | ||
| samples: transformMultiAggregationSamplesReply[3](samples) | ||
| }; | ||
| }); | ||
| } | ||
| }, | ||
| } as const satisfies Command; | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.