diff --git a/doctests/dt-time-series.js b/doctests/dt-time-series.js new file mode 100644 index 0000000000..d2d94e7dc4 --- /dev/null +++ b/doctests/dt-time-series.js @@ -0,0 +1,635 @@ +// EXAMPLE: time_series_tutorial +// HIDE_START +import assert from 'assert'; +import { createClient } from 'redis'; +import { TIME_SERIES_AGGREGATION_TYPE, TIME_SERIES_REDUCERS } from '@redis/time-series'; + +const client = createClient(); +await client.connect(); +// HIDE_END + +// REMOVE_START +await client.del([ + 'thermometer:1', 'thermometer:2', 'thermometer:3', + 'rg:1', 'rg:2', 'rg:3', 'rg:4', + 'sensor3', + 'wind:1', 'wind:2', 'wind:3', 'wind:4', + 'hyg:1', 'hyg:compacted' +]); +// REMOVE_END + +// STEP_START create +const res1 = await client.ts.create('thermometer:1'); +console.log(res1); // >>> OK + +const res2 = await client.type('thermometer:1'); +console.log(res2); // >>> TSDB-TYPE + +const res3 = await client.ts.info('thermometer:1'); +console.log(res3); +// >>> { rules: [], ... totalSamples: 0, ... +// STEP_END +// REMOVE_START +assert.equal(res1, 'OK'); +assert.equal(res2, 'TSDB-TYPE'); +assert.equal(res3.totalSamples, 0); +// REMOVE_END + +// STEP_START create_retention +const res4 = await client.ts.add('thermometer:2', 1, 10.8, { RETENTION: 100 }); +console.log(res4); // >>> 1 + +const res5 = await client.ts.info('thermometer:2'); +console.log(res5); +// >>> { rules: [], ... retentionTime: 100, ... +// STEP_END +// REMOVE_START +assert.equal(res4, 1); +assert.equal(res5.retentionTime, 100); +// REMOVE_END + +// STEP_START create_labels +const res6 = await client.ts.add('thermometer:3', 1, 10.4, { + LABELS: { location: 'UK', type: 'Mercury' } +}); +console.log(res6); // >>> 1 + +const res7 = await client.ts.info('thermometer:3'); +console.log(res7); +// >>> { labels: [{ name: 'location', value: 'UK' }, { name: 'type', value: 'Mercury' }], ... } +// STEP_END +// REMOVE_START +assert.equal(res6, 1); +assert.deepEqual(res7.labels, [ + { name: 'location', value: 'UK' }, + { name: 'type', value: 'Mercury' }, +]); +// REMOVE_END + +// STEP_START madd +const res8 = await client.ts.mAdd([ + { key: 'thermometer:1', timestamp: 1, value: 9.2 }, + { key: 'thermometer:1', timestamp: 2, value: 9.9 }, + { key: 'thermometer:2', timestamp: 2, value: 10.3 } +]); +console.log(res8); // >>> [1, 2, 2] +// STEP_END +// REMOVE_START +assert.deepEqual(res8, [1, 2, 2]); +// REMOVE_END + +// STEP_START get +// The last recorded temperature for thermometer:2 +// was 10.3 at time 2. +const res9 = await client.ts.get('thermometer:2'); +console.log(res9); // >>> { timestamp: 2, value: 10.3 } +// STEP_END +// REMOVE_START +assert.equal(res9.timestamp, 2); +assert.equal(res9.value, 10.3); +// REMOVE_END + +// STEP_START range +// Add 5 data points to a time series named "rg:1". +const res10 = await client.ts.create('rg:1'); +console.log(res10); // >>> OK + +const res11 = await client.ts.mAdd([ + { key: 'rg:1', timestamp: 0, value: 18 }, + { key: 'rg:1', timestamp: 1, value: 14 }, + { key: 'rg:1', timestamp: 2, value: 22 }, + { key: 'rg:1', timestamp: 3, value: 18 }, + { key: 'rg:1', timestamp: 4, value: 24 } +]); +console.log(res11); // >>> [0, 1, 2, 3, 4] + +// Retrieve all the data points in ascending order. +const res12 = await client.ts.range('rg:1', '-', '+'); +console.log(res12); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }, ...] + +// Retrieve data points up to time 1 (inclusive). +const res13 = await client.ts.range('rg:1', '-', 1); +console.log(res13); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 1, value: 14 }] + +// Retrieve data points from time 3 onwards. +const res14 = await client.ts.range('rg:1', 3, '+'); +console.log(res14); +// >>> [{ timestamp: 3, value: 18 }, { timestamp: 4, value: 24 }] + +// Retrieve all the data points in descending order. +const res15 = await client.ts.revRange('rg:1', '-', '+'); +console.log(res15); +// >>> [{ timestamp: 4, value: 24 }, { timestamp: 3, value: 18 }, ...] + +// Retrieve data points up to time 1 (inclusive), but return them +// in descending order. +const res16 = await client.ts.revRange('rg:1', '-', 1); +console.log(res16); +// >>> [{ timestamp: 1, value: 14 }, { timestamp: 0, value: 18 }] +// STEP_END +// REMOVE_START +assert.equal(res10, 'OK'); +assert.deepEqual(res11, [0, 1, 2, 3, 4]); + +assert.deepEqual(res12, [ + { timestamp: 0, value: 18 }, + { timestamp: 1, value: 14 }, + { timestamp: 2, value: 22 }, + { timestamp: 3, value: 18 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res13, [ + { timestamp: 0, value: 18 }, + { timestamp: 1, value: 14 } +]); +assert.deepEqual(res14, [ + { timestamp: 3, value: 18 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res15, [ + { timestamp: 4, value: 24 }, + { timestamp: 3, value: 18 }, + { timestamp: 2, value: 22 }, + { timestamp: 1, value: 14 }, + { timestamp: 0, value: 18 } +]); +assert.deepEqual(res16, [ + { timestamp: 1, value: 14 }, + { timestamp: 0, value: 18 } +]); +// REMOVE_END + +// STEP_START range_filter +const res17 = await client.ts.range('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4] +}); +console.log(res17); +// >>> [{ timestamp: 0, value: 18 }, { timestamp: 2, value: 22 }, { timestamp: 4, value: 24 }] + +const res18 = await client.ts.revRange('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4], + FILTER_BY_VALUE: { min: 20, max: 25 } +}); +console.log(res18); +// >>> [{ timestamp: 4, value: 24 }, { timestamp: 2, value: 22 }] + +const res19 = await client.ts.revRange('rg:1', '-', '+', { + FILTER_BY_TS: [0, 2, 4], + FILTER_BY_VALUE: { min: 22, max: 22 }, + COUNT: 1 +}); +console.log(res19); +// >>> [{ timestamp: 2, value: 22 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res17, [ + { timestamp: 0, value: 18 }, + { timestamp: 2, value: 22 }, + { timestamp: 4, value: 24 } +]); +assert.deepEqual(res18, [ + { timestamp: 4, value: 24 }, + { timestamp: 2, value: 22 } +]); +assert.deepEqual(res19, [ + { timestamp: 2, value: 22 } +]); +// REMOVE_END + +// STEP_START query_multi +// Create three new "rg:" time series (two in the US +// and one in the UK, with different units) and add some +// data points. +const res20 = await client.ts.create('rg:2', { + LABELS: { location: 'us', unit: 'cm' } +}); +console.log(res20); // >>> OK + +const res21 = await client.ts.create('rg:3', { + LABELS: { location: 'us', unit: 'in' } +}); +console.log(res21); // >>> OK + +const res22 = await client.ts.create('rg:4', { + LABELS: { location: 'uk', unit: 'mm' } +}); +console.log(res22); // >>> OK + +const res23 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 0, value: 1.8 }, + { key: 'rg:3', timestamp: 0, value: 0.9 }, + { key: 'rg:4', timestamp: 0, value: 25 } +]); +console.log(res23); // >>> [0, 0, 0] + +const res24 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 1, value: 2.1 }, + { key: 'rg:3', timestamp: 1, value: 0.77 }, + { key: 'rg:4', timestamp: 1, value: 18 } +]); +console.log(res24); // >>> [1, 1, 1] + +const res25 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 2, value: 2.3 }, + { key: 'rg:3', timestamp: 2, value: 1.1 }, + { key: 'rg:4', timestamp: 2, value: 21 } +]); +console.log(res25); // >>> [2, 2, 2] + +const res26 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 3, value: 1.9 }, + { key: 'rg:3', timestamp: 3, value: 0.81 }, + { key: 'rg:4', timestamp: 3, value: 19 } +]); +console.log(res26); // >>> [3, 3, 3] + +const res27 = await client.ts.mAdd([ + { key: 'rg:2', timestamp: 4, value: 1.78 }, + { key: 'rg:3', timestamp: 4, value: 0.74 }, + { key: 'rg:4', timestamp: 4, value: 23 } +]); +console.log(res27); // >>> [4, 4, 4] + +// Retrieve the last data point from each US time series. +const res28 = await client.ts.mGet(['location=us']); +console.log(res28); +// >>> { "rg:2": { sample: { timestamp: 4, value: 1.78 } }, "rg:3": { sample: { timestamp: 4, value: 0.74 } } } + +// Retrieve the same data points, but include the `unit` +// label in the results. +const res29 = await client.ts.mGetSelectedLabels(['location=us'], ['unit']); +console.log(res29); +// >>> { "rg:2": { labels: { unit: 'cm' }, sample: { timestamp: 4, value: 1.78 } }, "rg:3": { labels: { unit: 'in' }, sample: { timestamp: 4, value: 0.74 } } } + +// Retrieve data points up to time 2 (inclusive) from all +// time series that use millimeters as the unit. Include all +// labels in the results. +const res30 = await client.ts.mRangeWithLabels('-', 2, 'unit=mm'); +console.log(res30); +// >>> { "rg:4": { labels: { location: 'uk', unit: 'mm' }, samples: [ +// { timestamp: 0, value: 25 }, +// { timestamp: 1, value: 18 }, +// { timestamp: 2, value: 21 } +// ] } } + +// Retrieve data points from time 1 to time 3 (inclusive) from +// all time series that use centimeters or millimeters as the unit, +// but only return the `location` label. Return the results +// in descending order of timestamp. +const res31 = await client.ts.mRevRangeSelectedLabels( + 1, 3, + ['location'], + ['unit=(cm,mm)'] +); +console.log(res31); +// >>> { "rg:2": { labels: { location: 'us' }, samples: [ +// { timestamp: 3, value: 1.9 }, +// { timestamp: 2, value: 2.3 }, +// { timestamp: 1, value: 2.1 } +// ] }, "rg:4": { labels: { location: 'uk' }, samples: [ +// { timestamp: 3, value: 19 }, +// { timestamp: 2, value: 21 }, +// { timestamp: 1, value: 18 } +// ] } } +// STEP_END +// REMOVE_START +assert.equal(res20, 'OK'); +assert.equal(res21, 'OK'); +assert.equal(res22, 'OK'); +assert.deepEqual(res23, [0, 0, 0]); +assert.deepEqual(res24, [1, 1, 1]); +assert.deepEqual(res25, [2, 2, 2]); +assert.deepEqual(res26, [3, 3, 3]); +assert.deepEqual(res27, [4, 4, 4]); + +assert.deepEqual(res28, { + "rg:2": { sample: { timestamp: 4, value: 1.78 } }, + "rg:3": { sample: { timestamp: 4, value: 0.74 } } +}); +assert.deepEqual(res29, { + "rg:2": { labels: { unit: 'cm' }, sample: { timestamp: 4, value: 1.78 } }, + "rg:3": { labels: { unit: 'in' }, sample: { timestamp: 4, value: 0.74 } } +}); + +assert.deepEqual(res30, { + "rg:4": { + labels: { location: 'uk', unit: 'mm' }, + samples: [ + { timestamp: 0, value: 25 }, + { timestamp: 1, value: 18 }, + { timestamp: 2, value: 21 } + ] + } +}); +assert.deepEqual(res31, { + "rg:2": { + labels: { location: 'us' }, + samples: [ + { timestamp: 3, value: 1.9 }, + { timestamp: 2, value: 2.3 }, + { timestamp: 1, value: 2.1 } + ] + }, + "rg:4": { + labels: { location: 'uk' }, + samples: [ + { timestamp: 3, value: 19 }, + { timestamp: 2, value: 21 }, + { timestamp: 1, value: 18 } + ] + } +}); +// REMOVE_END + +// STEP_START agg +const res32 = await client.ts.range('rg:2', '-', '+', { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.AVG, + timeBucket: 2 + } +}); +console.log(res32); +// >>> [{ timestamp: 0, value: 1.9500000000000002 },{ timestamp: 2, value: 2.0999999999999996 }, { timestamp: 4, value: 1.78 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res32, [ + { timestamp: 0, value: 1.9500000000000002 }, + { timestamp: 2, value: 2.0999999999999996 }, + { timestamp: 4, value: 1.78 } +]); +// REMOVE_END + +// STEP_START agg_bucket +const res33 = await client.ts.create('sensor3'); +console.log(res33); // >>> OK + +const res34 = await client.ts.mAdd([ + { key: 'sensor3', timestamp: 10, value: 1000 }, + { key: 'sensor3', timestamp: 20, value: 2000 }, + { key: 'sensor3', timestamp: 30, value: 3000 }, + { key: 'sensor3', timestamp: 40, value: 4000 }, + { key: 'sensor3', timestamp: 50, value: 5000 }, + { key: 'sensor3', timestamp: 60, value: 6000 }, + { key: 'sensor3', timestamp: 70, value: 7000 } +]); +console.log(res34); // >>> [10, 20, 30, 40, 50, 60, 70] + +const res35 = await client.ts.range('sensor3', 10, 70, { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.MIN, + timeBucket: 25 + } +}); +console.log(res35); +// >>> [{ timestamp: 0, value: 1000 }, { timestamp: 25, value: 3000 }, { timestamp: 50, value: 5000 }] +// STEP_END +// REMOVE_START +assert.equal(res33, 'OK'); +assert.deepEqual(res34, [10, 20, 30, 40, 50, 60, 70]); +assert.deepEqual(res35, [ + { timestamp: 0, value: 1000 }, + { timestamp: 25, value: 3000 }, + { timestamp: 50, value: 5000 } +]); +// REMOVE_END + +// STEP_START agg_align +const res36 = await client.ts.range('sensor3', 10, 70, { + AGGREGATION: { + type: TIME_SERIES_AGGREGATION_TYPE.MIN, + timeBucket: 25 + }, + ALIGN: 'START' +}); +console.log(res36); +// >>> [{ timestamp: 10, value: 1000 }, { timestamp: 35, value: 4000 }, { timestamp: 60, value: 6000 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res36, [ + { timestamp: 10, value: 1000 }, + { timestamp: 35, value: 4000 }, + { timestamp: 60, value: 6000 } +]); +// REMOVE_END + +// STEP_START agg_multi +const res37 = await client.ts.create('wind:1', { + LABELS: { country: 'uk' } +}); +console.log(res37); // >>> OK + +const res38 = await client.ts.create('wind:2', { + LABELS: { country: 'uk' } +}); +console.log(res38); // >>> OK + +const res39 = await client.ts.create('wind:3', { + LABELS: { country: 'us' } +}); +console.log(res39); // >>> OK + +const res40 = await client.ts.create('wind:4', { + LABELS: { country: 'us' } +}); +console.log(res40); // >>> OK + +const res41 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 1, value: 12 }, + { key: 'wind:2', timestamp: 1, value: 18 }, + { key: 'wind:3', timestamp: 1, value: 5 }, + { key: 'wind:4', timestamp: 1, value: 20 } +]); +console.log(res41); // >>> [1, 1, 1, 1] + +const res42 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 2, value: 14 }, + { key: 'wind:2', timestamp: 2, value: 21 }, + { key: 'wind:3', timestamp: 2, value: 4 }, + { key: 'wind:4', timestamp: 2, value: 25 } +]); +console.log(res42); // >>> [2, 2, 2, 2] + +const res43 = await client.ts.mAdd([ + { key: 'wind:1', timestamp: 3, value: 10 }, + { key: 'wind:2', timestamp: 3, value: 24 }, + { key: 'wind:3', timestamp: 3, value: 8 }, + { key: 'wind:4', timestamp: 3, value: 18 } +]); +console.log(res43); // >>> [3, 3, 3, 3] + +// The result pairs contain the timestamp and the maximum sample value +// for the country at that timestamp. +const res44 = await client.ts.mRangeGroupBy( + '-', '+', ['country=(us,uk)'], + {label: 'country', REDUCE: TIME_SERIES_REDUCERS.MAX} +); +console.log(res44); +// >>> { "country=uk": { samples: [ +// { timestamp: 1, value: 18 }, +// { timestamp: 2, value: 21 }, +// { timestamp: 3, value: 24 } +// ] }, "country=us": { samples: [ +// { timestamp: 1, value: 20 }, +// { timestamp: 2, value: 25 }, +// { timestamp: 3, value: 18 } +// ] } } + +// The result pairs contain the timestamp and the average sample value +// for the country at that timestamp. +const res45 = await client.ts.mRangeGroupBy( + '-', '+', ['country=(us,uk)'], + { label: 'country', REDUCE: TIME_SERIES_REDUCERS.AVG} +); +console.log(res45); +// >>> { +// "country=uk": { +// samples: [{ timestamp: 1, value: 15 }, { timestamp: 2, value: 17.5 }, { timestamp: 3, value: 17 }] +// }, +// "country=us": { +// samples: [{ timestamp: 1, value: 12.5 }, { timestamp: 2, value: 14.5 }, { timestamp: 3, value: 13 }] +// } +// } +// STEP_END +// REMOVE_START +assert.equal(res37, 'OK'); +assert.equal(res38, 'OK'); +assert.equal(res39, 'OK'); +assert.equal(res40, 'OK'); +assert.deepEqual(res41, [1, 1, 1, 1]); +assert.deepEqual(res42, [2, 2, 2, 2]); +assert.deepEqual(res43, [3, 3, 3, 3]); + +assert.deepEqual(res44, { + "country=uk": { + samples: [ + { timestamp: 1, value: 18 }, + { timestamp: 2, value: 21 }, + { timestamp: 3, value: 24 } + ] + }, + "country=us": { + samples: [ + { timestamp: 1, value: 20 }, + { timestamp: 2, value: 25 }, + { timestamp: 3, value: 18 } + ] + } +}); +assert.deepEqual(res45, { + "country=uk": { + samples: [ + { timestamp: 1, value: 15 }, + { timestamp: 2, value: 17.5 }, + { timestamp: 3, value: 17 } + ] + }, + "country=us": { + samples: [ + { timestamp: 1, value: 12.5 }, + { timestamp: 2, value: 14.5 }, + { timestamp: 3, value: 13 } + ] + } +}); +// REMOVE_END + +// STEP_START create_compaction +const res46 = await client.ts.create('hyg:1'); +console.log(res46); // >>> OK + +const res47 = await client.ts.create('hyg:compacted'); +console.log(res47); // >>> OK + +const res48 = await client.ts.createRule('hyg:1', 'hyg:compacted', TIME_SERIES_AGGREGATION_TYPE.MIN, 3); +console.log(res48); // >>> OK + +const res49 = await client.ts.info('hyg:1'); +console.log(res49.rules); +// >>> [{ aggregationType: 'MIN', key: 'hyg:compacted', timeBucket: 3}] + +const res50 = await client.ts.info('hyg:compacted'); +console.log(res50.sourceKey); // >>> 'hyg:1' +// STEP_END +// REMOVE_START +assert.equal(res46, 'OK'); +assert.equal(res47, 'OK'); +assert.equal(res48, 'OK'); +assert.deepEqual(res49.rules, [ + { aggregationType: 'MIN', key: 'hyg:compacted', timeBucket: 3} +]); +assert.equal(res50.sourceKey, 'hyg:1'); +// REMOVE_END + +// STEP_START comp_add +const res51 = await client.ts.mAdd([ + { key: 'hyg:1', timestamp: 0, value: 75 }, + { key: 'hyg:1', timestamp: 1, value: 77 }, + { key: 'hyg:1', timestamp: 2, value: 78 } +]); +console.log(res51); // >>> [0, 1, 2] + +const res52 = await client.ts.range('hyg:compacted', '-', '+'); +console.log(res52); // >>> [] + +const res53 = await client.ts.add('hyg:1', 3, 79); +console.log(res53); // >>> 3 + +const res54 = await client.ts.range('hyg:compacted', '-', '+'); +console.log(res54); // >>> [{ timestamp: 0, value: 75 }] +// STEP_END +// REMOVE_START +assert.deepEqual(res51, [0, 1, 2]); +assert.deepEqual(res52, []); +assert.equal(res53, 3); +assert.deepEqual(res54, [{ timestamp: 0, value: 75 }]); +// REMOVE_END + +// STEP_START del +const res55 = await client.ts.info('thermometer:1'); +console.log(res55.totalSamples); // >>> 2 +console.log(res55.firstTimestamp); // >>> 1 +console.log(res55.lastTimestamp); // >>> 2 + +const res56 = await client.ts.add('thermometer:1', 3, 9.7); +console.log(res56); // >>> 3 + +const res57 = await client.ts.info('thermometer:1'); +console.log(res57.totalSamples); // >>> 3 +console.log(res57.firstTimestamp); // >>> 1 +console.log(res57.lastTimestamp); // >>> 3 + +const res58 = await client.ts.del('thermometer:1', 1, 2); +console.log(res58); // >>> 2 + +const res59 = await client.ts.info('thermometer:1'); +console.log(res59.totalSamples); // >>> 1 +console.log(res59.firstTimestamp); // >>> 3 +console.log(res59.lastTimestamp); // >>> 3 + +const res60 = await client.ts.del('thermometer:1', 3, 3); +console.log(res60); // >>> 1 + +const res61 = await client.ts.info('thermometer:1'); +console.log(res61.totalSamples); // >>> 0 +// STEP_END +// REMOVE_START +assert.equal(res55.totalSamples, 2); +assert.equal(res55.firstTimestamp, 1); +assert.equal(res55.lastTimestamp, 2); +assert.equal(res56, 3); +assert.equal(res57.totalSamples, 3); +assert.equal(res57.firstTimestamp, 1); +assert.equal(res57.lastTimestamp, 3); +assert.equal(res58, 2); +assert.equal(res59.totalSamples, 1); +assert.equal(res59.firstTimestamp, 3); +assert.equal(res59.lastTimestamp, 3); +assert.equal(res60, 1); +assert.equal(res61.totalSamples, 0); +// REMOVE_END + +// HIDE_START +await client.quit(); +// HIDE_END \ No newline at end of file