You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
Adding a RedisTimeSeries example (#1839)
* Adds the start of a timeseries example. * Exports required TimeSeries items. * Fixed import. * Added TS.INFO example output. * Fixed typo. * Fixed typo. * Exported aggregation enum. * Working time series example. Co-authored-by: Leibale Eidelman <leibale1998@gmail.com>
This commit is contained in:
@@ -17,6 +17,7 @@ This folder contains example scripts showing how to use Node Redis in different
|
|||||||
| `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality |
|
| `set-scan.js` | An example script that shows how to use the SSCAN iterator functionality |
|
||||||
| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command |
|
| `stream-producer.js` | Adds entries to a [Redis Stream](https://redis.io/topics/streams-intro) using the `XADD` command |
|
||||||
| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command |
|
| `stream-consumer.js` | Reads entries from a [Redis Stream](https://redis.io/topics/streams-intro) using the blocking `XREAD` command |
|
||||||
|
| `time-series.js` | Create, populate and query timeseries data with [Redis Timeseries](https://redistimeseries.io) |
|
||||||
| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. |
|
| `topk.js` | Use the [RedisBloom](https://redisbloom.io) TopK to track the most frequently seen items. |
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
126
examples/time-series.js
Normal file
126
examples/time-series.js
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// Add data to a Redis TimeSeries and query it.
|
||||||
|
// Requires the RedisTimeSeries module: https://redistimeseries.io/
|
||||||
|
|
||||||
|
import { createClient } from 'redis';
|
||||||
|
import { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from '@node-redis/time-series';
|
||||||
|
|
||||||
|
async function timeSeries() {
|
||||||
|
const client = createClient();
|
||||||
|
|
||||||
|
await client.connect();
|
||||||
|
await client.del('mytimeseries');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create a timeseries
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tscreate
|
||||||
|
const created = await client.ts.create('mytimeseries', {
|
||||||
|
RETENTION: 86400000, // 1 day in milliseconds
|
||||||
|
ENCODING: TimeSeriesEncoding.UNCOMPRESSED, // No compression
|
||||||
|
DUPLICATE_POLICY: TimeSeriesDuplicatePolicies.BLOCK // No duplicates
|
||||||
|
});
|
||||||
|
|
||||||
|
if (created === 'OK') {
|
||||||
|
console.log('Created timeseries.');
|
||||||
|
} else {
|
||||||
|
console.log('Error creating timeseries :(');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = Math.floor(Math.random() * 1000) + 1; // Random data point value
|
||||||
|
let currentTimestamp = 1640995200000; // Jan 1 2022 00:00:00
|
||||||
|
let num = 0;
|
||||||
|
|
||||||
|
while (num < 10000) {
|
||||||
|
// Add a new value to the timeseries, providing our own timestamp:
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tsadd
|
||||||
|
await client.ts.add('mytimeseries', currentTimestamp, value);
|
||||||
|
console.log(`Added timestamp ${currentTimestamp}, value ${value}.`);
|
||||||
|
|
||||||
|
num += 1;
|
||||||
|
value = Math.floor(Math.random() * 1000) + 1; // Get another random value
|
||||||
|
currentTimestamp += 1000; // Move on one second.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add multiple values to the timeseries in round trip to the server:
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tsmadd
|
||||||
|
const response = await client.ts.mAdd([{
|
||||||
|
key: 'mytimeseries',
|
||||||
|
timestamp: currentTimestamp + 60000,
|
||||||
|
value: Math.floor(Math.random() * 1000) + 1
|
||||||
|
}, {
|
||||||
|
key: 'mytimeseries',
|
||||||
|
timestamp: currentTimestamp + 120000,
|
||||||
|
value: Math.floor(Math.random() * 1000) + 1
|
||||||
|
}]);
|
||||||
|
|
||||||
|
// response = array of timestamps added by TS.MADD command.
|
||||||
|
if (response.length === 2) {
|
||||||
|
console.log('Added 2 entries to timeseries with TS.MADD.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update timeseries retention with TS.ALTER:
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tsalter
|
||||||
|
const alterResponse = await client.ts.alter('mytimeseries', {
|
||||||
|
RETENTION: 0 // Keep the entries forever
|
||||||
|
});
|
||||||
|
|
||||||
|
if (alterResponse === 'OK') {
|
||||||
|
console.log('Timeseries retention settings altered successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query the timeseries with TS.RANGE:
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tsrangetsrevrange
|
||||||
|
const fromTimestamp = 1640995200000; // Jan 1 2022 00:00:00
|
||||||
|
const toTimestamp = 1640995260000; // Jan 1 2022 00:01:00
|
||||||
|
const rangeResponse = await client.ts.range('mytimeseries', fromTimestamp, toTimestamp, {
|
||||||
|
// Group into 10 second averages.
|
||||||
|
AGGREGATION: {
|
||||||
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
|
timeBucket: 10000
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('RANGE RESPONSE:');
|
||||||
|
// rangeResponse looks like:
|
||||||
|
// [
|
||||||
|
// { timestamp: 1640995200000, value: 356.8 },
|
||||||
|
// { timestamp: 1640995210000, value: 534.8 },
|
||||||
|
// { timestamp: 1640995220000, value: 481.3 },
|
||||||
|
// { timestamp: 1640995230000, value: 437 },
|
||||||
|
// { timestamp: 1640995240000, value: 507.3 },
|
||||||
|
// { timestamp: 1640995250000, value: 581.2 },
|
||||||
|
// { timestamp: 1640995260000, value: 600 }
|
||||||
|
// ]
|
||||||
|
|
||||||
|
console.log(rangeResponse);
|
||||||
|
|
||||||
|
// Get some information about the state of the timeseries.
|
||||||
|
// https://oss.redis.com/redistimeseries/commands/#tsinfo
|
||||||
|
const tsInfo = await client.ts.info('mytimeseries');
|
||||||
|
|
||||||
|
// tsInfo looks like this:
|
||||||
|
// {
|
||||||
|
// totalSamples: 1440,
|
||||||
|
// memoryUsage: 28904,
|
||||||
|
// firstTimestamp: 1641508920000,
|
||||||
|
// lastTimestamp: 1641595320000,
|
||||||
|
// retentionTime: 86400000,
|
||||||
|
// chunkCount: 7,
|
||||||
|
// chunkSize: 4096,
|
||||||
|
// chunkType: 'uncompressed',
|
||||||
|
// duplicatePolicy: 'block',
|
||||||
|
// labels: [],
|
||||||
|
// sourceKey: null,
|
||||||
|
// rules: []
|
||||||
|
// }
|
||||||
|
|
||||||
|
console.log('Timeseries info:');
|
||||||
|
console.log(tsInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
timeSeries();
|
@@ -6,7 +6,7 @@ import { transformArguments } from './CREATERULE';
|
|||||||
describe('CREATERULE', () => {
|
describe('CREATERULE', () => {
|
||||||
it('transformArguments', () => {
|
it('transformArguments', () => {
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1),
|
transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1),
|
||||||
['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1']
|
['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -18,7 +18,7 @@ describe('CREATERULE', () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1),
|
await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1),
|
||||||
'OK'
|
'OK'
|
||||||
);
|
);
|
||||||
}, GLOBAL.SERVERS.OPEN);
|
}, GLOBAL.SERVERS.OPEN);
|
||||||
|
@@ -15,7 +15,7 @@ describe('DELETERULE', () => {
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
client.ts.create('source'),
|
client.ts.create('source'),
|
||||||
client.ts.create('destination'),
|
client.ts.create('destination'),
|
||||||
client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1)
|
client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert.equal(
|
assert.equal(
|
||||||
|
@@ -15,7 +15,7 @@ describe('MRANGE', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
},
|
},
|
||||||
GROUPBY: {
|
GROUPBY: {
|
||||||
|
@@ -16,7 +16,7 @@ describe('MRANGE_WITHLABELS', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
},
|
},
|
||||||
GROUPBY: {
|
GROUPBY: {
|
||||||
|
@@ -15,7 +15,7 @@ describe('MREVRANGE', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
},
|
},
|
||||||
GROUPBY: {
|
GROUPBY: {
|
||||||
|
@@ -16,7 +16,7 @@ describe('MREVRANGE_WITHLABELS', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
},
|
},
|
||||||
GROUPBY: {
|
GROUPBY: {
|
||||||
|
@@ -15,7 +15,7 @@ describe('RANGE', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@@ -55,7 +55,7 @@ describe('REVRANGE', () => {
|
|||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
transformArguments('key', '-', '+', {
|
transformArguments('key', '-', '+', {
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -74,7 +74,7 @@ describe('REVRANGE', () => {
|
|||||||
COUNT: 1,
|
COUNT: 1,
|
||||||
ALIGN: '-',
|
ALIGN: '-',
|
||||||
AGGREGATION: {
|
AGGREGATION: {
|
||||||
type: TimeSeriesAggregationType.AVARAGE,
|
type: TimeSeriesAggregationType.AVERAGE,
|
||||||
timeBucket: 1
|
timeBucket: 1
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
@@ -68,7 +68,7 @@ export default {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export enum TimeSeriesAggregationType {
|
export enum TimeSeriesAggregationType {
|
||||||
AVARAGE = 'avg',
|
AVERAGE = 'avg',
|
||||||
SUM = 'sum',
|
SUM = 'sum',
|
||||||
MINIMUM = 'min',
|
MINIMUM = 'min',
|
||||||
MAXIMUM = 'max',
|
MAXIMUM = 'max',
|
||||||
|
@@ -1,3 +1,3 @@
|
|||||||
export { default } from './commands';
|
export { default } from './commands';
|
||||||
|
|
||||||
// TODO
|
export { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from './commands';
|
||||||
|
Reference in New Issue
Block a user