0
# Time Series
1
2
Time series data operations for storing and querying time-stamped data with aggregation rules and downsampling using RedisTimeSeries. Ideal for metrics, IoT data, financial data, and monitoring applications.
3
4
## Capabilities
5
6
### Series Management
7
8
Operations for creating and managing time series with metadata and configuration.
9
10
```typescript { .api }
11
/**
12
* Create a new time series
13
* @param key - Time series key
14
* @param options - Optional series configuration
15
* @returns 'OK'
16
*/
17
function create(key: RedisArgument, options?: TsCreateOptions): Promise<SimpleStringReply<'OK'>>;
18
19
/**
20
* Alter time series configuration
21
* @param key - Time series key
22
* @param options - Configuration changes
23
* @returns 'OK'
24
*/
25
function alter(key: RedisArgument, options: TsAlterOptions): Promise<SimpleStringReply<'OK'>>;
26
27
/**
28
* Get time series information and statistics
29
* @param key - Time series key
30
* @returns Array containing series metadata and stats
31
*/
32
function info(key: RedisArgument): Promise<ArrayReply>;
33
34
/**
35
* Get detailed debug information about time series
36
* @param key - Time series key
37
* @returns Array containing debug information
38
*/
39
function infoDebug(key: RedisArgument): Promise<ArrayReply>;
40
41
/**
42
* Query time series index by labels
43
* @param filters - Label filter expressions
44
* @returns Array of matching time series keys
45
*/
46
function queryIndex(...filters: string[]): Promise<ArrayReply<BlobStringReply>>;
47
48
interface TsCreateOptions {
49
/** Data retention period in milliseconds */
50
RETENTION?: number;
51
/** Encoding algorithm */
52
ENCODING?: TimeSeriesEncoding;
53
/** Chunk size in bytes */
54
CHUNK_SIZE?: number;
55
/** Duplicate sample policy */
56
DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies;
57
/** Labels for the series */
58
LABELS?: Record<string, string>;
59
}
60
61
interface TsAlterOptions {
62
/** Data retention period in milliseconds */
63
RETENTION?: number;
64
/** Chunk size in bytes */
65
CHUNK_SIZE?: number;
66
/** Duplicate sample policy */
67
DUPLICATE_POLICY?: TimeSeriesDuplicatePolicies;
68
/** Labels for the series */
69
LABELS?: Record<string, string>;
70
}
71
72
type TimeSeriesEncoding = 'COMPRESSED' | 'UNCOMPRESSED';
73
type TimeSeriesDuplicatePolicies = 'BLOCK' | 'FIRST' | 'LAST' | 'MIN' | 'MAX' | 'SUM';
74
```
75
76
**Usage Examples:**
77
78
```typescript
79
import { createClient } from "redis";
80
81
const client = createClient();
82
await client.connect();
83
84
// Create time series with configuration
85
await client.ts.create("temperature:sensor1", {
86
RETENTION: 86400000, // 24 hours in milliseconds
87
LABELS: {
88
sensor: "temperature",
89
location: "room1",
90
building: "office"
91
},
92
DUPLICATE_POLICY: 'LAST',
93
ENCODING: 'COMPRESSED'
94
});
95
96
// Create multiple related series
97
await client.ts.create("humidity:sensor1", {
98
RETENTION: 86400000,
99
LABELS: {
100
sensor: "humidity",
101
location: "room1",
102
building: "office"
103
}
104
});
105
106
// Get series information
107
const info = await client.ts.info("temperature:sensor1");
108
console.log("Series info:", info);
109
110
// Query by labels
111
const temperatureSensors = await client.ts.queryIndex("sensor=temperature");
112
const room1Sensors = await client.ts.queryIndex("location=room1");
113
const allOfficeSensors = await client.ts.queryIndex("building=office");
114
115
// Alter existing series
116
await client.ts.alter("temperature:sensor1", {
117
RETENTION: 172800000, // Extend to 48 hours
118
LABELS: {
119
sensor: "temperature",
120
location: "room1",
121
building: "office",
122
calibrated: "2023-01-01"
123
}
124
});
125
```
126
127
### Data Operations
128
129
Core operations for adding, retrieving, and manipulating time series data points.
130
131
```typescript { .api }
132
/**
133
* Add sample to time series
134
* @param key - Time series key
135
* @param timestamp - Unix timestamp in milliseconds, or '*' for current time
136
* @param value - Numeric value
137
* @param options - Optional add parameters
138
* @returns The timestamp of the added sample
139
*/
140
function add(key: RedisArgument, timestamp: number | '*', value: number, options?: TsAddOptions): Promise<NumberReply>;
141
142
/**
143
* Add multiple samples to multiple time series
144
* @param samples - Array of sample specifications
145
* @returns Array of timestamps for added samples
146
*/
147
function mAdd(...samples: TsSample[]): Promise<ArrayReply<NumberReply>>;
148
149
/**
150
* Increment time series value
151
* @param key - Time series key
152
* @param value - Value to increment by
153
* @param options - Optional increment parameters
154
* @returns The timestamp of the incremented sample
155
*/
156
function incrBy(key: RedisArgument, value: number, options?: TsIncrByOptions): Promise<NumberReply>;
157
158
/**
159
* Decrement time series value
160
* @param key - Time series key
161
* @param value - Value to decrement by
162
* @param options - Optional decrement parameters
163
* @returns The timestamp of the decremented sample
164
*/
165
function decrBy(key: RedisArgument, value: number, options?: TsDecrByOptions): Promise<NumberReply>;
166
167
/**
168
* Delete samples in time range
169
* @param key - Time series key
170
* @param fromTimestamp - Start timestamp (inclusive)
171
* @param toTimestamp - End timestamp (inclusive)
172
* @returns Number of samples deleted
173
*/
174
function del(key: RedisArgument, fromTimestamp: number, toTimestamp: number): Promise<NumberReply>;
175
176
interface TsAddOptions {
177
/** Data retention period in milliseconds */
178
RETENTION?: number;
179
/** Encoding algorithm */
180
ENCODING?: TimeSeriesEncoding;
181
/** Chunk size in bytes */
182
CHUNK_SIZE?: number;
183
/** Only add if key doesn't exist */
184
ON_DUPLICATE?: TimeSeriesDuplicatePolicies;
185
/** Labels for new series */
186
LABELS?: Record<string, string>;
187
}
188
189
interface TsIncrByOptions extends TsAddOptions {
190
/** Custom timestamp (default: current time) */
191
TIMESTAMP?: number;
192
}
193
194
interface TsDecrByOptions extends TsAddOptions {
195
/** Custom timestamp (default: current time) */
196
TIMESTAMP?: number;
197
}
198
199
interface TsSample {
200
key: RedisArgument;
201
timestamp: number | '*';
202
value: number;
203
}
204
```
205
206
**Usage Examples:**
207
208
```typescript
209
// Add single sample with current timestamp
210
const timestamp1 = await client.ts.add("temperature:sensor1", "*", 23.5);
211
212
// Add sample with specific timestamp
213
const timestamp2 = await client.ts.add("temperature:sensor1", Date.now(), 24.1);
214
215
// Add sample with options (auto-create series)
216
const timestamp3 = await client.ts.add("pressure:sensor2", "*", 1013.25, {
217
LABELS: {
218
sensor: "pressure",
219
location: "room1",
220
unit: "hPa"
221
},
222
RETENTION: 86400000,
223
ON_DUPLICATE: 'LAST'
224
});
225
226
// Add multiple samples at once
227
await client.ts.mAdd(
228
{ key: "temperature:sensor1", timestamp: "*", value: 22.8 },
229
{ key: "humidity:sensor1", timestamp: "*", value: 65.2 },
230
{ key: "pressure:sensor1", timestamp: "*", value: 1012.8 }
231
);
232
233
// Increment counter-style metrics
234
const counterTs = await client.ts.incrBy("requests:total", 1, {
235
LABELS: { metric: "requests", service: "api" }
236
});
237
238
// Decrement with custom timestamp
239
await client.ts.decrBy("available:slots", 5, {
240
TIMESTAMP: Date.now() - 1000 // 1 second ago
241
});
242
243
// Delete old data
244
const deletedCount = await client.ts.del(
245
"temperature:sensor1",
246
Date.now() - 7 * 24 * 60 * 60 * 1000, // 7 days ago
247
Date.now() - 24 * 60 * 60 * 1000 // 1 day ago
248
);
249
```
250
251
### Data Retrieval
252
253
Operations for querying time series data with various filtering and aggregation options.
254
255
```typescript { .api }
256
/**
257
* Get latest sample from time series
258
* @param key - Time series key
259
* @returns Array containing timestamp and value, or null if empty
260
*/
261
function get(key: RedisArgument): Promise<ArrayReply | null>;
262
263
/**
264
* Get latest samples from multiple time series
265
* @param keys - Array of time series keys
266
* @param options - Optional filter parameters
267
* @returns Array of time series data with latest samples
268
*/
269
function mGet(keys: RedisArgument[], options?: TsMGetOptions): Promise<ArrayReply>;
270
271
/**
272
* Get latest samples from multiple time series with labels
273
* @param keys - Array of time series keys
274
* @param options - Optional filter parameters
275
* @returns Array of time series data with labels and latest samples
276
*/
277
function mGetWithLabels(keys: RedisArgument[], options?: TsMGetOptions): Promise<ArrayReply>;
278
279
/**
280
* Get samples from time range
281
* @param key - Time series key
282
* @param fromTimestamp - Start timestamp (inclusive)
283
* @param toTimestamp - End timestamp (inclusive)
284
* @param options - Optional range parameters
285
* @returns Array of timestamp-value pairs
286
*/
287
function range(key: RedisArgument, fromTimestamp: number, toTimestamp: number, options?: TsRangeOptions): Promise<ArrayReply>;
288
289
/**
290
* Get samples from time range in reverse order
291
* @param key - Time series key
292
* @param fromTimestamp - Start timestamp (inclusive)
293
* @param toTimestamp - End timestamp (inclusive)
294
* @param options - Optional range parameters
295
* @returns Array of timestamp-value pairs in reverse order
296
*/
297
function revRange(key: RedisArgument, fromTimestamp: number, toTimestamp: number, options?: TsRangeOptions): Promise<ArrayReply>;
298
299
interface TsMGetOptions {
300
/** Label filters */
301
FILTER?: string[];
302
/** Include series labels in response */
303
WITHLABELS?: boolean;
304
/** Include only selected labels */
305
SELECTED_LABELS?: string[];
306
}
307
308
interface TsRangeOptions {
309
/** Filter by minimum value */
310
FILTER_BY_TS?: number[];
311
/** Filter by value range */
312
FILTER_BY_VALUE?: [min: number, max: number];
313
/** Limit number of samples */
314
COUNT?: number;
315
/** Align timestamps to bucket */
316
ALIGN?: number;
317
/** Aggregation configuration */
318
AGGREGATION?: {
319
type: TimeSeriesAggregationType;
320
bucketDuration: number;
321
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
322
EMPTY?: boolean;
323
};
324
}
325
326
type TimeSeriesAggregationType = 'AVG' | 'SUM' | 'MIN' | 'MAX' | 'RANGE' | 'COUNT' | 'STD.P' | 'STD.S' | 'VAR.P' | 'VAR.S' | 'FIRST' | 'LAST';
327
type TimeSeriesBucketTimestamp = '-' | 'low' | 'high' | 'mid';
328
```
329
330
**Usage Examples:**
331
332
```typescript
333
// Get latest sample
334
const latest = await client.ts.get("temperature:sensor1");
335
if (latest) {
336
const [timestamp, value] = latest;
337
console.log(`Latest: ${value}°C at ${new Date(timestamp)}`);
338
}
339
340
// Get latest from multiple series
341
const latestMultiple = await client.ts.mGet([
342
"temperature:sensor1",
343
"humidity:sensor1",
344
"pressure:sensor1"
345
]);
346
347
// Get latest with labels
348
const latestWithLabels = await client.ts.mGetWithLabels([
349
"temperature:sensor1"
350
], {
351
WITHLABELS: true
352
});
353
354
// Get range of data
355
const hourlyData = await client.ts.range(
356
"temperature:sensor1",
357
Date.now() - 60 * 60 * 1000, // 1 hour ago
358
Date.now() // now
359
);
360
361
// Get aggregated data (5-minute averages)
362
const averageData = await client.ts.range(
363
"temperature:sensor1",
364
Date.now() - 24 * 60 * 60 * 1000, // 24 hours ago
365
Date.now(),
366
{
367
AGGREGATION: {
368
type: 'AVG',
369
bucketDuration: 5 * 60 * 1000, // 5 minutes
370
BUCKETTIMESTAMP: 'mid'
371
}
372
}
373
);
374
375
// Get filtered range
376
const filteredData = await client.ts.range(
377
"temperature:sensor1",
378
Date.now() - 60 * 60 * 1000,
379
Date.now(),
380
{
381
FILTER_BY_VALUE: [20, 30], // Only values between 20-30
382
COUNT: 100 // Limit to 100 samples
383
}
384
);
385
386
// Get reverse chronological data
387
const recentData = await client.ts.revRange(
388
"temperature:sensor1",
389
Date.now() - 60 * 60 * 1000,
390
Date.now(),
391
{
392
COUNT: 10 // Last 10 samples
393
}
394
);
395
```
396
397
### Multi-Series Queries
398
399
Advanced querying operations across multiple time series with filtering and aggregation.
400
401
```typescript { .api }
402
/**
403
* Query multiple time series by labels in time range
404
* @param fromTimestamp - Start timestamp
405
* @param toTimestamp - End timestamp
406
* @param filters - Label filter expressions
407
* @param options - Optional query parameters
408
* @returns Array of time series data
409
*/
410
function mRange(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
411
412
/**
413
* Query multiple time series with labels in time range
414
* @param fromTimestamp - Start timestamp
415
* @param toTimestamp - End timestamp
416
* @param filters - Label filter expressions
417
* @param options - Optional query parameters
418
* @returns Array of time series data with labels
419
*/
420
function mRangeWithLabels(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
421
422
/**
423
* Query multiple time series in reverse order
424
* @param fromTimestamp - Start timestamp
425
* @param toTimestamp - End timestamp
426
* @param filters - Label filter expressions
427
* @param options - Optional query parameters
428
* @returns Array of time series data in reverse order
429
*/
430
function mRevRange(fromTimestamp: number, toTimestamp: number, filters: string[], options?: TsMRangeOptions): Promise<ArrayReply>;
431
432
/**
433
* Query with grouping and reduction
434
* @param fromTimestamp - Start timestamp
435
* @param toTimestamp - End timestamp
436
* @param filters - Label filter expressions
437
* @param groupBy - Labels to group by
438
* @param reducer - Reduction function
439
* @param options - Optional query parameters
440
* @returns Array of grouped and reduced time series data
441
*/
442
function mRangeGroupBy(
443
fromTimestamp: number,
444
toTimestamp: number,
445
filters: string[],
446
groupBy: string,
447
reducer: TimeSeriesReducer,
448
options?: TsMRangeOptions
449
): Promise<ArrayReply>;
450
451
interface TsMRangeOptions {
452
/** Include series labels in response */
453
WITHLABELS?: boolean;
454
/** Include only selected labels */
455
SELECTED_LABELS?: string[];
456
/** Limit number of samples per series */
457
COUNT?: number;
458
/** Aggregation configuration */
459
AGGREGATION?: {
460
type: TimeSeriesAggregationType;
461
bucketDuration: number;
462
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
463
EMPTY?: boolean;
464
};
465
/** Filter by value range */
466
FILTER_BY_VALUE?: [min: number, max: number];
467
/** Filter by timestamp array */
468
FILTER_BY_TS?: number[];
469
/** Align timestamps */
470
ALIGN?: number;
471
}
472
473
type TimeSeriesReducer = 'SUM' | 'MIN' | 'MAX' | 'AVG' | 'STD.P' | 'STD.S' | 'VAR.P' | 'VAR.S' | 'COUNT';
474
```
475
476
**Usage Examples:**
477
478
```typescript
479
// Query all temperature sensors for last hour
480
const allTemperatures = await client.ts.mRange(
481
Date.now() - 60 * 60 * 1000, // 1 hour ago
482
Date.now(),
483
["sensor=temperature"]
484
);
485
486
// Query with labels and aggregation
487
const avgTemperatures = await client.ts.mRangeWithLabels(
488
Date.now() - 24 * 60 * 60 * 1000, // 24 hours ago
489
Date.now(),
490
["sensor=temperature", "building=office"],
491
{
492
WITHLABELS: true,
493
AGGREGATION: {
494
type: 'AVG',
495
bucketDuration: 60 * 60 * 1000, // 1 hour buckets
496
BUCKETTIMESTAMP: 'mid'
497
}
498
}
499
);
500
501
// Group by location and average
502
const locationAverages = await client.ts.mRangeGroupBy(
503
Date.now() - 60 * 60 * 1000,
504
Date.now(),
505
["sensor=temperature"],
506
"location",
507
"AVG",
508
{
509
AGGREGATION: {
510
type: 'AVG',
511
bucketDuration: 5 * 60 * 1000 // 5 minute buckets
512
}
513
}
514
);
515
516
// Complex multi-series query
517
const complexQuery = await client.ts.mRange(
518
Date.now() - 2 * 60 * 60 * 1000, // 2 hours ago
519
Date.now(),
520
["building=office", "location!=server_room"],
521
{
522
COUNT: 100,
523
FILTER_BY_VALUE: [18, 35], // Reasonable temperature range
524
AGGREGATION: {
525
type: 'MAX',
526
bucketDuration: 10 * 60 * 1000, // 10 minute max values
527
EMPTY: true // Include empty buckets
528
}
529
}
530
);
531
```
532
533
### Aggregation Rules
534
535
Operations for creating and managing downsampling and aggregation rules.
536
537
```typescript { .api }
538
/**
539
* Create aggregation rule between time series
540
* @param sourceKey - Source time series key
541
* @param destKey - Destination time series key
542
* @param aggregation - Aggregation configuration
543
* @param options - Optional rule parameters
544
* @returns 'OK'
545
*/
546
function createRule(
547
sourceKey: RedisArgument,
548
destKey: RedisArgument,
549
aggregation: TsAggregation,
550
options?: TsCreateRuleOptions
551
): Promise<SimpleStringReply<'OK'>>;
552
553
/**
554
* Delete aggregation rule
555
* @param sourceKey - Source time series key
556
* @param destKey - Destination time series key
557
* @returns 'OK'
558
*/
559
function deleteRule(sourceKey: RedisArgument, destKey: RedisArgument): Promise<SimpleStringReply<'OK'>>;
560
561
interface TsAggregation {
562
/** Aggregation type */
563
type: TimeSeriesAggregationType;
564
/** Time bucket duration in milliseconds */
565
bucketDuration: number;
566
/** Bucket timestamp alignment */
567
BUCKETTIMESTAMP?: TimeSeriesBucketTimestamp;
568
}
569
570
interface TsCreateRuleOptions {
571
/** Alignment timestamp */
572
alignTimestamp?: number;
573
}
574
```
575
576
**Usage Examples:**
577
578
```typescript
579
// Create destination series for aggregated data
580
await client.ts.create("temperature:sensor1:hourly", {
581
RETENTION: 30 * 24 * 60 * 60 * 1000, // 30 days
582
LABELS: {
583
sensor: "temperature",
584
location: "room1",
585
resolution: "hourly"
586
}
587
});
588
589
await client.ts.create("temperature:sensor1:daily", {
590
RETENTION: 365 * 24 * 60 * 60 * 1000, // 1 year
591
LABELS: {
592
sensor: "temperature",
593
location: "room1",
594
resolution: "daily"
595
}
596
});
597
598
// Create aggregation rules
599
await client.ts.createRule(
600
"temperature:sensor1", // Source: raw data
601
"temperature:sensor1:hourly", // Destination: hourly averages
602
{
603
type: 'AVG',
604
bucketDuration: 60 * 60 * 1000, // 1 hour
605
BUCKETTIMESTAMP: 'mid'
606
}
607
);
608
609
await client.ts.createRule(
610
"temperature:sensor1:hourly", // Source: hourly data
611
"temperature:sensor1:daily", // Destination: daily averages
612
{
613
type: 'AVG',
614
bucketDuration: 24 * 60 * 60 * 1000, // 1 day
615
BUCKETTIMESTAMP: 'low'
616
}
617
);
618
619
// Create multiple aggregation rules for different statistics
620
const sensors = ["temperature:sensor1", "humidity:sensor1"];
621
const aggregations = [
622
{ suffix: "min", type: 'MIN' as const },
623
{ suffix: "max", type: 'MAX' as const },
624
{ suffix: "avg", type: 'AVG' as const }
625
];
626
627
for (const sensor of sensors) {
628
for (const agg of aggregations) {
629
const destKey = `${sensor}:hourly:${agg.suffix}`;
630
631
// Create destination series
632
await client.ts.create(destKey, {
633
RETENTION: 7 * 24 * 60 * 60 * 1000, // 7 days
634
LABELS: {
635
source: sensor,
636
aggregation: agg.type,
637
resolution: "hourly"
638
}
639
});
640
641
// Create rule
642
await client.ts.createRule(sensor, destKey, {
643
type: agg.type,
644
bucketDuration: 60 * 60 * 1000 // 1 hour
645
});
646
}
647
}
648
649
// Delete rule when no longer needed
650
await client.ts.deleteRule("temperature:sensor1", "temperature:sensor1:hourly");
651
```
652
653
## Label Filtering Syntax
654
655
Time series queries support powerful label filtering:
656
657
```typescript
658
// Label filter examples:
659
"sensor=temperature" // Exact match
660
"location!=server_room" // Not equal
661
"building=(office|warehouse)" // Multiple values
662
"priority=(high|critical)" // OR condition
663
"sensor=temperature location=room1" // AND condition (space-separated)
664
"sensor!=pressure" // Exclude sensor type
665
```
666
667
**Usage Examples:**
668
669
```typescript
670
// Query with various label filters
671
const officeTemperatures = await client.ts.queryIndex("sensor=temperature", "building=office");
672
673
const nonServerMetrics = await client.ts.queryIndex("location!=server_room");
674
675
const criticalMetrics = await client.ts.queryIndex("priority=(high|critical)");
676
677
const roomSensors = await client.ts.queryIndex("location=(room1|room2|room3)", "sensor=temperature");
678
```
679
680
## Constants and Enums
681
682
```typescript { .api }
683
const TIME_SERIES_ENCODING = {
684
COMPRESSED: 'COMPRESSED',
685
UNCOMPRESSED: 'UNCOMPRESSED'
686
} as const;
687
688
const TIME_SERIES_DUPLICATE_POLICIES = {
689
BLOCK: 'BLOCK',
690
FIRST: 'FIRST',
691
LAST: 'LAST',
692
MIN: 'MIN',
693
MAX: 'MAX',
694
SUM: 'SUM'
695
} as const;
696
697
const TIME_SERIES_AGGREGATION_TYPE = {
698
AVG: 'AVG',
699
SUM: 'SUM',
700
MIN: 'MIN',
701
MAX: 'MAX',
702
RANGE: 'RANGE',
703
COUNT: 'COUNT',
704
STD_P: 'STD.P',
705
STD_S: 'STD.S',
706
VAR_P: 'VAR.P',
707
VAR_S: 'VAR.S',
708
FIRST: 'FIRST',
709
LAST: 'LAST'
710
} as const;
711
712
const TIME_SERIES_BUCKET_TIMESTAMP = {
713
LOW: 'low',
714
HIGH: 'high',
715
MID: 'mid'
716
} as const;
717
718
const TIME_SERIES_REDUCERS = {
719
SUM: 'SUM',
720
MIN: 'MIN',
721
MAX: 'MAX',
722
AVG: 'AVG',
723
STD_P: 'STD.P',
724
STD_S: 'STD.S',
725
VAR_P: 'VAR.P',
726
VAR_S: 'VAR.S',
727
COUNT: 'COUNT'
728
} as const;
729
```