diff --git a/examples/README.md b/examples/README.md index a387b17cb6..c96571241e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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 | | `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 | +| `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. | ## Contributing diff --git a/examples/time-series.js b/examples/time-series.js new file mode 100644 index 0000000000..2fd27e4411 --- /dev/null +++ b/examples/time-series.js @@ -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(); diff --git a/packages/time-series/lib/commands/CREATERULE.spec.ts b/packages/time-series/lib/commands/CREATERULE.spec.ts index eb338bb804..09050e2389 100644 --- a/packages/time-series/lib/commands/CREATERULE.spec.ts +++ b/packages/time-series/lib/commands/CREATERULE.spec.ts @@ -6,7 +6,7 @@ import { transformArguments } from './CREATERULE'; describe('CREATERULE', () => { it('transformArguments', () => { assert.deepEqual( - transformArguments('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + transformArguments('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), ['TS.CREATERULE', 'source', 'destination', 'AGGREGATION', 'avg', '1'] ); }); @@ -18,7 +18,7 @@ describe('CREATERULE', () => { ]); assert.equal( - await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1), + await client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1), 'OK' ); }, GLOBAL.SERVERS.OPEN); diff --git a/packages/time-series/lib/commands/DELETERULE.spec.ts b/packages/time-series/lib/commands/DELETERULE.spec.ts index 5cbcc8edb5..9364bea711 100644 --- a/packages/time-series/lib/commands/DELETERULE.spec.ts +++ b/packages/time-series/lib/commands/DELETERULE.spec.ts @@ -15,7 +15,7 @@ describe('DELETERULE', () => { await Promise.all([ client.ts.create('source'), client.ts.create('destination'), - client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVARAGE, 1) + client.ts.createRule('source', 'destination', TimeSeriesAggregationType.AVERAGE, 1) ]); assert.equal( diff --git a/packages/time-series/lib/commands/MRANGE.spec.ts b/packages/time-series/lib/commands/MRANGE.spec.ts index da01ebb204..1913576ce6 100644 --- a/packages/time-series/lib/commands/MRANGE.spec.ts +++ b/packages/time-series/lib/commands/MRANGE.spec.ts @@ -15,7 +15,7 @@ describe('MRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts index e8381a1793..441124ccc9 100644 --- a/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MRANGE_WITHLABELS.spec.ts @@ -16,7 +16,7 @@ describe('MRANGE_WITHLABELS', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MREVRANGE.spec.ts b/packages/time-series/lib/commands/MREVRANGE.spec.ts index 08c40d8c60..764fbb4845 100644 --- a/packages/time-series/lib/commands/MREVRANGE.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE.spec.ts @@ -15,7 +15,7 @@ describe('MREVRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts index a636450bef..279bcf6528 100644 --- a/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts +++ b/packages/time-series/lib/commands/MREVRANGE_WITHLABELS.spec.ts @@ -16,7 +16,7 @@ describe('MREVRANGE_WITHLABELS', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 }, GROUPBY: { diff --git a/packages/time-series/lib/commands/RANGE.spec.ts b/packages/time-series/lib/commands/RANGE.spec.ts index 15d2706d95..10b2f452bd 100644 --- a/packages/time-series/lib/commands/RANGE.spec.ts +++ b/packages/time-series/lib/commands/RANGE.spec.ts @@ -15,7 +15,7 @@ describe('RANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), diff --git a/packages/time-series/lib/commands/REVRANGE.spec.ts b/packages/time-series/lib/commands/REVRANGE.spec.ts index 10a7f4b423..ae6722dbf4 100644 --- a/packages/time-series/lib/commands/REVRANGE.spec.ts +++ b/packages/time-series/lib/commands/REVRANGE.spec.ts @@ -55,7 +55,7 @@ describe('REVRANGE', () => { assert.deepEqual( transformArguments('key', '-', '+', { AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), @@ -74,7 +74,7 @@ describe('REVRANGE', () => { COUNT: 1, ALIGN: '-', AGGREGATION: { - type: TimeSeriesAggregationType.AVARAGE, + type: TimeSeriesAggregationType.AVERAGE, timeBucket: 1 } }), diff --git a/packages/time-series/lib/commands/index.ts b/packages/time-series/lib/commands/index.ts index ad9d5962c9..5836f4aa33 100644 --- a/packages/time-series/lib/commands/index.ts +++ b/packages/time-series/lib/commands/index.ts @@ -68,7 +68,7 @@ export default { }; export enum TimeSeriesAggregationType { - AVARAGE = 'avg', + AVERAGE = 'avg', SUM = 'sum', MINIMUM = 'min', MAXIMUM = 'max', diff --git a/packages/time-series/lib/index.ts b/packages/time-series/lib/index.ts index 567795c83c..5530a328cd 100644 --- a/packages/time-series/lib/index.ts +++ b/packages/time-series/lib/index.ts @@ -1,3 +1,3 @@ export { default } from './commands'; -// TODO +export { TimeSeriesDuplicatePolicies, TimeSeriesEncoding, TimeSeriesAggregationType } from './commands';